Merge "Accumulate battery usage stats incrementally" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index fbe4905..1ae9ada 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -24,6 +24,7 @@
         "android-sdk-flags-java",
         "android.adaptiveauth.flags-aconfig-java",
         "android.app.appfunctions.flags-aconfig-java",
+        "android.app.assist.flags-aconfig-java",
         "android.app.contextualsearch.flags-aconfig-java",
         "android.app.flags-aconfig-java",
         "android.app.jank.flags-aconfig-java",
@@ -64,6 +65,7 @@
         "android.server.app.flags-aconfig-java",
         "android.service.autofill.flags-aconfig-java",
         "android.service.chooser.flags-aconfig-java",
+        "android.service.compat.flags-aconfig-java",
         "android.service.controls.flags-aconfig-java",
         "android.service.dreams.flags-aconfig-java",
         "android.service.notification.flags-aconfig-java",
@@ -79,6 +81,7 @@
         "android.view.inputmethod.flags-aconfig-java",
         "android.webkit.flags-aconfig-java",
         "android.widget.flags-aconfig-java",
+        "android.xr.flags-aconfig-java",
         "art_exported_aconfig_flags_lib",
         "backstage_power_flags_lib",
         "backup_flags_lib",
@@ -107,6 +110,7 @@
         "hwui_flags_java_lib",
         "interaction_jank_monitor_flags_lib",
         "libcore_exported_aconfig_flags_lib",
+        "libcore_readonly_aconfig_flags_lib",
         "libgui_flags_java_lib",
         "power_flags_lib",
         "sdk_sandbox_flags_lib",
@@ -166,6 +170,26 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// See b/368409430 - This is for libcore flags to be generated with
+// force-read-only mode, so access to the flags does not involve I/O,
+// which could break Isolated Processes with I/O permission disabled.
+// The issue will be addressed once new Aconfig storage API is landed
+// and the readonly version will be removed.
+aconfig_declarations {
+    name: "libcore-readonly-aconfig-flags",
+    package: "com.android.libcore.readonly",
+    container: "system",
+    srcs: ["libcore-readonly.aconfig"],
+}
+
+// Core Libraries / libcore
+java_aconfig_library {
+    name: "libcore_readonly_aconfig_flags_lib",
+    aconfig_declarations: "libcore-readonly-aconfig-flags",
+    mode: "force-read-only",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Telecom
 java_aconfig_library {
     name: "telecom_flags_core_java_lib",
@@ -789,6 +813,12 @@
     ],
 }
 
+cc_aconfig_library {
+    name: "android.permission.flags-aconfig-cc",
+    aconfig_declarations: "android.permission.flags-aconfig",
+    host_supported: true,
+}
+
 // SQLite
 aconfig_declarations {
     name: "android.database.sqlite-aconfig",
@@ -862,6 +892,21 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+aconfig_declarations {
+    name: "android.service.compat.flags-aconfig",
+    package: "com.android.server.compat",
+    container: "system",
+    srcs: [
+        "services/core/java/com/android/server/compat/*.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "android.service.compat.flags-aconfig-java",
+    aconfig_declarations: "android.service.compat.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Multi user
 aconfig_declarations {
     name: "android.multiuser.flags-aconfig",
@@ -876,6 +921,20 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// XR
+aconfig_declarations {
+    name: "android.xr.flags-aconfig",
+    package: "android.xr",
+    container: "system",
+    srcs: ["core/java/android/content/pm/xr.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.xr.flags-aconfig-java",
+    aconfig_declarations: "android.xr.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // android.app
 aconfig_declarations {
     name: "android.app.flags-aconfig",
@@ -1230,6 +1289,20 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// Assist
+aconfig_declarations {
+    name: "android.app.assist.flags-aconfig",
+    package: "android.app.assist.flags",
+    container: "system",
+    srcs: ["core/java/android/app/assist/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.app.assist.flags-aconfig-java",
+    aconfig_declarations: "android.app.assist.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Smartspace
 aconfig_declarations {
     name: "android.app.smartspace.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index 811755d..252aeef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -150,6 +150,7 @@
         // etc.
         ":framework-javastream-protos",
         ":statslog-framework-java-gen", // FrameworkStatsLog.java
+        ":statslog-hwui-java-gen", // HwuiStatsLog.java
         ":audio_policy_configuration_V7_0",
     ],
 }
@@ -170,12 +171,6 @@
         //same purpose.
         "//external/robolectric:__subpackages__",
         "//frameworks/layoutlib:__subpackages__",
-
-        // This is for the same purpose as robolectric -- to build "framework.jar" for host-side
-        // testing.
-        // TODO: Once Ravenwood is stable, move the host side jar targets to this directory,
-        // and remove this line.
-        "//frameworks/base/tools/hoststubgen:__subpackages__",
     ],
 }
 
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
index b8857ec..1738a35 100644
--- a/MULTIUSER_OWNERS
+++ b/MULTIUSER_OWNERS
@@ -3,7 +3,5 @@
 bookatz@google.com
 nykkumar@google.com
 olilan@google.com
-omakoto@google.com
 tetianameronyk@google.com
 tyk@google.com
-yamasani@google.com
diff --git a/OWNERS b/OWNERS
index afa60be..eb2bfcf 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,8 +7,6 @@
 hackbod@android.com #{LAST_RESORT_SUGGESTION}
 hackbod@google.com #{LAST_RESORT_SUGGESTION}
 jjaggi@google.com #{LAST_RESORT_SUGGESTION}
-jsharkey@android.com #{LAST_RESORT_SUGGESTION}
-jsharkey@google.com #{LAST_RESORT_SUGGESTION}
 lorenzo@google.com #{LAST_RESORT_SUGGESTION}
 michaelwr@google.com #{LAST_RESORT_SUGGESTION}
 nandana@google.com #{LAST_RESORT_SUGGESTION}
@@ -33,19 +31,19 @@
 per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
 per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
 
-per-file INPUT_OWNERS = file:/INPUT_OWNERS
-per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
-per-file SQLITE_OWNERS = file:/SQLITE_OWNERS
-
 per-file *ravenwood* = file:ravenwood/OWNERS
 per-file *Ravenwood* = file:ravenwood/OWNERS
 
+per-file INPUT_OWNERS = file:/INPUT_OWNERS
+per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
+per-file SQLITE_OWNERS = file:/SQLITE_OWNERS
 per-file PERFORMANCE_OWNERS = file:/PERFORMANCE_OWNERS
-
 per-file PACKAGE_MANAGER_OWNERS = file:/PACKAGE_MANAGER_OWNERS
-
 per-file WEAR_OWNERS = file:/WEAR_OWNERS
-
+per-file ACTIVITY_MANAGER_OWNERS = file:/ACTIVITY_MANAGER_OWNERS
+per-file BATTERY_STATS_OWNERS = file:/BATTERY_STATS_OWNERS
+per-file OOM_ADJUSTER_OWNERS = file:/OOM_ADJUSTER_OWNERS
+per-file MULTIUSER_OWNERS = file:/MULTIUSER_OWNERS
+per-file BROADCASTS_OWNERS = file:/BROADCASTS_OWNERS
 per-file ADPF_OWNERS = file:/ADPF_OWNERS
-
 per-file GAME_MANAGER_OWNERS = file:/GAME_MANAGER_OWNERS
diff --git a/Ravenwood.bp b/Ravenwood.bp
index ec58210..2e038e0 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -12,256 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// We need this "trampoline" rule to force soong to give a host-side jar to
-// framework-minus-apex.ravenwood-base. Otherwise, soong would mix up the arch (?) and we'd get
-// a dex jar.
-java_library {
-    name: "framework-minus-apex-for-hoststubgen",
-    installable: false, // host only jar.
-    static_libs: [
-        "framework-minus-apex",
-    ],
-    sdk_version: "core_platform",
-    visibility: ["//visibility:private"],
-}
-
-// Process framework-all with hoststubgen for Ravenwood.
-// This step takes several tens of seconds, so we manually shard it to multiple modules.
-// All the copies have to be kept in sync.
-// TODO: Do the sharding better, either by making hostsubgen support sharding natively, or
-// making a better build rule.
-
-genrule_defaults {
-    name: "framework-minus-apex.ravenwood-base_defaults",
-    defaults: ["ravenwood-internal-only-visibility-genrule"],
-    tools: ["hoststubgen"],
-    srcs: [
-        ":framework-minus-apex-for-hoststubgen",
-        ":ravenwood-framework-policies",
-        ":ravenwood-standard-options",
-        ":ravenwood-annotation-allowed-classes",
-    ],
-    out: [
-        "ravenwood.jar",
-        "hoststubgen_framework-minus-apex.log",
-    ],
-}
-
-framework_minus_apex_cmd = "$(location hoststubgen) " +
-    "@$(location :ravenwood-standard-options) " +
-    "--debug-log $(location hoststubgen_framework-minus-apex.log) " +
-    "--out-jar $(location ravenwood.jar) " +
-    "--in-jar $(location :framework-minus-apex-for-hoststubgen) " +
-    "--policy-override-file $(location :ravenwood-framework-policies) " +
-    "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) "
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X0",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 0",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X1",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 1",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X2",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 2",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X3",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 3",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X4",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 4",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X5",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 5",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X6",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 6",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X7",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 7",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X8",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 8",
-}
-
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_X9",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 9",
-}
-
-// Build framework-minus-apex.ravenwood-base without sharding.
-// We extract the various dump files from this one, rather than the sharded ones, because
-// some dumps use the output from other classes (e.g. base classes) which may not be in the
-// same shard. Also some of the dump files ("apis") may be slow even when sharded, because
-// the output contains the information from all the input classes, rather than the output classes.
-// Not using sharding is fine for this module because it's only used for collecting the
-// dump / stats files, which don't have to happen regularly.
-java_genrule {
-    name: "framework-minus-apex.ravenwood-base_all",
-    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
-    cmd: framework_minus_apex_cmd +
-        "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " +
-        "--supported-api-list-file $(location hoststubgen_framework-minus-apex_apis.csv) " +
-
-        "--gen-keep-all-file $(location hoststubgen_framework-minus-apex_keep_all.txt) " +
-        "--gen-input-dump-file $(location hoststubgen_framework-minus-apex_dump.txt) ",
-
-    out: [
-        "hoststubgen_framework-minus-apex_keep_all.txt",
-        "hoststubgen_framework-minus-apex_dump.txt",
-        "hoststubgen_framework-minus-apex_stats.csv",
-        "hoststubgen_framework-minus-apex_apis.csv",
-    ],
-}
-
-// Marge all the sharded jars
-java_genrule {
-    name: "framework-minus-apex.ravenwood",
-    defaults: ["ravenwood-internal-only-visibility-java"],
-    cmd: "$(location merge_zips) $(out) $(in)",
-    tools: ["merge_zips"],
-    srcs: [
-        ":framework-minus-apex.ravenwood-base_X0{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X1{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X2{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X3{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X4{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X5{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X6{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X7{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X8{ravenwood.jar}",
-        ":framework-minus-apex.ravenwood-base_X9{ravenwood.jar}",
-    ],
-    out: [
-        "framework-minus-apex.ravenwood.jar",
-    ],
-}
+// "framework-minus-apex" and "all-updatable-modules-system-stubs" are not
+// visible publicly. We re-export them to Ravenwood in this file.
 
 java_library {
-    name: "services.core-for-hoststubgen",
-    installable: false, // host only jar.
-    static_libs: [
-        "services.core",
-    ],
-    sdk_version: "core_platform",
-    visibility: ["//visibility:private"],
-}
-
-java_genrule {
-    name: "services.core.ravenwood-base",
-    tools: ["hoststubgen"],
-    cmd: "$(location hoststubgen) " +
-        "@$(location :ravenwood-standard-options) " +
-
-        "--debug-log $(location hoststubgen_services.core.log) " +
-        "--stats-file $(location hoststubgen_services.core_stats.csv) " +
-        "--supported-api-list-file $(location hoststubgen_services.core_apis.csv) " +
-
-        "--out-jar $(location ravenwood.jar) " +
-
-        "--gen-keep-all-file $(location hoststubgen_services.core_keep_all.txt) " +
-        "--gen-input-dump-file $(location hoststubgen_services.core_dump.txt) " +
-
-        "--in-jar $(location :services.core-for-hoststubgen) " +
-        "--policy-override-file $(location :ravenwood-services-policies) " +
-        "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) ",
-    srcs: [
-        ":services.core-for-hoststubgen",
-        ":ravenwood-services-policies",
-        ":ravenwood-standard-options",
-        ":ravenwood-annotation-allowed-classes",
-    ],
-    out: [
-        "ravenwood.jar",
-
-        // Following files are created just as FYI.
-        "hoststubgen_services.core_keep_all.txt",
-        "hoststubgen_services.core_dump.txt",
-
-        "hoststubgen_services.core.log",
-        "hoststubgen_services.core_stats.csv",
-        "hoststubgen_services.core_apis.csv",
-    ],
-    defaults: ["ravenwood-internal-only-visibility-genrule"],
-}
-
-java_genrule {
-    name: "services.core.ravenwood",
-    defaults: ["ravenwood-internal-only-visibility-genrule"],
-    cmd: "cp $(in) $(out)",
-    srcs: [
-        ":services.core.ravenwood-base{ravenwood.jar}",
-    ],
-    out: [
-        "services.core.ravenwood.jar",
-    ],
-}
-
-// TODO(b/313930116) This jarjar is a bit slow. We should use hoststubgen for renaming,
-// but services.core.ravenwood has complex dependencies, so it'll take more than
-// just using hoststubgen "rename"s.
-java_library {
-    name: "services.core.ravenwood-jarjar",
-    defaults: ["ravenwood-internal-only-visibility-java"],
+    name: "framework-minus-apex-for-host",
     installable: false,
-    static_libs: [
-        "services.core.ravenwood",
-    ],
-    jarjar_rules: ":ravenwood-services-jarjar-rules",
+    static_libs: ["framework-minus-apex"],
+    visibility: ["//frameworks/base/ravenwood"],
 }
 
-// Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
-// Rename some of the dependencies to make sure they're included in the intended order.
 java_library {
-    name: "100-framework-minus-apex.ravenwood",
-    defaults: ["ravenwood-internal-only-visibility-java"],
-    static_libs: [
-        "framework-minus-apex.ravenwood",
-    ],
-    sdk_version: "core_platform",
-    // See b/313930116. Jarjar is too slow on this jar. We use HostStubGen to do the rename.
-    // jarjar_rules: ":ravenwood-framework-jarjar-rules",
-}
-
-java_genrule {
-    // Use 200 to make sure it comes before the mainline stub ("all-updatable...").
-    name: "200-kxml2-android",
-    defaults: ["ravenwood-internal-only-visibility-genrule"],
-    cmd: "cp $(in) $(out)",
-    srcs: [":kxml2-android"],
-    out: ["200-kxml2-android.jar"],
-}
-
-java_genrule {
-    name: "z00-all-updatable-modules-system-stubs",
-    defaults: ["ravenwood-internal-only-visibility-genrule"],
-    cmd: "cp $(in) $(out)",
-    srcs: [":all-updatable-modules-system-stubs"],
-    out: ["z00-all-updatable-modules-system-stubs.jar"],
+    name: "all-updatable-modules-system-stubs-for-host",
+    installable: false,
+    static_libs: ["all-updatable-modules-system-stubs"],
+    visibility: ["//frameworks/base/ravenwood"],
 }
diff --git a/apct-tests/perftests/core/src/android/os/MessageQueuePerfTest.java b/apct-tests/perftests/core/src/android/os/MessageQueuePerfTest.java
new file mode 100644
index 0000000..b14de83
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/MessageQueuePerfTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.perftests.utils.Stats;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.Random;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Performance tests for {@link MessageQueue}.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MessageQueuePerfTest {
+    static final String TAG = "MessageQueuePerfTest";
+    private static final int PER_THREAD_MESSAGE_COUNT = 1000;
+    private static final int THREAD_COUNT = 8;
+    private static final int TOTAL_MESSAGE_COUNT = PER_THREAD_MESSAGE_COUNT * THREAD_COUNT;
+
+    static Object sLock = new Object();
+    private ArrayList<Long> mResults;
+
+    @Before
+    public void setUp() { }
+
+    @After
+    public void tearDown() { }
+
+    class EnqueueThread extends Thread {
+        CountDownLatch mStartLatch;
+        CountDownLatch mEndLatch;
+        Handler mHandler;
+        int mMessageStartIdx;
+        Message[] mMessages;
+        long[] mDelays;
+
+        EnqueueThread(CountDownLatch startLatch, CountDownLatch endLatch, Handler handler,
+                int startIdx, Message[] messages, long[] delays) {
+            super();
+            mStartLatch = startLatch;
+            mEndLatch = endLatch;
+            mHandler = handler;
+            mMessageStartIdx = startIdx;
+            mMessages = messages;
+            mDelays = delays;
+        }
+
+        @Override
+        public void run() {
+            Log.d(TAG, "Enqueue thread started at message index " + mMessageStartIdx);
+            try {
+                mStartLatch.await();
+            } catch (InterruptedException e) {
+
+            }
+            long now = SystemClock.uptimeMillis();
+            long startTimeNS = SystemClock.elapsedRealtimeNanos();
+            for (int i = mMessageStartIdx; i < (mMessageStartIdx + PER_THREAD_MESSAGE_COUNT); i++) {
+                if (mDelays[i] == 0) {
+                    mHandler.sendMessageAtFrontOfQueue(mMessages[i]);
+                } else {
+                    mHandler.sendMessageAtTime(mMessages[i], now + mDelays[i]);
+                }
+            }
+            long endTimeNS = SystemClock.elapsedRealtimeNanos();
+
+            synchronized (sLock) {
+                mResults.add(endTimeNS - startTimeNS);
+            }
+            mEndLatch.countDown();
+        }
+    }
+
+    class TestHandler extends Handler {
+        TestHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void handleMessage(Message msg) { }
+    }
+
+    void reportPerf(String prefix, int threadCount, int perThreadMessageCount) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Stats stats = new Stats(mResults);
+
+        Log.d(TAG, "Reporting perf now");
+
+        Bundle status = new Bundle();
+        status.putLong(prefix + "_median_ns", stats.getMedian());
+        status.putLong(prefix + "_mean_ns", (long) stats.getMean());
+        status.putLong(prefix + "_min_ns", stats.getMin());
+        status.putLong(prefix + "_max_ns", stats.getMax());
+        status.putLong(prefix + "_stddev_ns", (long) stats.getStandardDeviation());
+        status.putLong(prefix + "_nr_threads", threadCount);
+        status.putLong(prefix + "_msgs_per_thread", perThreadMessageCount);
+        instrumentation.sendStatus(Activity.RESULT_OK, status);
+    }
+
+    HandlerThread mHandlerThread;
+
+    private void fillMessagesArray(Message[] messages) {
+        for (int i = 0; i < messages.length; i++) {
+            messages[i] = mHandlerThread.getThreadHandler().obtainMessage(i);
+        }
+    }
+
+    private void startTestAndWaitOnThreads(CountDownLatch threadStartLatch, CountDownLatch threadEndLatch) {
+        try {
+            threadStartLatch.countDown();
+            Log.e(TAG, "Test threads started");
+            threadEndLatch.await();
+        } catch (InterruptedException ignored) {
+        }
+        Log.e(TAG, "Test threads ended, quitting handler thread");
+    }
+
+    @Test
+    public void benchmarkEnqueueAtFrontOfQueue() {
+        CountDownLatch threadStartLatch = new CountDownLatch(1);
+        CountDownLatch threadEndLatch  = new CountDownLatch(THREAD_COUNT);
+        mHandlerThread = new HandlerThread("MessageQueuePerfTest");
+        mHandlerThread.start();
+        Message[] messages = new Message[TOTAL_MESSAGE_COUNT];
+        fillMessagesArray(messages);
+
+        long[] delays = new long[TOTAL_MESSAGE_COUNT];
+        mResults = new ArrayList<>();
+
+        TestHandler handler = new TestHandler(mHandlerThread.getLooper());
+        for (int i = 0; i < THREAD_COUNT; i++) {
+            EnqueueThread thread = new EnqueueThread(threadStartLatch, threadEndLatch, handler,
+                    i * PER_THREAD_MESSAGE_COUNT, messages, delays);
+            thread.start();
+        }
+
+        startTestAndWaitOnThreads(threadStartLatch, threadEndLatch);
+
+        mHandlerThread.quitSafely();
+
+        reportPerf("enqueueAtFront", THREAD_COUNT, PER_THREAD_MESSAGE_COUNT);
+    }
+
+    /**
+     * Fill array with random delays, for benchmarkEnqueueDelayed
+     */
+    public long[] fillDelayArray() {
+        long[] delays = new long[TOTAL_MESSAGE_COUNT];
+        Random rand = new Random(0xDEADBEEF);
+        for (int i = 0; i < TOTAL_MESSAGE_COUNT; i++) {
+            delays[i] = Math.abs(rand.nextLong() % 5000);
+        }
+        return delays;
+    }
+
+    @Test
+    public void benchmarkEnqueueDelayed() {
+        CountDownLatch threadStartLatch = new CountDownLatch(1);
+        CountDownLatch threadEndLatch  = new CountDownLatch(THREAD_COUNT);
+        mHandlerThread = new HandlerThread("MessageQueuePerfTest");
+        mHandlerThread.start();
+        Message[] messages = new Message[TOTAL_MESSAGE_COUNT];
+        fillMessagesArray(messages);
+
+        long[] delays = fillDelayArray();
+        mResults = new ArrayList<>();
+
+        TestHandler handler = new TestHandler(mHandlerThread.getLooper());
+        for (int i = 0; i < THREAD_COUNT; i++) {
+            EnqueueThread thread = new EnqueueThread(threadStartLatch, threadEndLatch, handler,
+                    i * PER_THREAD_MESSAGE_COUNT, messages, delays);
+            thread.start();
+        }
+
+        startTestAndWaitOnThreads(threadStartLatch, threadEndLatch);
+
+        mHandlerThread.quitSafely();
+
+        reportPerf("enqueueDelayed", THREAD_COUNT, PER_THREAD_MESSAGE_COUNT);
+    }
+}
diff --git a/apex/jobscheduler/service/aconfig/app_idle.aconfig b/apex/jobscheduler/service/aconfig/app_idle.aconfig
index c8976ca..f079c02 100644
--- a/apex/jobscheduler/service/aconfig/app_idle.aconfig
+++ b/apex/jobscheduler/service/aconfig/app_idle.aconfig
@@ -12,3 +12,12 @@
     }
 }
 
+flag {
+    name: "screen_time_bypass"
+    namespace: "backstage_power"
+    description: "Bypass the screen time check for bucket evaluation"
+    bug: "374114769"
+    metadata {
+       purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index e5389b4..98e53ab 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -75,3 +75,17 @@
        purpose: PURPOSE_BUGFIX
    }
 }
+
+flag {
+   name: "enforce_quota_policy_to_fgs_jobs"
+   namespace: "backstage_power"
+   description: "Applies the normal quota policy to FGS jobs"
+   bug: "341201311"
+}
+
+flag {
+   name: "adjust_quota_default_constants"
+   namespace: "backstage_power"
+   description: "Adjust quota default parameters"
+   bug: "347058927"
+}
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index c1894f0..a37779e 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -3568,7 +3568,7 @@
             Slog.i(TAG, "becomeActiveLocked, reason=" + activeReason
                     + ", changeLightIdle=" + changeLightIdle);
         }
-        if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {
+        if (mState != STATE_ACTIVE || mLightState != LIGHT_STATE_ACTIVE) {
             moveToStateLocked(STATE_ACTIVE, activeReason);
             mInactiveTimeout = newInactiveTimeout;
             resetIdleManagementLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index a1c72fb..885bad5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -99,10 +99,10 @@
  * the number of jobs or sessions that can run within the window. Regardless of bucket, apps will
  * not be allowed to run more than 20 jobs within the past 10 minutes.
  *
- * Jobs are throttled while an app is not in a foreground state. All jobs are allowed to run
- * freely when an app enters the foreground state and are restricted when the app leaves the
- * foreground state. However, jobs that are started while the app is in the TOP state do not count
- * towards any quota and are not restricted regardless of the app's state change.
+ * Jobs are throttled while an app is not in a TOP or BOUND_TOP state. All jobs are allowed to run
+ * freely when an app enters the TOP or BOUND_TOP state and are restricted when the app leaves those
+ * states. However, jobs that are started while the app is in the TOP state do not count towards any
+ * quota and are not restricted regardless of the app's state change.
  *
  * Jobs will not be throttled when the device is charging. The device is considered to be charging
  * once the {@link BatteryManager#ACTION_CHARGING} intent has been broadcast.
@@ -394,13 +394,13 @@
      * minutes to run its jobs.
      */
     private final long[] mBucketPeriodsMs = new long[]{
-            QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS,
-            QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS,
-            QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
+            QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS,
+            QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS,
+            QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS,
             QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS,
             0, // NEVER
             QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS,
-            QcConstants.DEFAULT_WINDOW_SIZE_EXEMPTED_MS
+            QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS
     };
 
     /** The maximum period any bucket can have. */
@@ -454,7 +454,7 @@
      */
     private final long[] mEJLimitsMs = new long[]{
             QcConstants.DEFAULT_EJ_LIMIT_ACTIVE_MS,
-            QcConstants.DEFAULT_EJ_LIMIT_WORKING_MS,
+            QcConstants.DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS,
             QcConstants.DEFAULT_EJ_LIMIT_FREQUENT_MS,
             QcConstants.DEFAULT_EJ_LIMIT_RARE_MS,
             0, // NEVER
@@ -476,7 +476,8 @@
     /**
      * Length of time used to split an app's top time into chunks.
      */
-    private long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+    private long mEJTopAppTimeChunkSizeMs =
+            QcConstants.DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
 
     /**
      * How much EJ quota to give back to an app based on the number of top app time chunks it had.
@@ -486,7 +487,7 @@
     /**
      * How much EJ quota to give back to an app based on each non-top user interaction.
      */
-    private long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS;
+    private long mEJRewardInteractionMs = QcConstants.DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS;
 
     /**
      * How much EJ quota to give back to an app based on each notification seen event.
@@ -498,14 +499,6 @@
 
     private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
 
-    private long mQuotaBumpAdditionalDurationMs =
-            QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS;
-    private int mQuotaBumpAdditionalJobCount = QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT;
-    private int mQuotaBumpAdditionalSessionCount =
-            QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT;
-    private long mQuotaBumpWindowSizeMs = QcConstants.DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS;
-    private int mQuotaBumpLimit = QcConstants.DEFAULT_QUOTA_BUMP_LIMIT;
-
     /**
      * List of system apps with the {@link android.Manifest.permission#INSTALL_PACKAGES} permission
      * granted for each user.
@@ -567,12 +560,19 @@
             ActivityManager.getService().registerUidObserver(new QcUidObserver(),
                     ActivityManager.UID_OBSERVER_PROCSTATE,
                     ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, null);
+            if (Flags.enforceQuotaPolicyToFgsJobs()) {
+                ActivityManager.getService().registerUidObserver(new QcUidObserver(),
+                        ActivityManager.UID_OBSERVER_PROCSTATE,
+                        ActivityManager.PROCESS_STATE_BOUND_TOP, null);
+            }
             ActivityManager.getService().registerUidObserver(new QcUidObserver(),
                     ActivityManager.UID_OBSERVER_PROCSTATE,
                     ActivityManager.PROCESS_STATE_TOP, null);
         } catch (RemoteException e) {
             // ignored; both services live in system_server
         }
+
+        processQuotaConstantsAdjustment();
     }
 
     @Override
@@ -1090,7 +1090,7 @@
         // essentially run until they reach the maximum limit.
         if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
             return calculateTimeUntilQuotaConsumedLocked(
-                    events, startMaxElapsed, maxExecutionTimeRemainingMs, false);
+                    events, startMaxElapsed, maxExecutionTimeRemainingMs);
         }
 
         // Need to check both max time and period time in case one is less than the other.
@@ -1099,9 +1099,9 @@
         // bucket value.
         return Math.min(
                 calculateTimeUntilQuotaConsumedLocked(
-                        events, startMaxElapsed, maxExecutionTimeRemainingMs, false),
+                        events, startMaxElapsed, maxExecutionTimeRemainingMs),
                 calculateTimeUntilQuotaConsumedLocked(
-                        events, startWindowElapsed, allowedTimeRemainingMs, true));
+                        events, startWindowElapsed, allowedTimeRemainingMs));
     }
 
     /**
@@ -1111,36 +1111,12 @@
      * @param deadSpaceMs        How much time can be allowed to count towards the quota
      */
     private long calculateTimeUntilQuotaConsumedLocked(@NonNull List<TimedEvent> sessions,
-            final long windowStartElapsed, long deadSpaceMs, boolean allowQuotaBumps) {
+            final long windowStartElapsed, long deadSpaceMs) {
         long timeUntilQuotaConsumedMs = 0;
         long start = windowStartElapsed;
-        int numQuotaBumps = 0;
-        final long quotaBumpWindowStartElapsed =
-                sElapsedRealtimeClock.millis() - mQuotaBumpWindowSizeMs;
         final int numSessions = sessions.size();
-        if (allowQuotaBumps) {
-            for (int i = numSessions - 1; i >= 0; --i) {
-                TimedEvent event = sessions.get(i);
-
-                if (event instanceof QuotaBump) {
-                    if (event.getEndTimeElapsed() >= quotaBumpWindowStartElapsed
-                            && numQuotaBumps++ < mQuotaBumpLimit) {
-                        deadSpaceMs += mQuotaBumpAdditionalDurationMs;
-                    } else {
-                        break;
-                    }
-                }
-            }
-        }
         for (int i = 0; i < numSessions; ++i) {
-            TimedEvent event = sessions.get(i);
-
-            if (event instanceof QuotaBump) {
-                continue;
-            }
-
-            TimingSession session = (TimingSession) event;
-
+            TimingSession session = (TimingSession) sessions.get(i);
             if (session.endTimeElapsed < windowStartElapsed) {
                 // Outside of window. Ignore.
                 continue;
@@ -1325,41 +1301,15 @@
         final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
         final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
         int sessionCountInWindow = 0;
-        int numQuotaBumps = 0;
-        final long quotaBumpWindowStartElapsed = nowElapsed - mQuotaBumpWindowSizeMs;
         // The minimum time between the start time and the beginning of the events that were
         // looked at --> how much time the stats will be valid for.
         long emptyTimeMs = Long.MAX_VALUE;
         // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
         // the most recent ones.
         final int loopStart = events.size() - 1;
-        // Process QuotaBumps first to ensure the limits are properly adjusted.
-        for (int i = loopStart; i >= 0; --i) {
-            TimedEvent event = events.get(i);
-
-            if (event.getEndTimeElapsed() < quotaBumpWindowStartElapsed
-                    || numQuotaBumps >= mQuotaBumpLimit) {
-                break;
-            }
-
-            if (event instanceof QuotaBump) {
-                stats.allowedTimePerPeriodMs += mQuotaBumpAdditionalDurationMs;
-                stats.jobCountLimit += mQuotaBumpAdditionalJobCount;
-                stats.sessionCountLimit += mQuotaBumpAdditionalSessionCount;
-                emptyTimeMs = Math.min(emptyTimeMs,
-                        event.getEndTimeElapsed() - quotaBumpWindowStartElapsed);
-                numQuotaBumps++;
-            }
-        }
         TimingSession lastSeenTimingSession = null;
         for (int i = loopStart; i >= 0; --i) {
-            TimedEvent event = events.get(i);
-
-            if (event instanceof QuotaBump) {
-                continue;
-            }
-
-            TimingSession session = (TimingSession) event;
+            TimingSession session = (TimingSession) events.get(i);
 
             // Window management.
             if (startWindowElapsed < session.endTimeElapsed) {
@@ -1464,6 +1414,13 @@
         }
     }
 
+    void processQuotaConstantsAdjustment() {
+        if (Flags.adjustQuotaDefaultConstants()) {
+            mQcConstants.adjustDefaultBucketWindowSizes();
+            mQcConstants.adjustDefaultEjLimits();
+        }
+    }
+
     @VisibleForTesting
     void incrementJobCountLocked(final int userId, @NonNull final String packageName, int count) {
         final long now = sElapsedRealtimeClock.millis();
@@ -2053,28 +2010,6 @@
     }
 
     @VisibleForTesting
-    static final class QuotaBump implements TimedEvent {
-        // Event timestamp in elapsed realtime timebase.
-        public final long eventTimeElapsed;
-
-        QuotaBump(long eventElapsed) {
-            this.eventTimeElapsed = eventElapsed;
-        }
-
-        @Override
-        public long getEndTimeElapsed() {
-            return eventTimeElapsed;
-        }
-
-        @Override
-        public void dump(IndentingPrintWriter pw) {
-            pw.print("Quota bump @ ");
-            pw.print(eventTimeElapsed);
-            pw.println();
-        }
-    }
-
-    @VisibleForTesting
     static final class ShrinkableDebits {
         /** The amount of quota remaining. Can be negative if limit changes. */
         private long mDebitTally;
@@ -2523,21 +2458,6 @@
                 updateStandbyBucket(userId, packageName, bucketIndex);
             });
         }
-
-        @Override
-        public void triggerTemporaryQuotaBump(String packageName, @UserIdInt int userId) {
-            synchronized (mLock) {
-                List<TimedEvent> events = mTimingEvents.get(userId, packageName);
-                if (events == null || events.size() == 0) {
-                    // If the app hasn't run any jobs, there's no point giving it a quota bump.
-                    return;
-                }
-                events.add(new QuotaBump(sElapsedRealtimeClock.millis()));
-                invalidateAllExecutionStatsLocked(userId, packageName);
-            }
-            // Update jobs out of band.
-            mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
-        }
     }
 
     @VisibleForTesting
@@ -2706,6 +2626,12 @@
         }
     }
 
+    @VisibleForTesting
+    int getProcessStateQuotaFreeThreshold() {
+        return Flags.enforceQuotaPolicyToFgsJobs() ? ActivityManager.PROCESS_STATE_BOUND_TOP :
+                ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+    }
+
     private class QcHandler extends Handler {
 
         QcHandler(Looper looper) {
@@ -2832,15 +2758,15 @@
                                 mTopAppCache.put(uid, true);
                                 mTopAppGraceCache.delete(uid);
                                 if (mForegroundUids.get(uid)) {
-                                    // Went from FGS to TOP. We don't need to reprocess timers or
-                                    // jobs.
+                                    // Went from a process state with quota free to TOP. We don't
+                                    // need to reprocess timers or jobs.
                                     break;
                                 }
                                 mForegroundUids.put(uid, true);
                                 isQuotaFree = true;
                             } else {
                                 final boolean reprocess;
-                                if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                                if (procState <= getProcessStateQuotaFreeThreshold()) {
                                     reprocess = !mForegroundUids.get(uid);
                                     mForegroundUids.put(uid, true);
                                     isQuotaFree = true;
@@ -3008,7 +2934,6 @@
         mQcConstants.mRateLimitingConstantsUpdated = false;
         mQcConstants.mExecutionPeriodConstantsUpdated = false;
         mQcConstants.mEJLimitConstantsUpdated = false;
-        mQcConstants.mQuotaBumpConstantsUpdated = false;
     }
 
     @Override
@@ -3035,7 +2960,6 @@
         private boolean mRateLimitingConstantsUpdated = false;
         private boolean mExecutionPeriodConstantsUpdated = false;
         private boolean mEJLimitConstantsUpdated = false;
-        private boolean mQuotaBumpConstantsUpdated = false;
 
         /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
         private static final String QC_CONSTANT_PREFIX = "qc_";
@@ -3183,21 +3107,6 @@
         @VisibleForTesting
         static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
                 QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS =
-                QC_CONSTANT_PREFIX + "quota_bump_additional_duration_ms";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT =
-                QC_CONSTANT_PREFIX + "quota_bump_additional_job_count";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT =
-                QC_CONSTANT_PREFIX + "quota_bump_additional_session_count";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_WINDOW_SIZE_MS =
-                QC_CONSTANT_PREFIX + "quota_bump_window_size_ms";
-        @VisibleForTesting
-        static final String KEY_QUOTA_BUMP_LIMIT =
-                QC_CONSTANT_PREFIX + "quota_bump_limit";
 
         private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
                 10 * 60 * 1000L; // 10 minutes
@@ -3213,14 +3122,28 @@
                 10 * 60 * 1000L; // 10 minutes
         private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
                 30 * 1000L; // 30 seconds
-        private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS =
+        // Legacy default window size for EXEMPTED bucket
+        private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
                 DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
-        private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
+        // Legacy default window size for ACTIVE bucket
+        private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
                 DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
-        private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
+        // Legacy default window size for WORKING bucket
+        private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
                 2 * 60 * 60 * 1000L; // 2 hours
-        private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS =
+        // Legacy default window size for FREQUENT bucket
+        private static final long DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS =
                 8 * 60 * 60 * 1000L; // 8 hours
+
+        private static final long DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS =
+                20 * 60 * 1000L; // 20 minutes.
+        private static final long DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS =
+                30 * 60 * 1000L; // 30 minutes.
+        private static final long DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS =
+                4 * 60 * 60 * 1000L; // 4 hours
+        private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
+                12 * 60 * 60 * 1000L; // 12 hours
+
         private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
                 24 * 60 * 60 * 1000L; // 24 hours
         private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3234,9 +3157,9 @@
                 75; // 75/window = 450/hr = 1/session
         private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_EXEMPTED;
         private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session
-                (int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
+                (int) (60.0 * DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
         private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session
-                (int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
+                (int) (25.0 * DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
         private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
                 (int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
         private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10;
@@ -3257,24 +3180,24 @@
         // TODO(267949143): set a different limit for headless system apps
         private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 60 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS;
-        private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
+        private static final long DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
+        private static final long DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS = 15 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_LIMIT_RARE_MS = DEFAULT_EJ_LIMIT_FREQUENT_MS;
         private static final long DEFAULT_EJ_LIMIT_RESTRICTED_MS = 5 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_LIMIT_ADDITION_SPECIAL_MS = 15 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_LIMIT_ADDITION_INSTALLER_MS = 30 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_WINDOW_SIZE_MS = 24 * HOUR_IN_MILLIS;
-        private static final long DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS = 30 * SECOND_IN_MILLIS;
+        private static final long DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
+                30 * SECOND_IN_MILLIS;
+        private static final long DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
+                5 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS;
-        private static final long DEFAULT_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS;
+        private static final long DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS;
+        private static final long DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS = 5 * SECOND_IN_MILLIS;
         private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0;
         private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS;
         private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS;
-        private static final long DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS = 1 * MINUTE_IN_MILLIS;
-        private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = 2;
-        private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = 1;
-        private static final long DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS = 8 * HOUR_IN_MILLIS;
-        private static final int DEFAULT_QUOTA_BUMP_LIMIT = 8;
 
         /**
          * How much time each app in the exempted bucket will have to run jobs within their standby
@@ -3321,28 +3244,28 @@
          * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past
          * WINDOW_SIZE_MS.
          */
-        public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_WINDOW_SIZE_EXEMPTED_MS;
+        public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
          * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_ACTIVE_MS} within the past
          * WINDOW_SIZE_MS.
          */
-        public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_WINDOW_SIZE_ACTIVE_MS;
+        public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
          * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_WORKING_MS} within the past
          * WINDOW_SIZE_MS.
          */
-        public long WINDOW_SIZE_WORKING_MS = DEFAULT_WINDOW_SIZE_WORKING_MS;
+        public long WINDOW_SIZE_WORKING_MS = DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
          * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_FREQUENT_MS} within the past
          * WINDOW_SIZE_MS.
          */
-        public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_WINDOW_SIZE_FREQUENT_MS;
+        public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS;
 
         /**
          * The quota window size of the particular standby bucket. Apps in this standby bucket are
@@ -3503,7 +3426,7 @@
          * standby bucket can only have expedited job sessions totalling EJ_LIMIT (without factoring
          * in any rewards or free EJs).
          */
-        public long EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_WORKING_MS;
+        public long EJ_LIMIT_WORKING_MS = DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS;
 
         /**
          * The total expedited job session limit of the particular standby bucket. Apps in this
@@ -3547,7 +3470,7 @@
         /**
          * Length of time used to split an app's top time into chunks.
          */
-        public long EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+        public long EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
 
         /**
          * How much EJ quota to give back to an app based on the number of top app time chunks it
@@ -3558,7 +3481,7 @@
         /**
          * How much EJ quota to give back to an app based on each non-top user interaction.
          */
-        public long EJ_REWARD_INTERACTION_MS = DEFAULT_EJ_REWARD_INTERACTION_MS;
+        public long EJ_REWARD_INTERACTION_MS = DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS;
 
         /**
          * How much EJ quota to give back to an app based on each notification seen event.
@@ -3576,32 +3499,51 @@
          */
         public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
 
-        /**
-         * How much additional session duration to give an app for each accepted quota bump.
-         */
-        public long QUOTA_BUMP_ADDITIONAL_DURATION_MS = DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS;
+        void adjustDefaultBucketWindowSizes() {
+            WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+            WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+            WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
+            WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
 
-        /**
-         * How many additional regular jobs to give an app for each accepted quota bump.
-         */
-        public int QUOTA_BUMP_ADDITIONAL_JOB_COUNT = DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT;
+            mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
+                    mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+                    Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
+            mBucketPeriodsMs[ACTIVE_INDEX] = Math.max(
+                    mAllowedTimePerPeriodMs[ACTIVE_INDEX],
+                    Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
+            mBucketPeriodsMs[WORKING_INDEX] = Math.max(
+                    mAllowedTimePerPeriodMs[WORKING_INDEX],
+                    Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
+            mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
+                    mAllowedTimePerPeriodMs[FREQUENT_INDEX],
+                    Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+        }
 
-        /**
-         * How many additional sessions to give an app for each accepted quota bump.
-         */
-        public int QUOTA_BUMP_ADDITIONAL_SESSION_COUNT =
-                DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT;
+        void adjustDefaultEjLimits() {
+            EJ_LIMIT_WORKING_MS = DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
+            EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+            EJ_REWARD_INTERACTION_MS = DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
 
-        /**
-         * The rolling window size within which to accept and apply quota bump events.
-         */
-        public long QUOTA_BUMP_WINDOW_SIZE_MS = DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS;
+            // The limit must be in the range [15 minutes, active limit].
+            mEJLimitsMs[WORKING_INDEX] = Math.max(15 * MINUTE_IN_MILLIS,
+                        Math.min(mEJLimitsMs[ACTIVE_INDEX], EJ_LIMIT_WORKING_MS));
 
-        /**
-         * The maximum number of quota bumps to accept and apply within the
-         * {@link #QUOTA_BUMP_WINDOW_SIZE_MS window}.
-         */
-        public int QUOTA_BUMP_LIMIT = DEFAULT_QUOTA_BUMP_LIMIT;
+            // Limit interaction reward to be in the range [5 seconds, 15 minutes] per event.
+            mEJRewardInteractionMs = Math.min(15 * MINUTE_IN_MILLIS,
+                        Math.max(5 * SECOND_IN_MILLIS, EJ_REWARD_INTERACTION_MS));
+
+            // Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
+            long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS,
+                    Math.max(1, EJ_TOP_APP_TIME_CHUNK_SIZE_MS));
+            mEJTopAppTimeChunkSizeMs = newChunkSizeMs;
+            if (mEJTopAppTimeChunkSizeMs < mEJRewardTopAppMs) {
+                // Not making chunk sizes and top rewards to be the upper/lower
+                // limits of the other to allow trying different policies. Just log
+                // the discrepancy.
+                Slog.w(TAG, "EJ top app time chunk less than reward: "
+                        + mEJTopAppTimeChunkSizeMs + " vs " + mEJRewardTopAppMs);
+            }
+        }
 
         public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
                 @NonNull String key) {
@@ -3639,14 +3581,6 @@
                     updateEJLimitConstantsLocked();
                     break;
 
-                case KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS:
-                case KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT:
-                case KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT:
-                case KEY_QUOTA_BUMP_WINDOW_SIZE_MS:
-                case KEY_QUOTA_BUMP_LIMIT:
-                    updateQuotaBumpConstantsLocked();
-                    break;
-
                 case KEY_MAX_JOB_COUNT_EXEMPTED:
                     MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED);
                     int newExemptedMaxJobCount =
@@ -3779,7 +3713,9 @@
                 case KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS:
                     // We don't need to re-evaluate execution stats or constraint status for this.
                     EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
-                            properties.getLong(key, DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS);
+                            properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
+                                    DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS);
                     // Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
                     long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS,
                             Math.max(1, EJ_TOP_APP_TIME_CHUNK_SIZE_MS));
@@ -3815,7 +3751,9 @@
                 case KEY_EJ_REWARD_INTERACTION_MS:
                     // We don't need to re-evaluate execution stats or constraint status for this.
                     EJ_REWARD_INTERACTION_MS =
-                            properties.getLong(key, DEFAULT_EJ_REWARD_INTERACTION_MS);
+                            properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS :
+                                    DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS);
                     // Limit interaction reward to be in the range [5 seconds, 15 minutes] per
                     // event.
                     mEJRewardInteractionMs = Math.min(15 * MINUTE_IN_MILLIS,
@@ -3889,14 +3827,23 @@
             MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
                     DEFAULT_MAX_EXECUTION_TIME_MS);
             WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
-                    DEFAULT_WINDOW_SIZE_EXEMPTED_MS);
+                    Flags.adjustQuotaDefaultConstants()
+                            ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+                            DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
             WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
-                    DEFAULT_WINDOW_SIZE_ACTIVE_MS);
+                    Flags.adjustQuotaDefaultConstants()
+                            ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+                            DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
             WINDOW_SIZE_WORKING_MS =
-                    properties.getLong(KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS);
+                    properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
+                            Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS :
+                                    DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS);
             WINDOW_SIZE_FREQUENT_MS =
                     properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS,
-                            DEFAULT_WINDOW_SIZE_FREQUENT_MS);
+                            Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS :
+                                    DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS);
             WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS,
                     DEFAULT_WINDOW_SIZE_RARE_MS);
             WINDOW_SIZE_RESTRICTED_MS =
@@ -4067,7 +4014,9 @@
             EJ_LIMIT_ACTIVE_MS = properties.getLong(
                     KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS);
             EJ_LIMIT_WORKING_MS = properties.getLong(
-                    KEY_EJ_LIMIT_WORKING_MS, DEFAULT_EJ_LIMIT_WORKING_MS);
+                    KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants()
+                            ? DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS :
+                            DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS);
             EJ_LIMIT_FREQUENT_MS = properties.getLong(
                     KEY_EJ_LIMIT_FREQUENT_MS, DEFAULT_EJ_LIMIT_FREQUENT_MS);
             EJ_LIMIT_RARE_MS = properties.getLong(
@@ -4145,65 +4094,6 @@
             }
         }
 
-        private void updateQuotaBumpConstantsLocked() {
-            if (mQuotaBumpConstantsUpdated) {
-                return;
-            }
-            mQuotaBumpConstantsUpdated = true;
-
-            // Query the values as an atomic set.
-            final DeviceConfig.Properties properties = DeviceConfig.getProperties(
-                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
-                    KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                    KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
-                    KEY_QUOTA_BUMP_WINDOW_SIZE_MS, KEY_QUOTA_BUMP_LIMIT);
-            QUOTA_BUMP_ADDITIONAL_DURATION_MS = properties.getLong(
-                    KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                    DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS);
-            QUOTA_BUMP_ADDITIONAL_JOB_COUNT = properties.getInt(
-                    KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT);
-            QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = properties.getInt(
-                    KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
-                    DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT);
-            QUOTA_BUMP_WINDOW_SIZE_MS = properties.getLong(
-                    KEY_QUOTA_BUMP_WINDOW_SIZE_MS, DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS);
-            QUOTA_BUMP_LIMIT = properties.getInt(
-                    KEY_QUOTA_BUMP_LIMIT, DEFAULT_QUOTA_BUMP_LIMIT);
-
-            // The window must be in the range [1 hour, 24 hours].
-            long newWindowSizeMs = Math.max(HOUR_IN_MILLIS,
-                    Math.min(MAX_PERIOD_MS, QUOTA_BUMP_WINDOW_SIZE_MS));
-            if (mQuotaBumpWindowSizeMs != newWindowSizeMs) {
-                mQuotaBumpWindowSizeMs = newWindowSizeMs;
-                mShouldReevaluateConstraints = true;
-            }
-            // The limit must be nonnegative.
-            int newLimit = Math.max(0, QUOTA_BUMP_LIMIT);
-            if (mQuotaBumpLimit != newLimit) {
-                mQuotaBumpLimit = newLimit;
-                mShouldReevaluateConstraints = true;
-            }
-            // The job count must be nonnegative.
-            int newJobAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_JOB_COUNT);
-            if (mQuotaBumpAdditionalJobCount != newJobAddition) {
-                mQuotaBumpAdditionalJobCount = newJobAddition;
-                mShouldReevaluateConstraints = true;
-            }
-            // The session count must be nonnegative.
-            int newSessionAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_SESSION_COUNT);
-            if (mQuotaBumpAdditionalSessionCount != newSessionAddition) {
-                mQuotaBumpAdditionalSessionCount = newSessionAddition;
-                mShouldReevaluateConstraints = true;
-            }
-            // The additional duration must be in the range [0, 10 minutes].
-            long newAdditionalDuration = Math.max(0,
-                    Math.min(10 * MINUTE_IN_MILLIS, QUOTA_BUMP_ADDITIONAL_DURATION_MS));
-            if (mQuotaBumpAdditionalDurationMs != newAdditionalDuration) {
-                mQuotaBumpAdditionalDurationMs = newAdditionalDuration;
-                mShouldReevaluateConstraints = true;
-            }
-        }
-
         private void dump(IndentingPrintWriter pw) {
             pw.println();
             pw.println("QuotaController:");
@@ -4266,15 +4156,6 @@
                     EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println();
             pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println();
 
-            pw.print(KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                    QUOTA_BUMP_ADDITIONAL_DURATION_MS).println();
-            pw.print(KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT,
-                    QUOTA_BUMP_ADDITIONAL_JOB_COUNT).println();
-            pw.print(KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
-                    QUOTA_BUMP_ADDITIONAL_SESSION_COUNT).println();
-            pw.print(KEY_QUOTA_BUMP_WINDOW_SIZE_MS, QUOTA_BUMP_WINDOW_SIZE_MS).println();
-            pw.print(KEY_QUOTA_BUMP_LIMIT, QUOTA_BUMP_LIMIT).println();
-
             pw.decreaseIndent();
         }
 
@@ -4492,37 +4373,19 @@
         return mQcConstants;
     }
 
-    @VisibleForTesting
-    long getQuotaBumpAdditionDurationMs() {
-        return mQuotaBumpAdditionalDurationMs;
-    }
-
-    @VisibleForTesting
-    int getQuotaBumpAdditionJobCount() {
-        return mQuotaBumpAdditionalJobCount;
-    }
-
-    @VisibleForTesting
-    int getQuotaBumpAdditionSessionCount() {
-        return mQuotaBumpAdditionalSessionCount;
-    }
-
-    @VisibleForTesting
-    int getQuotaBumpLimit() {
-        return mQuotaBumpLimit;
-    }
-
-    @VisibleForTesting
-    long getQuotaBumpWindowSizeMs() {
-        return mQuotaBumpWindowSizeMs;
-    }
-
     //////////////////////////// DATA DUMP //////////////////////////////
 
     @NeverCompile // Avoid size overhead of debugging code.
     @Override
     public void dumpControllerStateLocked(final IndentingPrintWriter pw,
             final Predicate<JobStatus> predicate) {
+        pw.println("Flags: ");
+        pw.println("    " + Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS
+                + ": " + Flags.adjustQuotaDefaultConstants());
+        pw.println("    " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS
+                + ": " + Flags.enforceQuotaPolicyToFgsJobs());
+        pw.println();
+
         pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
         pw.println();
 
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 6265d9b..a8641ae 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -629,14 +629,15 @@
     }
 
     /**
-     * Returns the index in the arrays of screenTimeThresholds and elapsedTimeThresholds
-     * that corresponds to how long since the app was used.
+     * Returns the index in the array of elapsedTimeThresholds that corresponds to
+     * how long since the app was used.
      * @param packageName
      * @param userId
      * @param elapsedRealtime current time
-     * @param screenTimeThresholds Array of screen times, in ascending order, first one is 0
+     * @param screenTimeThresholds Array of screen times, in ascending order,
+     *        first one is 0 (this will not be used any more)
      * @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0
-     * @return The index whose values the app's used time exceeds (in both arrays) or {@code -1} to
+     * @return The index whose values the app's used time exceeds or {@code -1} to
      *         indicate that the app has never been used.
      */
     int getThresholdIndex(String packageName, int userId, long elapsedRealtime,
@@ -646,7 +647,7 @@
                 elapsedRealtime, false);
         // If we don't have any state for the app, assume never used
         if (appUsageHistory == null || appUsageHistory.lastUsedElapsedTime < 0
-                || appUsageHistory.lastUsedScreenTime < 0) {
+                || (!Flags.screenTimeBypass() && appUsageHistory.lastUsedScreenTime < 0)) {
             return -1;
         }
 
@@ -659,7 +660,7 @@
         if (DEBUG) Slog.d(TAG, packageName + " screenOn=" + screenOnDelta
                 + ", elapsed=" + elapsedDelta);
         for (int i = screenTimeThresholds.length - 1; i >= 0; i--) {
-            if (screenOnDelta >= screenTimeThresholds[i]
+            if ((Flags.screenTimeBypass() || screenOnDelta >= screenTimeThresholds[i])
                 && elapsedDelta >= elapsedTimeThresholds[i]) {
                 return i;
             }
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 3534624..f80ccf7 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -7,6 +7,18 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+aconfig_declarations {
+    name: "bootanimation_flags",
+    package: "com.android.graphics.bootanimation.flags",
+    container: "system",
+    srcs: ["bootanimation_flags.aconfig"],
+}
+
+cc_aconfig_library {
+    name: "libbootanimationflags",
+    aconfig_declarations: "bootanimation_flags",
+}
+
 cc_defaults {
     name: "bootanimation_defaults",
 
@@ -28,6 +40,10 @@
         "liblog",
         "libutils",
     ],
+
+    static_libs: [
+        "libbootanimationflags",
+    ],
 }
 
 // bootanimation executable
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 87c9fa4..14e2387 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -15,6 +15,7 @@
  */
 
 #define LOG_NDEBUG 0
+
 #define LOG_TAG "BootAnimation"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
@@ -61,6 +62,8 @@
 
 #include "BootAnimation.h"
 
+#include <com_android_graphics_bootanimation_flags.h>
+
 #define ANIM_PATH_MAX 255
 #define STR(x)   #x
 #define STRTO(x) STR(x)
@@ -448,19 +451,21 @@
                     auto token = SurfaceComposerClient::getPhysicalDisplayToken(
                         event.header.displayId);
 
-                    if (token != mBootAnimation->mDisplayToken) {
+                    auto firstDisplay = mBootAnimation->mDisplays.front();
+                    if (token != firstDisplay.displayToken) {
                         // ignore hotplug of a secondary display
                         continue;
                     }
 
                     DisplayMode displayMode;
                     const status_t error = SurfaceComposerClient::getActiveDisplayMode(
-                        mBootAnimation->mDisplayToken, &displayMode);
+                                               firstDisplay.displayToken, &displayMode);
                     if (error != NO_ERROR) {
                         SLOGE("Can't get active display mode.");
                     }
                     mBootAnimation->resizeSurface(displayMode.resolution.getWidth(),
-                        displayMode.resolution.getHeight());
+                                                  displayMode.resolution.getHeight(),
+                                                  firstDisplay);
                 }
             }
         } while (numEvents > 0);
@@ -506,91 +511,106 @@
 status_t BootAnimation::readyToRun() {
     ATRACE_CALL();
     mAssets.addDefaultAssets();
+    return initDisplaysAndSurfaces();
+}
 
-    const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
-    if (ids.empty()) {
-        SLOGE("Failed to get ID for any displays\n");
+status_t BootAnimation::initDisplaysAndSurfaces() {
+    std::vector<PhysicalDisplayId> displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
+    if (displayIds.empty()) {
+        SLOGE("Failed to get ID for any displays");
         return NAME_NOT_FOUND;
     }
 
-    mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
-    if (mDisplayToken == nullptr) {
-        return NAME_NOT_FOUND;
+    // If Multi-Display isn't explicitly enabled, ignore all displays after the first one
+    if (!com::android::graphics::bootanimation::flags::multidisplay()) {
+        displayIds.erase(displayIds.begin() + 1, displayIds.end());
     }
 
-    DisplayMode displayMode;
-    const status_t error =
-            SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode);
-    if (error != NO_ERROR) {
-        return error;
+    for (const auto id : displayIds) {
+        if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) {
+            mDisplays.push_back({.displayToken = token});
+        } else {
+            SLOGE("Failed to get display token for a display");
+            SLOGE("Failed to get display token for display %" PRIu64, id.value);
+            return NAME_NOT_FOUND;
+        }
     }
 
+    // Initialize EGL
+    mEgl = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    eglInitialize(mEgl, nullptr, nullptr);
+    EGLConfig config = getEglConfig(mEgl);
+    EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+    mEglContext = eglCreateContext(mEgl, config, nullptr, contextAttributes);
+
     mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
     mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
-    ui::Size resolution = displayMode.resolution;
-    resolution = limitSurfaceSize(resolution.width, resolution.height);
-    // create the native surface
-    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
-            resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565,
-            ISurfaceComposerClient::eOpaque);
 
-    SurfaceComposerClient::Transaction t;
-    t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
-    t.setLayerStack(control, ui::DEFAULT_LAYER_STACK);
+    for (size_t displayIdx = 0; displayIdx < mDisplays.size(); displayIdx++) {
+        auto& display = mDisplays[displayIdx];
+        DisplayMode displayMode;
+        const status_t error =
+                SurfaceComposerClient::getActiveDisplayMode(display.displayToken, &displayMode);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        ui::Size resolution = displayMode.resolution;
+        // Clamp each surface to max size
+        resolution = limitSurfaceSize(resolution.width, resolution.height);
+        // Create the native surface
+        display.surfaceControl =
+                session()->createSurface(String8("BootAnimation"), resolution.width,
+                                         resolution.height, PIXEL_FORMAT_RGB_565,
+                                         ISurfaceComposerClient::eOpaque);
+        // Attach surface to layerstack, and associate layerstack with physical display
+        configureDisplayAndLayerStack(display, ui::LayerStack::fromValue(displayIdx));
+        display.surface = display.surfaceControl->getSurface();
+        display.eglSurface = eglCreateWindowSurface(mEgl, config, display.surface.get(), nullptr);
 
-    t.setLayer(control, 0x40000000)
-        .apply();
+        EGLint w, h;
+        eglQuerySurface(mEgl, display.eglSurface, EGL_WIDTH, &w);
+        eglQuerySurface(mEgl, display.eglSurface, EGL_HEIGHT, &h);
+        if (eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface,
+                           mEglContext) == EGL_FALSE) {
+            return NO_INIT;
+        }
+        display.initWidth = display.width = w;
+        display.initHeight = display.height = h;
+        mTargetInset = -1;
 
-    sp<Surface> s = control->getSurface();
+        // Rotate the boot animation according to the value specified in the sysprop
+        // ro.bootanim.set_orientation_<display_id>. Four values are supported: ORIENTATION_0,
+        // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270.
+        // If the value isn't specified or is ORIENTATION_0, nothing will be changed.
+        // This is needed to support boot animation in orientations different from the natural
+        // device orientation. For example, on tablets that may want to keep natural orientation
+        // portrait for applications compatibility and to have the boot animation in landscape.
+        rotateAwayFromNaturalOrientationIfNeeded(display);
 
-    // initialize opengl and egl
-    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    eglInitialize(display, nullptr, nullptr);
-    EGLConfig config = getEglConfig(display);
-    EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
-    // Initialize egl context with client version number 2.0.
-    EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
-    EGLContext context = eglCreateContext(display, config, nullptr, contextAttributes);
-    EGLint w, h;
-    eglQuerySurface(display, surface, EGL_WIDTH, &w);
-    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
-
-    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
-        return NO_INIT;
-    }
-
-    mDisplay = display;
-    mContext = context;
-    mSurface = surface;
-    mInitWidth = mWidth = w;
-    mInitHeight = mHeight = h;
-    mFlingerSurfaceControl = control;
-    mFlingerSurface = s;
-    mTargetInset = -1;
-
-    // Rotate the boot animation according to the value specified in the sysprop
-    // ro.bootanim.set_orientation_<display_id>. Four values are supported: ORIENTATION_0,
-    // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270.
-    // If the value isn't specified or is ORIENTATION_0, nothing will be changed.
-    // This is needed to support having boot animation in orientations different from the natural
-    // device orientation. For example, on tablets that may want to keep natural orientation
-    // portrait for applications compatibility and to have the boot animation in landscape.
-    rotateAwayFromNaturalOrientationIfNeeded();
-
-    projectSceneToWindow();
+        projectSceneToWindow(display);
+    } // end iteration over all display tokens
 
     // Register a display event receiver
     mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
     status_t status = mDisplayEventReceiver->initCheck();
     SLOGE_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver failed with status: %d",
-            status);
+             status);
     mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT,
-            new DisplayEventCallback(this), nullptr);
+                   new DisplayEventCallback(this), nullptr);
 
     return NO_ERROR;
 }
 
-void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() {
+void BootAnimation::configureDisplayAndLayerStack(const Display& display,
+                                                  ui::LayerStack layerStack) {
+    SurfaceComposerClient::Transaction t;
+    t.setDisplayLayerStack(display.displayToken, layerStack);
+    t.setLayerStack(display.surfaceControl, layerStack)
+     .setLayer(display.surfaceControl, 0x40000000)
+     .apply();
+}
+
+void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded(Display& display) {
     ATRACE_CALL();
     const auto orientation = parseOrientationProperty();
 
@@ -600,16 +620,16 @@
     }
 
     if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
-        std::swap(mWidth, mHeight);
-        std::swap(mInitWidth, mInitHeight);
-        mFlingerSurfaceControl->updateDefaultBufferSize(mWidth, mHeight);
+        std::swap(display.width, display.height);
+        std::swap(display.initWidth, display.initHeight);
+        display.surfaceControl->updateDefaultBufferSize(display.width, display.height);
     }
 
-    Rect displayRect(0, 0, mWidth, mHeight);
-    Rect layerStackRect(0, 0, mWidth, mHeight);
+    Rect displayRect(0, 0, display.width, display.height);
+    Rect layerStackRect(0, 0, display.width, display.height);
 
     SurfaceComposerClient::Transaction t;
-    t.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect);
+    t.setDisplayProjection(display.displayToken, orientation, layerStackRect, displayRect);
     t.apply();
 }
 
@@ -640,38 +660,37 @@
     return ui::ROTATION_0;
 }
 
-void BootAnimation::projectSceneToWindow() {
+void BootAnimation::projectSceneToWindow(const Display& display) {
     ATRACE_CALL();
-    glViewport(0, 0, mWidth, mHeight);
-    glScissor(0, 0, mWidth, mHeight);
+    glViewport(0, 0, display.width, display.height);
+    glScissor(0, 0, display.width, display.height);
 }
 
-void BootAnimation::resizeSurface(int newWidth, int newHeight) {
+void BootAnimation::resizeSurface(int newWidth, int newHeight, Display& display) {
     ATRACE_CALL();
     // We assume this function is called on the animation thread.
-    if (newWidth == mWidth && newHeight == mHeight) {
+    if (newWidth == display.width && newHeight == display.height) {
         return;
     }
-    SLOGV("Resizing the boot animation surface to %d %d", newWidth, newHeight);
 
-    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    eglDestroySurface(mDisplay, mSurface);
+    eglMakeCurrent(mEgl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglDestroySurface(mEgl, display.eglSurface);
 
     const auto limitedSize = limitSurfaceSize(newWidth, newHeight);
-    mWidth = limitedSize.width;
-    mHeight = limitedSize.height;
+    display.width = limitedSize.width;
+    display.height = limitedSize.height;
 
-    mFlingerSurfaceControl->updateDefaultBufferSize(mWidth, mHeight);
-    EGLConfig config = getEglConfig(mDisplay);
-    EGLSurface surface = eglCreateWindowSurface(mDisplay, config, mFlingerSurface.get(), nullptr);
-    if (eglMakeCurrent(mDisplay, surface, surface, mContext) == EGL_FALSE) {
-        SLOGE("Can't make the new surface current. Error %d", eglGetError());
+    display.surfaceControl->updateDefaultBufferSize(display.width, display.height);
+    EGLConfig config = getEglConfig(mEgl);
+    EGLSurface eglSurface = eglCreateWindowSurface(mEgl, config, display.surface.get(), nullptr);
+    if (eglMakeCurrent(mEgl, eglSurface, eglSurface, mEglContext) == EGL_FALSE) {
+        SLOGE("Can't make the new eglSurface current. Error %d", eglGetError());
         return;
     }
 
-    projectSceneToWindow();
+    projectSceneToWindow(display);
 
-    mSurface = surface;
+    display.eglSurface = eglSurface;
 }
 
 bool BootAnimation::preloadAnimation() {
@@ -801,24 +820,26 @@
     // animation.
     if (mZipFileName.empty()) {
         ALOGD("No animation file");
-        result = android();
+        result = android(mDisplays.front());
     } else {
         result = movie();
     }
 
     mCallbacks->shutdown();
-    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    eglDestroyContext(mDisplay, mContext);
-    eglDestroySurface(mDisplay, mSurface);
-    mFlingerSurface.clear();
-    mFlingerSurfaceControl.clear();
-    eglTerminate(mDisplay);
+    eglMakeCurrent(mEgl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglDestroyContext(mEgl, mEglContext);
+    for (auto& display : mDisplays) {
+        eglDestroySurface(mEgl, display.eglSurface);
+        display.surface.clear();
+        display.surfaceControl.clear();
+    }
+    eglTerminate(mEgl);
     eglReleaseThread();
     IPCThreadState::self()->stopProcess();
     return result;
 }
 
-bool BootAnimation::android() {
+bool BootAnimation::android(const Display& display) {
     ATRACE_CALL();
     glActiveTexture(GL_TEXTURE0);
 
@@ -836,7 +857,7 @@
 
     glClearColor(0,0,0,1);
     glClear(GL_COLOR_BUFFER_BIT);
-    eglSwapBuffers(mDisplay, mSurface);
+    eglSwapBuffers(mEgl, display.eglSurface);
 
     // Blend state
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -844,11 +865,11 @@
     const nsecs_t startTime = systemTime();
     do {
         processDisplayEvents();
-        const GLint xc = (mWidth  - mAndroid[0].w) / 2;
-        const GLint yc = (mHeight - mAndroid[0].h) / 2;
+        const GLint xc = (display.width  - mAndroid[0].w) / 2;
+        const GLint yc = (display.height - mAndroid[0].h) / 2;
         const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
-        glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
-                updateRect.height());
+        glScissor(updateRect.left, display.height - updateRect.bottom, updateRect.width(),
+                  updateRect.height());
 
         nsecs_t now = systemTime();
         double time = now - startTime;
@@ -862,14 +883,14 @@
         glEnable(GL_SCISSOR_TEST);
         glDisable(GL_BLEND);
         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
-        drawTexturedQuad(x,                 yc, mAndroid[1].w, mAndroid[1].h);
-        drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h);
+        drawTexturedQuad(x,                 yc, mAndroid[1].w, mAndroid[1].h, display);
+        drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h, display);
 
         glEnable(GL_BLEND);
         glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
-        drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h);
+        drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h, display);
 
-        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
+        EGLBoolean res = eglSwapBuffers(mEgl, display.eglSurface);
         if (res == EGL_FALSE)
             break;
 
@@ -888,7 +909,7 @@
 
 void BootAnimation::checkExit() {
     ATRACE_CALL();
-    // Allow surface flinger to gracefully request shutdown
+    // Allow SurfaceFlinger to gracefully request shutdown
     char value[PROPERTY_VALUE_MAX];
     property_get(EXIT_PROP_NAME, value, "0");
     int exitnow = atoi(value);
@@ -1034,7 +1055,8 @@
     return status;
 }
 
-void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
+void BootAnimation::drawText(const char* str, const Font& font, bool bold,
+                             int* x, int* y, const Display& display) {
     ATRACE_CALL();
     glEnable(GL_BLEND);  // Allow us to draw on top of the animation
     glBindTexture(GL_TEXTURE_2D, font.texture.name);
@@ -1045,14 +1067,14 @@
     const int strWidth = font.char_width * len;
 
     if (*x == TEXT_CENTER_VALUE) {
-        *x = (mWidth - strWidth) / 2;
+        *x = (display.width - strWidth) / 2;
     } else if (*x < 0) {
-        *x = mWidth + *x - strWidth;
+        *x = display.width + *x - strWidth;
     }
     if (*y == TEXT_CENTER_VALUE) {
-        *y = (mHeight - font.char_height) / 2;
+        *y = (display.height - font.char_height) / 2;
     } else if (*y < 0) {
-        *y = mHeight + *y - font.char_height;
+        *y = display.height + *y - font.char_height;
     }
 
     for (int i = 0; i < len; i++) {
@@ -1072,7 +1094,7 @@
         float v1 = v0 + 1.0f / FONT_NUM_ROWS / 2;
         float u1 = u0 + 1.0f / FONT_NUM_COLS;
         glUniform4f(mTextCropAreaLocation, u0, v0, u1, v1);
-        drawTexturedQuad(*x, *y, font.char_width, font.char_height);
+        drawTexturedQuad(*x, *y, font.char_width, font.char_height, display);
 
         *x += font.char_width;
     }
@@ -1082,7 +1104,8 @@
 }
 
 // We render 12 or 24 hour time.
-void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) {
+void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos,
+                              const Display& display) {
     ATRACE_CALL();
     static constexpr char TIME_FORMAT_12[] = "%l:%M";
     static constexpr char TIME_FORMAT_24[] = "%H:%M";
@@ -1105,10 +1128,11 @@
     char* out = timeBuff[0] == ' ' ? &timeBuff[1] : &timeBuff[0];
     int x = xPos;
     int y = yPos;
-    drawText(out, font, false, &x, &y);
+    drawText(out, font, false, &x, &y, display);
 }
 
-void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos) {
+void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos,
+                                 const Display& display) {
     ATRACE_CALL();
     static constexpr int PERCENT_LENGTH = 5;
 
@@ -1118,7 +1142,7 @@
     sprintf(percentBuff, "%d;", percent);
     int x = xPos;
     int y = yPos;
-    drawText(percentBuff, font, false, &x, &y);
+    drawText(percentBuff, font, false, &x, &y, display);
 }
 
 bool BootAnimation::parseAnimationDesc(Animation& animation)  {
@@ -1247,8 +1271,7 @@
 
 bool BootAnimation::preloadZip(Animation& animation) {
     ATRACE_CALL();
-    // read all the data structures
-    const size_t pcount = animation.parts.size();
+    const size_t numParts = animation.parts.size();
     void *cookie = nullptr;
     ZipFileRO* zip = animation.zip;
     if (!zip->startIteration(&cookie)) {
@@ -1284,8 +1307,8 @@
                 continue;
             }
 
-            for (size_t j = 0; j < pcount; j++) {
-                if (path.string() == animation.parts[j].path.c_str()) {
+            for (size_t partIdx = 0; partIdx < numParts; partIdx++) {
+                if (path.string() == animation.parts[partIdx].path.c_str()) {
                     uint16_t method;
                     // supports only stored png files
                     if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr,
@@ -1293,7 +1316,7 @@
                         if (method == ZipFileRO::kCompressStored) {
                             FileMap* map = zip->createEntryFileMap(entry);
                             if (map) {
-                                Animation::Part& part(animation.parts.editItemAt(j));
+                                Animation::Part& part(animation.parts.editItemAt(partIdx));
                                 if (leaf == "audio.wav") {
                                     // a part may have at most one audio file
                                     part.audioData = (uint8_t *)map->getDataPtr();
@@ -1324,7 +1347,8 @@
     // If there is trimData present, override the positioning defaults.
     for (Animation::Part& part : animation.parts) {
         const char* trimDataStr = part.trimData.c_str();
-        for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) {
+        const size_t numFramesInPart = part.frames.size();
+        for (size_t frameIdxInPart = 0; frameIdxInPart < numFramesInPart; frameIdxInPart++) {
             const char* endl = strstr(trimDataStr, "\n");
             // No more trimData for this part.
             if (endl == nullptr) {
@@ -1335,7 +1359,7 @@
             trimDataStr = ++endl;
             int width = 0, height = 0, x = 0, y = 0;
             if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) {
-                Animation::Frame& frame(part.frames.editItemAt(frameIdx));
+                Animation::Frame& frame(part.frames.editItemAt(frameIdxInPart));
                 frame.trimWidth = width;
                 frame.trimHeight = height;
                 frame.trimX = x;
@@ -1458,13 +1482,15 @@
     return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
 }
 
-void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, float height) {
+void BootAnimation::drawTexturedQuad(float xStart, float yStart,
+                                     float width, float height,
+                                     const Display& display) {
     ATRACE_CALL();
     // Map coordinates from screen space to world space.
-    float x0 = mapLinear(xStart, 0, mWidth, -1, 1);
-    float y0 = mapLinear(yStart, 0, mHeight, -1, 1);
-    float x1 = mapLinear(xStart + width, 0, mWidth, -1, 1);
-    float y1 = mapLinear(yStart + height, 0, mHeight, -1, 1);
+    float x0 = mapLinear(xStart, 0, display.width, -1, 1);
+    float y0 = mapLinear(yStart, 0, display.height, -1, 1);
+    float x1 = mapLinear(xStart + width, 0, display.width, -1, 1);
+    float y1 = mapLinear(yStart + height, 0, display.height, -1, 1);
     // Update quad vertex positions.
     quadPositions[0] = x0;
     quadPositions[1] = y0;
@@ -1511,7 +1537,7 @@
 
 bool BootAnimation::playAnimation(const Animation& animation) {
     ATRACE_CALL();
-    const size_t pcount = animation.parts.size();
+    const size_t numParts = animation.parts.size();
     nsecs_t frameDuration = s2ns(1) / animation.fps;
 
     SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
@@ -1521,9 +1547,9 @@
     int lastDisplayedProgress = 0;
     int colorTransitionStart = animation.colorTransitionStart;
     int colorTransitionEnd = animation.colorTransitionEnd;
-    for (size_t i=0 ; i<pcount ; i++) {
-        const Animation::Part& part(animation.parts[i]);
-        const size_t fcount = part.frames.size();
+    for (size_t partIdx = 0; partIdx < numParts; partIdx++) {
+        const Animation::Part& part(animation.parts[partIdx]);
+        const size_t numFramesInPart = part.frames.size();
         glBindTexture(GL_TEXTURE_2D, 0);
 
         // Handle animation package
@@ -1535,7 +1561,9 @@
         }
 
         // process the part not only while the count allows but also if already fading
-        for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
+        for (int frameIdx = 0;
+             !part.count || frameIdx < part.count || fadedFramesCount > 0;
+             frameIdx++) {
             if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
 
             // It's possible that the sysprops were not loaded yet at this boot phase.
@@ -1550,12 +1578,12 @@
                     const int transitionLength = colorTransitionEnd - colorTransitionStart;
                     if (part.postDynamicColoring) {
                         colorTransitionStart = 0;
-                        colorTransitionEnd = fmin(transitionLength, fcount - 1);
+                        colorTransitionEnd = fmin(transitionLength, numFramesInPart - 1);
                     }
                 }
             }
 
-            mCallbacks->playPart(i, part, r);
+            mCallbacks->playPart(partIdx, part, frameIdx);
 
             glClearColor(
                     part.backgroundColor[0],
@@ -1569,11 +1597,10 @@
 
             // For the last animation, if we have progress indicator from
             // the system, display it.
-            int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
-            bool displayProgress = animation.progressEnabled &&
-                (i == (pcount -1)) && currentProgress != 0;
+            const bool displayProgress = animation.progressEnabled && (partIdx == (numParts - 1)) &&
+                    android::base::GetIntProperty(PROGRESS_PROP_NAME, 0) != 0;
 
-            for (size_t j=0 ; j<fcount ; j++) {
+            for (size_t frameIdxInPart = 0; frameIdxInPart < numFramesInPart; frameIdxInPart++) {
                 if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
 
                 // Color progress is
@@ -1584,21 +1611,15 @@
                 // - 1 for parts that come after.
                 float colorProgress = part.useDynamicColoring
                     ? fmin(fmax(
-                        ((float)j - colorTransitionStart) /
+                        (static_cast<float>(frameIdxInPart) - colorTransitionStart) /
                             fmax(colorTransitionEnd - colorTransitionStart, 1.0f), 0.0f), 1.0f)
                     : (part.postDynamicColoring ? 1 : 0);
-
                 processDisplayEvents();
 
-                const double ratio_w = static_cast<double>(mWidth) / mInitWidth;
-                const double ratio_h = static_cast<double>(mHeight) / mInitHeight;
-                const int animationX = (mWidth - animation.width * ratio_w) / 2;
-                const int animationY = (mHeight - animation.height * ratio_h) / 2;
-
-                const Animation::Frame& frame(part.frames[j]);
+                const Animation::Frame& frame(part.frames[frameIdxInPart]);
                 nsecs_t lastFrame = systemTime();
 
-                if (r > 0) {
+                if (frameIdx > 0) {
                     glBindTexture(GL_TEXTURE_2D, frame.tid);
                 } else {
                     if (part.count != 1) {
@@ -1611,17 +1632,6 @@
                     initTexture(frame.map, &w, &h, false /* don't premultiply alpha */);
                 }
 
-                const int trimWidth = frame.trimWidth * ratio_w;
-                const int trimHeight = frame.trimHeight * ratio_h;
-                const int trimX = frame.trimX * ratio_w;
-                const int trimY = frame.trimY * ratio_h;
-                const int xc = animationX + trimX;
-                const int yc = animationY + trimY;
-                glClear(GL_COLOR_BUFFER_BIT);
-                // specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
-                // which is equivalent to mHeight - (yc + frame.trimHeight)
-                const int frameDrawY = mHeight - (yc + trimHeight);
-
                 float fade = 0;
                 // if the part hasn't been stopped yet then continue fading if necessary
                 if (exitPending() && part.hasFadingPhase()) {
@@ -1630,40 +1640,66 @@
                         fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
                     }
                 }
-                glUseProgram(mImageShader);
-                glUniform1i(mImageTextureLocation, 0);
-                glUniform1f(mImageFadeLocation, fade);
-                if (animation.dynamicColoringEnabled) {
-                    glUniform1f(mImageColorProgressLocation, colorProgress);
-                }
-                glEnable(GL_BLEND);
-                drawTexturedQuad(xc, frameDrawY, trimWidth, trimHeight);
-                glDisable(GL_BLEND);
 
-                if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
-                    drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
-                }
+                // Draw the current frame's texture on every physical display that is enabled.
+                for (const auto& display : mDisplays) {
+                    eglMakeCurrent(mEgl, display.eglSurface, display.eglSurface, mEglContext);
 
-                if (displayProgress) {
-                    int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
-                    // In case the new progress jumped suddenly, still show an
-                    // increment of 1.
-                    if (lastDisplayedProgress != 100) {
-                      // Artificially sleep 1/10th a second to slow down the animation.
-                      usleep(100000);
-                      if (lastDisplayedProgress < newProgress) {
-                        lastDisplayedProgress++;
-                      }
+                    const double ratioW =
+                            static_cast<double>(display.width) / display.initWidth;
+                    const double ratioH =
+                            static_cast<double>(display.height) / display.initHeight;
+                    const int animationX = (display.width - animation.width * ratioW) / 2;
+                    const int animationY = (display.height - animation.height * ratioH) / 2;
+
+                    const int trimWidth = frame.trimWidth * ratioW;
+                    const int trimHeight = frame.trimHeight * ratioH;
+                    const int trimX = frame.trimX * ratioW;
+                    const int trimY = frame.trimY * ratioH;
+                    const int xc = animationX + trimX;
+                    const int yc = animationY + trimY;
+                    projectSceneToWindow(display);
+                    handleViewport(frameDuration, display);
+                    glClear(GL_COLOR_BUFFER_BIT);
+                    // specify the y center as ceiling((height - frame.trimHeight) / 2)
+                    // which is equivalent to height - (yc + frame.trimHeight)
+                    const int frameDrawY = display.height - (yc + trimHeight);
+
+                    glUseProgram(mImageShader);
+                    glUniform1i(mImageTextureLocation, 0);
+                    glUniform1f(mImageFadeLocation, fade);
+                    if (animation.dynamicColoringEnabled) {
+                        glUniform1f(mImageColorProgressLocation, colorProgress);
                     }
-                    // Put the progress percentage right below the animation.
-                    int posY = animation.height / 3;
-                    int posX = TEXT_CENTER_VALUE;
-                    drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY);
+                    glEnable(GL_BLEND);
+                    drawTexturedQuad(xc, frameDrawY, trimWidth, trimHeight, display);
+                    glDisable(GL_BLEND);
+
+                    if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
+                        drawClock(animation.clockFont, part.clockPosX, part.clockPosY, display);
+                    }
+
+                    if (displayProgress) {
+                        int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
+                        // In case the new progress jumped suddenly, still show an
+                        // increment of 1.
+                        if (lastDisplayedProgress != 100) {
+                          // Artificially sleep 1/10th a second to slow down the animation.
+                          usleep(100000);
+                          if (lastDisplayedProgress < newProgress) {
+                            lastDisplayedProgress++;
+                          }
+                        }
+                        // Put the progress percentage right below the animation.
+                        int posY = animation.height / 3;
+                        int posX = TEXT_CENTER_VALUE;
+                        drawProgress(lastDisplayedProgress,
+                            animation.progressFont, posX, posY, display);
+                    }
+
+                    eglSwapBuffers(mEgl, display.eglSurface);
                 }
 
-                handleViewport(frameDuration);
-
-                eglSwapBuffers(mDisplay, mSurface);
 
                 nsecs_t now = systemTime();
                 nsecs_t delay = frameDuration - (now - lastFrame);
@@ -1709,9 +1745,7 @@
     // Free textures created for looping parts now that the animation is done.
     for (const Animation::Part& part : animation.parts) {
         if (part.count != 1) {
-            const size_t fcount = part.frames.size();
-            for (size_t j = 0; j < fcount; j++) {
-                const Animation::Frame& frame(part.frames[j]);
+            for (const auto& frame : part.frames) {
                 glDeleteTextures(1, &frame.tid);
             }
         }
@@ -1730,16 +1764,17 @@
     mLooper->pollOnce(0);
 }
 
-void BootAnimation::handleViewport(nsecs_t timestep) {
+void BootAnimation::handleViewport(nsecs_t timestep, const Display& display) {
     ATRACE_CALL();
-    if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) {
+    if (mShuttingDown || !display.surfaceControl || mTargetInset == 0) {
         return;
     }
     if (mTargetInset < 0) {
         // Poll the amount for the top display inset. This will return -1 until persistent properties
         // have been loaded.
-        mTargetInset = android::base::GetIntProperty("persist.sys.displayinset.top",
-                -1 /* default */, -1 /* min */, mHeight / 2 /* max */);
+        mTargetInset =
+                android::base::GetIntProperty("persist.sys.displayinset.top", -1 /* default */,
+                                              -1 /* min */, display.height / 2 /* max */);
     }
     if (mTargetInset <= 0) {
         return;
@@ -1751,19 +1786,27 @@
         int interpolatedInset = (cosf((fraction + 1) * M_PI) / 2.0f + 0.5f) * mTargetInset;
 
         SurfaceComposerClient::Transaction()
-                .setCrop(mFlingerSurfaceControl, Rect(0, interpolatedInset, mWidth, mHeight))
+                .setCrop(display.surfaceControl,
+                         Rect(0, interpolatedInset, display.width, display.height))
                 .apply();
     } else {
         // At the end of the animation, we switch to the viewport that DisplayManager will apply
         // later. This changes the coordinate system, and means we must move the surface up by
         // the inset amount.
-        Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset);
-        Rect displayRect(0, mTargetInset, mWidth, mHeight);
-
+        Rect layerStackRect(0, 0,
+                display.width,
+                display.height - mTargetInset);
+        Rect displayRect(0, mTargetInset,
+                display.width,
+                display.height);
         SurfaceComposerClient::Transaction t;
-        t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
-                .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
-        t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, layerStackRect, displayRect);
+        t.setPosition(display.surfaceControl, 0, -mTargetInset)
+                .setCrop(display.surfaceControl,
+                         Rect(0, mTargetInset,
+                              display.width,
+                              display.height));
+        t.setDisplayProjection(display.displayToken,
+                ui::ROTATION_0, layerStackRect, displayRect);
         t.apply();
 
         mTargetInset = mCurrentInset = 0;
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 8683b71..0a05746 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -31,6 +31,7 @@
 #include <binder/IBinder.h>
 
 #include <ui/Rotation.h>
+#include <ui/LayerStack.h>
 
 #include <EGL/egl.h>
 #include <GLES2/gl2.h>
@@ -119,6 +120,18 @@
         float endColors[4][3];   // End colors of dynamic color transition.
     };
 
+    // Collects all attributes that must be tracked per physical display.
+    struct Display {
+        int width;
+        int height;
+        int initWidth;
+        int initHeight;
+        EGLDisplay  eglSurface;
+        sp<IBinder> displayToken;
+        sp<SurfaceControl> surfaceControl;
+        sp<Surface> surface;
+    };
+
     // All callbacks will be called from this class's internal thread.
     class Callbacks : public RefBase {
     public:
@@ -181,14 +194,18 @@
         bool premultiplyAlpha = true);
     status_t initFont(Font* font, const char* fallback);
     void initShaders();
-    bool android();
+    bool android(const Display& display);
+    status_t initDisplaysAndSurfaces();
     bool movie();
-    void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
-    void drawClock(const Font& font, const int xPos, const int yPos);
-    void drawProgress(int percent, const Font& font, const int xPos, const int yPos);
+    void drawText(const char* str, const Font& font, bool bold,
+                  int* x, int* y, const Display& display);
+    void drawClock(const Font& font, const int xPos, const int yPos, const Display& display);
+    void drawProgress(int percent, const Font& font,
+                      const int xPos, const int yPos, const Display& display);
     void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
                    const Animation::Part& part, int fadedFramesCount);
-    void drawTexturedQuad(float xStart, float yStart, float width, float height);
+    void drawTexturedQuad(float xStart, float yStart,
+                          float width, float height, const Display& display);
     bool validClock(const Animation::Part& part);
     Animation* loadAnimation(const String8&);
     bool playAnimation(const Animation&);
@@ -200,36 +217,31 @@
     bool preloadAnimation();
     EGLConfig getEglConfig(const EGLDisplay&);
     ui::Size limitSurfaceSize(int width, int height) const;
-    void resizeSurface(int newWidth, int newHeight);
-    void projectSceneToWindow();
-    void rotateAwayFromNaturalOrientationIfNeeded();
+    void resizeSurface(int newWidth, int newHeight, Display& display);
+    void projectSceneToWindow(const Display& display);
+    void rotateAwayFromNaturalOrientationIfNeeded(Display& display);
     ui::Rotation parseOrientationProperty();
+    void configureDisplayAndLayerStack(const Display& display, ui::LayerStack layerStack);
 
     bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount,
                                int lastDisplayedProgress);
     void checkExit();
 
-    void handleViewport(nsecs_t timestep);
+    void handleViewport(nsecs_t timestep, const Display& display);
     void initDynamicColors();
 
     sp<SurfaceComposerClient>       mSession;
     AssetManager mAssets;
     Texture     mAndroid[2];
-    int         mWidth;
-    int         mHeight;
-    int         mInitWidth;
-    int         mInitHeight;
     int         mMaxWidth = 0;
     int         mMaxHeight = 0;
     int         mCurrentInset;
     int         mTargetInset;
     bool        mUseNpotTextures = false;
-    EGLDisplay  mDisplay;
-    EGLDisplay  mContext;
-    EGLDisplay  mSurface;
-    sp<IBinder> mDisplayToken;
-    sp<SurfaceControl> mFlingerSurfaceControl;
-    sp<Surface> mFlingerSurface;
+    EGLDisplay  mEgl;
+    EGLDisplay  mEglContext;
+    // Per-Display Attributes (to support multi-display)
+    std::vector<Display> mDisplays;
     bool        mClockEnabled;
     bool        mTimeIsAccurate;
     bool        mTimeFormat12Hour;
diff --git a/cmds/bootanimation/OWNERS b/cmds/bootanimation/OWNERS
index b6fb007..2eda44d 100644
--- a/cmds/bootanimation/OWNERS
+++ b/cmds/bootanimation/OWNERS
@@ -1,3 +1,4 @@
 dupin@google.com
 shanh@google.com
 jreck@google.com
+rahulbanerjee@google.com
\ No newline at end of file
diff --git a/cmds/bootanimation/bootanimation_flags.aconfig b/cmds/bootanimation/bootanimation_flags.aconfig
new file mode 100644
index 0000000..04837b9
--- /dev/null
+++ b/cmds/bootanimation/bootanimation_flags.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.graphics.bootanimation.flags"
+container: "system"
+
+flag {
+  name: "multidisplay"
+  namespace: "bootanimation"
+  description: "Enable boot animation on multiple displays (e.g. foldables)"
+  bug: "335406617"
+  is_fixed_read_only: true
+}
+
+
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index e86f814..b0ba019 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -21,18 +21,19 @@
  * header                     := magic version target_crc overlay_crc fulfilled_policies
  *                               enforce_overlayable target_path overlay_path overlay_name
  *                               debug_info
- * data                       := data_header target_entry* target_inline_entry*
-                                 target_inline_entry_value* config* overlay_entry* string_pool
+ * data                       := data_header target_entries target_inline_entries
+                                 target_inline_entry_value* config* overlay_entries string_pool
  * data_header                := target_entry_count target_inline_entry_count
                                  target_inline_entry_value_count config_count overlay_entry_count
  *                               string_pool_index
- * target_entry               := target_id overlay_id
- * target_inline_entry        := target_id start_value_index value_count
+ * target_entries             := target_id* overlay_id*
+ * target_inline_entries      := target_id* target_inline_value_header*
+ * target_inline_value_header := start_value_index value_count
  * target_inline_entry_value  := config_index Res_value::size padding(1) Res_value::type
  *                               Res_value::value
  * config                     := target_id Res_value::size padding(1) Res_value::type
  *                               Res_value::value
- * overlay_entry              := overlay_id target_id
+ * overlay_entries            := overlay_id* target_id*
  *
  * debug_info                       := string
  * enforce_overlayable              := <uint32_t>
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 8976924..00ef0c7 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -66,43 +66,57 @@
 void BinaryStreamVisitor::visit(const IdmapData& data) {
   for (const auto& target_entry : data.GetTargetEntries()) {
     Write32(target_entry.target_id);
+  }
+  for (const auto& target_entry : data.GetTargetEntries()) {
     Write32(target_entry.overlay_id);
   }
 
-  static constexpr uint16_t kValueSize = 8U;
-  std::vector<std::pair<ConfigDescription, TargetValue>> target_values;
-  target_values.reserve(data.GetHeader()->GetTargetInlineEntryValueCount());
-  for (const auto& target_entry : data.GetTargetInlineEntries()) {
-    Write32(target_entry.target_id);
-    Write32(target_values.size());
-    Write32(target_entry.values.size());
-    target_values.insert(
-        target_values.end(), target_entry.values.begin(), target_entry.values.end());
+  uint32_t current_inline_entry_values_count = 0;
+  for (const auto& target_inline_entry : data.GetTargetInlineEntries()) {
+    Write32(target_inline_entry.target_id);
+  }
+  for (const auto& target_inline_entry : data.GetTargetInlineEntries()) {
+    Write32(current_inline_entry_values_count);
+    Write32(target_inline_entry.values.size());
+    current_inline_entry_values_count += target_inline_entry.values.size();
   }
 
   std::vector<ConfigDescription> configs;
   configs.reserve(data.GetHeader()->GetConfigCount());
-  for (const auto& target_entry_value : target_values) {
-    auto config_it = find(configs.begin(), configs.end(), target_entry_value.first);
-    if (config_it != configs.end()) {
-      Write32(config_it - configs.begin());
-    } else {
-      Write32(configs.size());
-      configs.push_back(target_entry_value.first);
+  for (const auto& target_entry : data.GetTargetInlineEntries()) {
+    for (const auto& target_entry_value : target_entry.values) {
+      auto config_it = std::find(configs.begin(), configs.end(), target_entry_value.first);
+      if (config_it != configs.end()) {
+        Write32(config_it - configs.begin());
+      } else {
+        Write32(configs.size());
+        configs.push_back(target_entry_value.first);
+      }
+      // We're writing a Res_value entry here, and the first 3 bytes of that are
+      // sizeof() + a padding 0 byte
+      static constexpr decltype(android::Res_value::size) kSize = sizeof(android::Res_value);
+      Write16(kSize);
+      Write8(0U);
+      Write8(target_entry_value.second.data_type);
+      Write32(target_entry_value.second.data_value);
     }
-    Write16(kValueSize);
-    Write8(0U);  // padding
-    Write8(target_entry_value.second.data_type);
-    Write32(target_entry_value.second.data_value);
   }
 
-  for( auto& cd : configs) {
-    cd.swapHtoD();
-    stream_.write(reinterpret_cast<char*>(&cd), sizeof(cd));
+  if (!configs.empty()) {
+    stream_.write(reinterpret_cast<const char*>(&configs.front()),
+                  sizeof(configs.front()) * configs.size());
+    if (configs.size() >= 100) {
+      // Let's write a message to future us so that they know when to replace the linear search
+      // in `configs` vector with something more efficient.
+      LOG(WARNING) << "Idmap got " << configs.size()
+                   << " configurations, time to fix the bruteforce search";
+    }
   }
 
   for (const auto& overlay_entry : data.GetOverlayEntries()) {
     Write32(overlay_entry.overlay_id);
+  }
+  for (const auto& overlay_entry : data.GetOverlayEntries()) {
     Write32(overlay_entry.target_id);
   }
 
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 12d9dd9..7680109 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -204,73 +204,91 @@
   }
 
   // Read the mapping of target resource id to overlay resource value.
+  data->target_entries_.resize(data->header_->GetTargetEntryCount());
   for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
-    TargetEntry target_entry{};
-    if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) {
+    if (!Read32(stream, &data->target_entries_[i].target_id)) {
       return nullptr;
     }
-    data->target_entries_.emplace_back(target_entry);
+  }
+  for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
+    if (!Read32(stream, &data->target_entries_[i].overlay_id)) {
+      return nullptr;
+    }
   }
 
   // Read the mapping of target resource id to inline overlay values.
-  std::vector<std::tuple<TargetInlineEntry, uint32_t, uint32_t>> target_inline_entries;
+  struct TargetInlineEntryHeader {
+    ResourceId target_id;
+    uint32_t values_offset;
+    uint32_t values_count;
+  };
+  std::vector<TargetInlineEntryHeader> target_inline_entries(
+      data->header_->GetTargetInlineEntryCount());
   for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
-    TargetInlineEntry target_entry{};
-    uint32_t entry_offset;
-    uint32_t entry_count;
-    if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &entry_offset)
-        || !Read32(stream, &entry_count)) {
+    if (!Read32(stream, &target_inline_entries[i].target_id)) {
       return nullptr;
     }
-    target_inline_entries.emplace_back(target_entry, entry_offset, entry_count);
+  }
+  for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
+    if (!Read32(stream, &target_inline_entries[i].values_offset) ||
+        !Read32(stream, &target_inline_entries[i].values_count)) {
+      return nullptr;
+    }
   }
 
   // Read the inline overlay resource values
-  std::vector<std::pair<uint32_t, TargetValue>> target_values;
-  uint8_t unused1;
-  uint16_t unused2;
-  for (size_t i = 0; i < data->header_->GetTargetInlineEntryValueCount(); i++) {
+  struct TargetValueHeader {
     uint32_t config_index;
-    if (!Read32(stream, &config_index)) {
+    DataType data_type;
+    DataValue data_value;
+  };
+  std::vector<TargetValueHeader> target_values(data->header_->GetTargetInlineEntryValueCount());
+  for (size_t i = 0; i < data->header_->GetTargetInlineEntryValueCount(); i++) {
+    auto& value = target_values[i];
+    if (!Read32(stream, &value.config_index)) {
       return nullptr;
     }
-    TargetValue value;
-    if (!Read16(stream, &unused2)
-        || !Read8(stream, &unused1)
-        || !Read8(stream, &value.data_type)
-        || !Read32(stream, &value.data_value)) {
+    // skip the padding
+    stream.seekg(3, std::ios::cur);
+    if (!Read8(stream, &value.data_type) || !Read32(stream, &value.data_value)) {
       return nullptr;
     }
-    target_values.emplace_back(config_index, value);
   }
 
   // Read the configurations
-  std::vector<ConfigDescription> configurations;
-  for (size_t i = 0; i < data->header_->GetConfigCount(); i++) {
-    ConfigDescription cd;
-    if (!stream.read(reinterpret_cast<char*>(&cd), sizeof(ConfigDescription))) {
+  std::vector<ConfigDescription> configurations(data->header_->GetConfigCount());
+  if (!configurations.empty()) {
+    if (!stream.read(reinterpret_cast<char*>(&configurations.front()),
+                     sizeof(configurations.front()) * configurations.size())) {
       return nullptr;
     }
-    configurations.emplace_back(cd);
   }
 
   // Construct complete target inline entries
-  for (auto [target_entry, entry_offset, entry_count] : target_inline_entries) {
-    for(size_t i = 0; i < entry_count; i++) {
-      const auto& target_value = target_values[entry_offset + i];
-      const auto& config = configurations[target_value.first];
-      target_entry.values[config] = target_value.second;
+  data->target_inline_entries_.reserve(target_inline_entries.size());
+  for (auto&& entry_header : target_inline_entries) {
+    TargetInlineEntry& entry = data->target_inline_entries_.emplace_back();
+    entry.target_id = entry_header.target_id;
+    for (size_t i = 0; i < entry_header.values_count; i++) {
+      const auto& value_header = target_values[entry_header.values_offset + i];
+      const auto& config = configurations[value_header.config_index];
+      auto& value = entry.values[config];
+      value.data_type = value_header.data_type;
+      value.data_value = value_header.data_value;
     }
-    data->target_inline_entries_.emplace_back(target_entry);
   }
 
   // Read the mapping of overlay resource id to target resource id.
+  data->overlay_entries_.resize(data->header_->GetOverlayEntryCount());
   for (size_t i = 0; i < data->header_->GetOverlayEntryCount(); i++) {
-    OverlayEntry overlay_entry{};
-    if (!Read32(stream, &overlay_entry.overlay_id) || !Read32(stream, &overlay_entry.target_id)) {
+    if (!Read32(stream, &data->overlay_entries_[i].overlay_id)) {
       return nullptr;
     }
-    data->overlay_entries_.emplace_back(overlay_entry);
+  }
+  for (size_t i = 0; i < data->header_->GetOverlayEntryCount(); i++) {
+    if (!Read32(stream, &data->overlay_entries_[i].target_id)) {
+      return nullptr;
+    }
   }
 
   // Read raw string pool bytes.
@@ -320,7 +338,7 @@
   std::unique_ptr<IdmapData> data(new IdmapData());
   data->string_pool_data_ = std::string(resource_mapping.GetStringPoolData());
   uint32_t inline_value_count = 0;
-  std::set<std::string> config_set;
+  std::set<std::string_view> config_set;
   for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) {
     if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) {
       data->target_entries_.push_back({mapping.first, *overlay_resource});
@@ -329,7 +347,9 @@
       for (const auto& [config, value] : std::get<ConfigMap>(mapping.second)) {
         config_set.insert(config);
         ConfigDescription cd;
-        ConfigDescription::Parse(config, &cd);
+        if (!ConfigDescription::Parse(config, &cd)) {
+          return Error("failed to parse configuration string '%s'", config.c_str());
+        }
         values[cd] = value;
         inline_value_count++;
       }
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index c85619c..1b656e8 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -68,7 +68,7 @@
   std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
   ASSERT_THAT(header, NotNull());
   ASSERT_EQ(header->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(header->GetVersion(), 0x09U);
+  ASSERT_EQ(header->GetVersion(), 10);
   ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -143,7 +143,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 10);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
@@ -204,7 +204,7 @@
 
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
-  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
+  ASSERT_EQ(idmap->GetHeader()->GetVersion(), 10);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
   ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 68164e2..7fae1c6 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -64,7 +64,7 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000009  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "0000000a  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(
       StringPrintf(ADDRESS "%s  target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
       stream.str());
@@ -113,7 +113,7 @@
   (*idmap)->accept(&visitor);
 
   ASSERT_CONTAINS_REGEX(ADDRESS "504d4449  magic\n", stream.str());
-  ASSERT_CONTAINS_REGEX(ADDRESS "00000009  version\n", stream.str());
+  ASSERT_CONTAINS_REGEX(ADDRESS "0000000a  version\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00001234  target crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00005678  overlay crc\n", stream.str());
   ASSERT_CONTAINS_REGEX(ADDRESS "00000011  fulfilled policies: public|signature\n", stream.str());
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index bf01c32..2b4ebd1 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -34,7 +34,7 @@
     0x49, 0x44, 0x4d, 0x50,
 
     // 0x4: version
-    0x09, 0x00, 0x00, 0x00,
+    0x0a, 0x00, 0x00, 0x00,
 
     // 0x8: target crc
     0x34, 0x12, 0x00, 0x00,
@@ -95,19 +95,15 @@
     // TARGET ENTRIES
     // 0x6c: target id (0x7f020000)
     0x00, 0x00, 0x02, 0x7f,
-
-    // 0x70: overlay_id (0x7f020000)
-    0x00, 0x00, 0x02, 0x7f,
-
-    // 0x74: target id (0x7f030000)
+    // 0x70: target id (0x7f030000)
     0x00, 0x00, 0x03, 0x7f,
-
-    // 0x78: overlay_id (0x7f030000)
-    0x00, 0x00, 0x03, 0x7f,
-
-    // 0x7c: target id (0x7f030002)
+    // 0x74: target id (0x7f030002)
     0x02, 0x00, 0x03, 0x7f,
 
+    // 0x78: overlay_id (0x7f020000)
+    0x00, 0x00, 0x02, 0x7f,
+    // 0x7c: overlay_id (0x7f030000)
+    0x00, 0x00, 0x03, 0x7f,
     // 0x80: overlay_id (0x7f030001)
     0x01, 0x00, 0x03, 0x7f,
 
@@ -178,16 +174,20 @@
     // 0xe1: padding
     0x00, 0x00, 0x00,
 
-
     // OVERLAY ENTRIES
-    // 0xe4: 0x7f020000 -> 0x7f020000
-    0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
+    // 0xe4: 0x7f020000 -> ...
+    0x00, 0x00, 0x02, 0x7f,
+    // 0xe8: 0x7f030000 -> ...
+    0x00, 0x00, 0x03, 0x7f,
+    // 0xec: 0x7f030001 -> ...
+    0x01, 0x00, 0x03, 0x7f,
 
-    // 0xec: 0x7f030000 -> 0x7f030000
-    0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
-
-    // 0xf4: 0x7f030001 -> 0x7f030002
-    0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
+    // 0xf0: ... -> 0x7f020000
+    0x00, 0x00, 0x02, 0x7f,
+    // 0xf4: ... -> 0x7f030000
+    0x00, 0x00, 0x03, 0x7f,
+    // 0xf8: ... -> 0x7f030002
+    0x02, 0x00, 0x03, 0x7f,
 
     // 0xfc: string pool
     // string length,
diff --git a/core/api/current.txt b/core/api/current.txt
index 565254f..ff20384 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -239,6 +239,7 @@
     field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
     field public static final String PROVIDE_OWN_AUTOFILL_SUGGESTIONS = "android.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS";
     field public static final String PROVIDE_REMOTE_CREDENTIALS = "android.permission.PROVIDE_REMOTE_CREDENTIALS";
+    field @FlaggedApi("android.security.aapm_api") public static final String QUERY_ADVANCED_PROTECTION_MODE = "android.permission.QUERY_ADVANCED_PROTECTION_MODE";
     field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
     field public static final String READ_ASSISTANT_APP_SEARCH_DATA = "android.permission.READ_ASSISTANT_APP_SEARCH_DATA";
     field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE";
@@ -4950,7 +4951,7 @@
     field @Deprecated @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOWED = 1; // 0x1
     field @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS = 3; // 0x3
     field @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE = 4; // 0x4
-    field @FlaggedApi("com.android.window.flags.bal_additional_start_modes") public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2; // 0x2
+    field public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2; // 0x2
     field public static final int MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED = 0; // 0x0
   }
 
@@ -5103,6 +5104,7 @@
     method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String);
     method @Nullable public static String permissionToOp(@NonNull String);
     method public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback);
+    method @FlaggedApi("android.permission.flags.sync_on_op_noted_api") public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback, int);
     method @Deprecated public int startOp(@NonNull String, int, @NonNull String);
     method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
     method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String);
@@ -5157,6 +5159,7 @@
     field public static final String OPSTR_WRITE_CONTACTS = "android:write_contacts";
     field public static final String OPSTR_WRITE_EXTERNAL_STORAGE = "android:write_external_storage";
     field public static final String OPSTR_WRITE_SETTINGS = "android:write_settings";
+    field @FlaggedApi("android.permission.flags.sync_on_op_noted_api") public static final int OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC = 1; // 0x1
     field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1
   }
 
@@ -8779,7 +8782,6 @@
 package android.app.appfunctions {
 
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
-    method @Deprecated @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
     method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
     method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
@@ -8791,8 +8793,7 @@
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
-    method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+    method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
 
@@ -9834,6 +9835,7 @@
   public final class AssociationInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.companion.AssociatedDevice getAssociatedDevice();
+    method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon();
     method @Nullable public android.net.MacAddress getDeviceMacAddress();
     method @Nullable public String getDeviceProfile();
     method @Nullable public CharSequence getDisplayName();
@@ -9847,6 +9849,7 @@
 
   public final class AssociationRequest implements android.os.Parcelable {
     method public int describeContents();
+    method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon();
     method @Nullable public String getDeviceProfile();
     method @Nullable public CharSequence getDisplayName();
     method public boolean isForceConfirmation();
@@ -9866,6 +9869,7 @@
     ctor public AssociationRequest.Builder();
     method @NonNull public android.companion.AssociationRequest.Builder addDeviceFilter(@Nullable android.companion.DeviceFilter<?>);
     method @NonNull public android.companion.AssociationRequest build();
+    method @FlaggedApi("android.companion.association_device_icon") @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setDeviceIcon(@NonNull android.graphics.drawable.Icon);
     method @NonNull public android.companion.AssociationRequest.Builder setDeviceProfile(@NonNull String);
     method @NonNull public android.companion.AssociationRequest.Builder setDisplayName(@NonNull CharSequence);
     method @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setForceConfirmation(boolean);
@@ -10798,6 +10802,7 @@
     field public static final String ACCESSIBILITY_SERVICE = "accessibility";
     field public static final String ACCOUNT_SERVICE = "account";
     field public static final String ACTIVITY_SERVICE = "activity";
+    field @FlaggedApi("android.security.aapm_api") public static final String ADVANCED_PROTECTION_SERVICE = "advanced_protection";
     field public static final String ALARM_SERVICE = "alarm";
     field public static final String APPWIDGET_SERVICE = "appwidget";
     field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String APP_FUNCTION_SERVICE = "app_function";
@@ -10859,6 +10864,7 @@
     field public static final String IPSEC_SERVICE = "ipsec";
     field public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
     field public static final String KEYGUARD_SERVICE = "keyguard";
+    field @FlaggedApi("android.security.keystore_grant_api") public static final String KEYSTORE_SERVICE = "keystore";
     field public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
     field @UiContext public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
     field public static final String LOCALE_SERVICE = "locale";
@@ -12286,6 +12292,7 @@
     method public int getMemtagMode();
     method public int getNativeHeapZeroInitialized();
     method public int getRequestRawExternalStorageAccess();
+    method @FlaggedApi("android.content.pm.audio_playback_capture_allowance") public boolean isAudioPlaybackCaptureAllowed();
     method public boolean isProfileable();
     method public boolean isProfileableByShell();
     method public boolean isResourceOverlay();
@@ -16097,6 +16104,7 @@
     enum_constant public static final android.graphics.ColorSpace.Named CIE_LAB;
     enum_constant public static final android.graphics.ColorSpace.Named CIE_XYZ;
     enum_constant public static final android.graphics.ColorSpace.Named DCI_P3;
+    enum_constant @FlaggedApi("com.android.graphics.flags.display_bt2020_colorspace") public static final android.graphics.ColorSpace.Named DISPLAY_BT2020;
     enum_constant public static final android.graphics.ColorSpace.Named DISPLAY_P3;
     enum_constant public static final android.graphics.ColorSpace.Named EXTENDED_SRGB;
     enum_constant public static final android.graphics.ColorSpace.Named LINEAR_EXTENDED_SRGB;
@@ -18613,6 +18621,7 @@
     field public static final int DATASPACE_BT709 = 281083904; // 0x10c10000
     field public static final int DATASPACE_DCI_P3 = 155844608; // 0x94a0000
     field public static final int DATASPACE_DEPTH = 4096; // 0x1000
+    field @FlaggedApi("com.android.graphics.flags.display_bt2020_colorspace") public static final int DATASPACE_DISPLAY_BT2020 = 142999552; // 0x8860000
     field public static final int DATASPACE_DISPLAY_P3 = 143261696; // 0x88a0000
     field public static final int DATASPACE_DYNAMIC_DEPTH = 4098; // 0x1002
     field public static final int DATASPACE_HEIF = 4100; // 0x1004
@@ -22601,6 +22610,7 @@
     method public void sendEvent(int, int, @Nullable byte[]) throws android.media.MediaCasException;
     method public void setEventListener(@Nullable android.media.MediaCas.EventListener, @Nullable android.os.Handler);
     method public void setPrivateData(@NonNull byte[]) throws android.media.MediaCasException;
+    method @FlaggedApi("com.android.media.flags.update_client_profile_priority") public boolean updateResourcePriority(int, int);
     field public static final int PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED = 0; // 0x0
     field public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = 1; // 0x1
     field public static final int SCRAMBLING_MODE_AES128 = 9; // 0x9
@@ -32774,6 +32784,8 @@
   public class Build {
     ctor public Build();
     method @NonNull public static java.util.List<android.os.Build.Partition> getFingerprintedPartitions();
+    method @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static int getMajorSdkVersion(int);
+    method @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static int getMinorSdkVersion(int);
     method public static String getRadioVersion();
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public static String getSerial();
     field public static final String BOARD;
@@ -32870,6 +32882,41 @@
   }
 
   @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL {
+    field public static final int BASE = 100000; // 0x186a0
+    field public static final int BASE_1_1 = 200000; // 0x30d40
+    field public static final int CUPCAKE = 300000; // 0x493e0
+    field public static final int DONUT = 400000; // 0x61a80
+    field public static final int ECLAIR = 500000; // 0x7a120
+    field public static final int ECLAIR_0_1 = 600000; // 0x927c0
+    field public static final int ECLAIR_MR1 = 700000; // 0xaae60
+    field public static final int FROYO = 800000; // 0xc3500
+    field public static final int GINGERBREAD = 900000; // 0xdbba0
+    field public static final int GINGERBREAD_MR1 = 1000000; // 0xf4240
+    field public static final int HONEYCOMB = 1100000; // 0x10c8e0
+    field public static final int HONEYCOMB_MR1 = 1200000; // 0x124f80
+    field public static final int HONEYCOMB_MR2 = 1300000; // 0x13d620
+    field public static final int ICE_CREAM_SANDWICH = 1400000; // 0x155cc0
+    field public static final int ICE_CREAM_SANDWICH_MR1 = 1500000; // 0x16e360
+    field public static final int JELLY_BEAN = 1600000; // 0x186a00
+    field public static final int JELLY_BEAN_MR1 = 1700000; // 0x19f0a0
+    field public static final int JELLY_BEAN_MR2 = 1800000; // 0x1b7740
+    field public static final int KITKAT = 1900000; // 0x1cfde0
+    field public static final int KITKAT_WATCH = 2000000; // 0x1e8480
+    field public static final int LOLLIPOP = 2100000; // 0x200b20
+    field public static final int LOLLIPOP_MR1 = 2200000; // 0x2191c0
+    field public static final int M = 2300000; // 0x231860
+    field public static final int N = 2400000; // 0x249f00
+    field public static final int N_MR1 = 2500000; // 0x2625a0
+    field public static final int O = 2600000; // 0x27ac40
+    field public static final int O_MR1 = 2700000; // 0x2932e0
+    field public static final int P = 2800000; // 0x2ab980
+    field public static final int Q = 2900000; // 0x2c4020
+    field public static final int R = 3000000; // 0x2dc6c0
+    field public static final int S = 3100000; // 0x2f4d60
+    field public static final int S_V2 = 3200000; // 0x30d400
+    field public static final int TIRAMISU = 3300000; // 0x325aa0
+    field public static final int UPSIDE_DOWN_CAKE = 3400000; // 0x33e140
+    field public static final int VANILLA_ICE_CREAM = 3500000; // 0x3567e0
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -33923,9 +33970,12 @@
   public class RemoteCallbackList<E extends android.os.IInterface> {
     ctor public RemoteCallbackList();
     method public int beginBroadcast();
+    method @FlaggedApi("android.os.binder_frozen_state_change_callback") public void broadcast(@NonNull java.util.function.Consumer<E>);
     method public void finishBroadcast();
     method public Object getBroadcastCookie(int);
     method public E getBroadcastItem(int);
+    method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy();
+    method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize();
     method public Object getRegisteredCallbackCookie(int);
     method public int getRegisteredCallbackCount();
     method public E getRegisteredCallbackItem(int);
@@ -33935,6 +33985,16 @@
     method public boolean register(E);
     method public boolean register(E, Object);
     method public boolean unregister(E);
+    field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_DROP = 3; // 0x3
+    field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1; // 0x1
+    field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2; // 0x2
+    field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_UNSET = 0; // 0x0
+  }
+
+  @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder<E extends android.os.IInterface> {
+    ctor public RemoteCallbackList.Builder(int);
+    method @NonNull public android.os.RemoteCallbackList<E> build();
+    method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int);
   }
 
   public class RemoteException extends android.util.AndroidException {
@@ -34334,6 +34394,7 @@
     method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public boolean areEnvelopeEffectsSupported();
     method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
     method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
     method public int getId();
     method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectControlPointDurationMillis();
     method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectDurationMillis();
@@ -34687,6 +34748,19 @@
 
 }
 
+package android.os.vibrator {
+
+  @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public final class VibratorFrequencyProfile {
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.util.SparseArray<java.lang.Float> getFrequenciesOutputAcceleration();
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.util.Range<java.lang.Float> getFrequencyRange(float);
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMaxFrequencyHz();
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMaxOutputAccelerationGs();
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMinFrequencyHz();
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getOutputAccelerationGs(float);
+  }
+
+}
+
 package android.preference {
 
   @Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -39662,6 +39736,20 @@
 
 }
 
+package android.security.advancedprotection {
+
+  @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager {
+    method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public boolean isAdvancedProtectionEnabled();
+    method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void registerAdvancedProtectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback);
+    method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void unregisterAdvancedProtectionCallback(@NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback);
+  }
+
+  @FlaggedApi("android.security.aapm_api") public static interface AdvancedProtectionManager.Callback {
+    method public void onAdvancedProtectionChanged(boolean);
+  }
+
+}
+
 package android.security.identity {
 
   public class AccessControlProfile {
@@ -40079,6 +40167,14 @@
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserPresenceRequired(boolean);
   }
 
+  @FlaggedApi("android.security.keystore_grant_api") public class KeyStoreManager {
+    method @NonNull public java.util.List<java.security.cert.X509Certificate> getGrantedCertificateChainFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
+    method @NonNull public java.security.Key getGrantedKeyFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
+    method @NonNull public java.security.KeyPair getGrantedKeyPairFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException;
+    method public long grantKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
+    method public void revokeKeyAccess(@NonNull String, int) throws android.security.KeyStoreException, java.security.UnrecoverableKeyException;
+  }
+
   public class SecureKeyImportUnavailableException extends java.security.ProviderException {
     ctor public SecureKeyImportUnavailableException();
     ctor public SecureKeyImportUnavailableException(String);
@@ -43814,8 +43910,8 @@
     field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
     field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
     field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
-    field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool";
-    field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool";
+    field public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool";
+    field public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool";
     field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
     field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
@@ -43898,7 +43994,7 @@
     field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
     field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
-    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
+    field public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
     field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
     field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
     field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
@@ -45874,6 +45970,7 @@
     method public byte[] getPdu();
     method public int getProtocolIdentifier();
     method public String getPseudoSubject();
+    method @FlaggedApi("com.android.internal.telephony.flags.support_sms_over_ims_apis") @Nullable public String getRecipientAddress();
     method public String getServiceCenterAddress();
     method public int getStatus();
     method public int getStatusOnIcc();
@@ -45935,7 +46032,7 @@
     method @Nullable public String getMncString();
     method @Deprecated public String getNumber();
     method public int getPortIndex();
-    method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities();
+    method @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities();
     method public int getSimSlotIndex();
     method public int getSubscriptionId();
     method public int getSubscriptionType();
@@ -46018,9 +46115,9 @@
     field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2
     field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3
     field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
-    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3
-    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2
-    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1
+    field public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3
+    field public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2
+    field public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1
     field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
     field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
     field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
@@ -46269,8 +46366,8 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabledForReason(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled();
-    method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceSmsCapable();
-    method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceVoiceCapable();
+    method public boolean isDeviceSmsCapable();
+    method public boolean isDeviceVoiceCapable();
     method public boolean isEmergencyNumber(@NonNull String);
     method public boolean isHearingAidCompatibilitySupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
@@ -46329,7 +46426,7 @@
     field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
     field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
-    field @FlaggedApi("com.android.internal.telephony.flags.reset_mobile_network_settings") public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
+    field public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
     field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
     field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
     field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
@@ -50909,6 +51006,7 @@
     method public android.view.Display.Mode[] getSupportedModes();
     method @Deprecated public float[] getSupportedRefreshRates();
     method @Deprecated public int getWidth();
+    method @FlaggedApi("com.android.server.display.feature.flags.enable_has_arr_support") public boolean hasArrSupport();
     method public boolean isHdr();
     method public boolean isHdrSdrRatioAvailable();
     method public boolean isMinimalPostProcessingSupported();
@@ -52142,7 +52240,7 @@
     method public void clear();
     method public void copyFrom(android.view.MotionEvent.PointerCoords);
     method public float getAxisValue(int);
-    method @FlaggedApi("com.android.hardware.input.pointer_coords_is_resampled_api") public boolean isResampled();
+    method public boolean isResampled();
     method public void setAxisValue(int, float);
     field public float orientation;
     field public float pressure;
@@ -52559,10 +52657,12 @@
     ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int);
     ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
     method public void applyTransactionToFrame(@NonNull android.view.SurfaceControl.Transaction);
+    method @FlaggedApi("android.view.flags.surface_view_set_composition_order") public int getCompositionOrder();
     method public android.view.SurfaceHolder getHolder();
     method @Deprecated @Nullable public android.os.IBinder getHostToken();
     method public android.view.SurfaceControl getSurfaceControl();
     method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
+    method @FlaggedApi("android.view.flags.surface_view_set_composition_order") public void setCompositionOrder(int);
     method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
     method public void setSecure(boolean);
     method public void setSurfaceLifecycle(int);
@@ -54900,6 +55000,7 @@
     method public void setPackageName(CharSequence);
     method public void setSpeechStateChangeTypes(int);
     method public void writeToParcel(android.os.Parcel, int);
+    field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CONTENT_CHANGE_TYPE_CHECKED = 8192; // 0x2000
     field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
     field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
     field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
@@ -55045,6 +55146,7 @@
     method @Deprecated public void getBoundsInParent(android.graphics.Rect);
     method public void getBoundsInScreen(android.graphics.Rect);
     method public void getBoundsInWindow(@NonNull android.graphics.Rect);
+    method @FlaggedApi("android.view.accessibility.tri_state_checked") public int getChecked();
     method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
     method @Nullable public android.view.accessibility.AccessibilityNodeInfo getChild(int, int);
     method public int getChildCount();
@@ -55087,7 +55189,7 @@
     method public boolean isAccessibilityDataSensitive();
     method public boolean isAccessibilityFocused();
     method public boolean isCheckable();
-    method public boolean isChecked();
+    method @Deprecated @FlaggedApi("android.view.accessibility.tri_state_checked") public boolean isChecked();
     method public boolean isClickable();
     method public boolean isContentInvalid();
     method public boolean isContextClickable();
@@ -55132,7 +55234,8 @@
     method public void setBoundsInWindow(@NonNull android.graphics.Rect);
     method public void setCanOpenPopup(boolean);
     method public void setCheckable(boolean);
-    method public void setChecked(boolean);
+    method @Deprecated @FlaggedApi("android.view.accessibility.tri_state_checked") public void setChecked(boolean);
+    method @FlaggedApi("android.view.accessibility.tri_state_checked") public void setChecked(int);
     method public void setClassName(CharSequence);
     method public void setClickable(boolean);
     method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo);
@@ -55228,6 +55331,9 @@
     field public static final int ACTION_SELECT = 4; // 0x4
     field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
     field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_FALSE = 0; // 0x0
+    field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_PARTIAL = 2; // 0x2
+    field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_TRUE = 1; // 0x1
     field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
     field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
@@ -56554,7 +56660,7 @@
     method public java.util.Map<android.view.inputmethod.InputMethodInfo,java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes();
     method @Deprecated public void hideSoftInputFromInputMethod(android.os.IBinder, int);
     method public boolean hideSoftInputFromWindow(android.os.IBinder, int);
-    method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
+    method @Deprecated public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
     method @Deprecated public void hideStatusIcon(android.os.IBinder);
     method public void invalidateInput(@NonNull android.view.View);
     method public boolean isAcceptingText();
@@ -56578,7 +56684,7 @@
     method public void showInputMethodAndSubtypeEnabler(@Nullable String);
     method public void showInputMethodPicker();
     method public boolean showSoftInput(android.view.View, int);
-    method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
+    method @Deprecated public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
     method @Deprecated public void showSoftInputFromInputMethod(android.os.IBinder, int);
     method @Deprecated public void showStatusIcon(android.os.IBinder, String, @DrawableRes int);
     method @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public void startConnectionlessStylusHandwriting(@NonNull android.view.View, @Nullable android.view.inputmethod.CursorAnchorInfo, @NonNull java.util.concurrent.Executor, @NonNull android.view.inputmethod.ConnectionlessHandwritingCallback);
@@ -58084,12 +58190,16 @@
     method public abstract String[] getAcceptTypes();
     method @Nullable public abstract String getFilenameHint();
     method public abstract int getMode();
+    method @FlaggedApi("android.webkit.file_system_access") public int getPermissionMode();
     method @Nullable public abstract CharSequence getTitle();
     method public abstract boolean isCaptureEnabled();
     method @Nullable public static android.net.Uri[] parseResult(int, android.content.Intent);
     field public static final int MODE_OPEN = 0; // 0x0
+    field @FlaggedApi("android.webkit.file_system_access") public static final int MODE_OPEN_FOLDER = 2; // 0x2
     field public static final int MODE_OPEN_MULTIPLE = 1; // 0x1
     field public static final int MODE_SAVE = 3; // 0x3
+    field @FlaggedApi("android.webkit.file_system_access") public static final int PERMISSION_MODE_READ = 0; // 0x0
+    field @FlaggedApi("android.webkit.file_system_access") public static final int PERMISSION_MODE_READ_WRITE = 1; // 0x1
   }
 
   public abstract class WebHistoryItem implements java.lang.Cloneable {
@@ -58446,7 +58556,7 @@
     method public void setWebViewRenderProcessClient(@Nullable android.webkit.WebViewRenderProcessClient);
     method @Deprecated public boolean shouldDelayChildPressedState();
     method @Deprecated public boolean showFindDialog(@Nullable String, boolean);
-    method public static void startSafeBrowsing(@NonNull android.content.Context, @Nullable android.webkit.ValueCallback<java.lang.Boolean>);
+    method @Deprecated @FlaggedApi("android.webkit.deprecate_start_safe_browsing") public static void startSafeBrowsing(@NonNull android.content.Context, @Nullable android.webkit.ValueCallback<java.lang.Boolean>);
     method public void stopLoading();
     method public void zoomBy(float);
     method public boolean zoomIn();
@@ -61641,6 +61751,8 @@
 
   public final class BackEvent {
     ctor public BackEvent(float, float, float, int);
+    ctor @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public BackEvent(float, float, float, int, long);
+    method @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public long getFrameTime();
     method @FloatRange(from=0, to=1) public float getProgress();
     method public int getSwipeEdge();
     method public float getTouchX();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 287e787..4d1a423 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -321,7 +321,7 @@
 package android.net.wifi {
 
   public final class WifiMigration {
-    method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static int migrateLegacyKeystoreToWifiBlobstore();
+    method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static void migrateLegacyKeystoreToWifiBlobstore(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     field @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static final int KEYSTORE_MIGRATION_FAILURE_ENCOUNTERED_EXCEPTION = 2; // 0x2
     field @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static final int KEYSTORE_MIGRATION_SUCCESS_MIGRATION_COMPLETE = 0; // 0x0
     field @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration_read_only") public static final int KEYSTORE_MIGRATION_SUCCESS_MIGRATION_NOT_NEEDED = 1; // 0x1
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 3c7c0d6..a3cab29 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -82,12 +82,12 @@
 package android.graphics {
 
   @Deprecated public class AvoidXfermode extends android.graphics.Xfermode {
-    ctor public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode);
+    ctor @Deprecated public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode);
   }
 
-  public enum AvoidXfermode.Mode {
-    enum_constant public static final android.graphics.AvoidXfermode.Mode AVOID;
-    enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET;
+  @Deprecated public enum AvoidXfermode.Mode {
+    enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode AVOID;
+    enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode TARGET;
   }
 
   public class Canvas {
@@ -102,9 +102,9 @@
   }
 
   @Deprecated public class LayerRasterizer extends android.graphics.Rasterizer {
-    ctor public LayerRasterizer();
-    method public void addLayer(android.graphics.Paint, float, float);
-    method public void addLayer(android.graphics.Paint);
+    ctor @Deprecated public LayerRasterizer();
+    method @Deprecated public void addLayer(android.graphics.Paint, float, float);
+    method @Deprecated public void addLayer(android.graphics.Paint);
   }
 
   public class Paint {
@@ -118,7 +118,7 @@
   }
 
   @Deprecated public class PixelXorXfermode extends android.graphics.Xfermode {
-    ctor public PixelXorXfermode(int);
+    ctor @Deprecated public PixelXorXfermode(int);
   }
 
   public class Rasterizer {
@@ -170,14 +170,14 @@
 package android.net {
 
   @Deprecated public class NetworkBadging {
-    method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
-    field public static final int BADGING_4K = 30; // 0x1e
-    field public static final int BADGING_HD = 20; // 0x14
-    field public static final int BADGING_NONE = 0; // 0x0
-    field public static final int BADGING_SD = 10; // 0xa
+    method @Deprecated @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
+    field @Deprecated public static final int BADGING_4K = 30; // 0x1e
+    field @Deprecated public static final int BADGING_HD = 20; // 0x14
+    field @Deprecated public static final int BADGING_NONE = 0; // 0x0
+    field @Deprecated public static final int BADGING_SD = 10; // 0xa
   }
 
-  @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging {
+  @Deprecated @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging {
   }
 
   public final class Proxy {
@@ -304,14 +304,14 @@
 
   @Deprecated public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
     field @Deprecated public static final String CONTENT_DIRECTORY = "stream_items";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
     field @Deprecated public static final String PHOTO = "photo";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated protected static interface ContactsContract.StreamItemPhotosColumns {
@@ -332,16 +332,16 @@
     field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item";
     field @Deprecated public static final android.net.Uri CONTENT_URI;
     field @Deprecated public static final String MAX_ITEMS = "max_items";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
     field @Deprecated public static final String CONTENT_DIRECTORY = "photo";
     field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo";
     field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo";
-    field public static final String _COUNT = "_count";
-    field public static final String _ID = "_id";
+    field @Deprecated public static final String _COUNT = "_count";
+    field @Deprecated public static final String _ID = "_id";
   }
 
   @Deprecated protected static interface ContactsContract.StreamItemsColumns {
@@ -447,14 +447,14 @@
 package android.util {
 
   @Deprecated public class FloatMath {
-    method public static float ceil(float);
-    method public static float cos(float);
-    method public static float exp(float);
-    method public static float floor(float);
-    method public static float hypot(float, float);
-    method public static float pow(float, float);
-    method public static float sin(float);
-    method public static float sqrt(float);
+    method @Deprecated public static float ceil(float);
+    method @Deprecated public static float cos(float);
+    method @Deprecated public static float exp(float);
+    method @Deprecated public static float floor(float);
+    method @Deprecated public static float hypot(float, float);
+    method @Deprecated public static float pow(float, float);
+    method @Deprecated public static float sin(float);
+    method @Deprecated public static float sqrt(float);
   }
 
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8edfc21..207f4b5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -362,6 +362,7 @@
     field public static final String SERIAL_PORT = "android.permission.SERIAL_PORT";
     field @FlaggedApi("android.security.fsverity_api") public static final String SETUP_FSVERITY = "android.permission.SETUP_FSVERITY";
     field public static final String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
+    field @FlaggedApi("android.security.aapm_api") public static final String SET_ADVANCED_PROTECTION_MODE = "android.permission.SET_ADVANCED_PROTECTION_MODE";
     field public static final String SET_CLIP_SOURCE = "android.permission.SET_CLIP_SOURCE";
     field public static final String SET_DEFAULT_ACCOUNT_FOR_CONTACTS = "android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS";
     field public static final String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS";
@@ -3489,41 +3490,41 @@
 
   public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
     method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
-    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
-    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
+    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void addActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
     method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+    method public void close();
     method @NonNull public android.content.Context createContext();
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
-    method @FlaggedApi("android.companion.virtual.flags.virtual_camera") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.camera.VirtualCamera createVirtualCamera(@NonNull android.companion.virtual.camera.VirtualCameraConfig);
-    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
-    method @Nullable @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig);
-    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.input.VirtualMouseConfig);
-    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig);
-    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig);
-    method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
-    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+    method @NonNull public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
+    method @FlaggedApi("android.companion.virtual.flags.virtual_camera") @NonNull public android.companion.virtual.camera.VirtualCamera createVirtualCamera(@NonNull android.companion.virtual.camera.VirtualCameraConfig);
+    method @Deprecated @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @NonNull public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig);
+    method @NonNull public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig);
+    method @Deprecated @NonNull public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+    method @NonNull public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.input.VirtualMouseConfig);
+    method @Deprecated @NonNull public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+    method @NonNull public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig);
+    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig);
+    method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
+    method @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
+    method @Deprecated @NonNull public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method public int getDeviceId();
     method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public String getPersistentDeviceId();
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public java.util.List<android.companion.virtual.sensor.VirtualSensor> getVirtualSensorList();
-    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void goToSleep();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
+    method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensor> getVirtualSensorList();
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void goToSleep();
+    method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+    method public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
     method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
-    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
-    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
+    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void removeActivityPolicyExemption(@NonNull android.companion.virtual.ActivityPolicyExemption);
     method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
-    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
-    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int, int);
-    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDisplayImePolicy(int, int);
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
-    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void wakeUp();
+    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public void setDevicePolicy(int, int);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public void setDevicePolicy(int, int, int);
+    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public void setDisplayImePolicy(int, int);
+    method public void setShowPointerIcon(boolean);
+    method public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void wakeUp();
   }
 
   public final class VirtualDeviceParams implements android.os.Parcelable {
@@ -3632,7 +3633,7 @@
 package android.companion.virtual.camera {
 
   @FlaggedApi("android.companion.virtual.flags.virtual_camera") public final class VirtualCamera implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+    method public void close();
     method @NonNull public android.companion.virtual.camera.VirtualCameraConfig getConfig();
   }
 
@@ -3684,7 +3685,7 @@
     method public int getDeviceId();
     method @NonNull public String getName();
     method public int getType();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
+    method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR;
   }
@@ -4170,9 +4171,11 @@
   }
 
   public class PackageInstaller {
+    method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final int getVerificationPolicy();
     method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
     method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
+    method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final boolean setVerificationPolicy(int);
     field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
     field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
     field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -4183,12 +4186,20 @@
     field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_DELETE_FLAGS = "android.content.pm.extra.DELETE_FLAGS";
     field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
     field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
+    field @FlaggedApi("android.content.pm.verification_service") public static final String EXTRA_VERIFICATION_FAILURE_REASON = "android.content.pm.extra.VERIFICATION_FAILURE_REASON";
     field public static final int LOCATION_DATA_APP = 0; // 0x0
     field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
     field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
     field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0
     field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1
     field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1; // 0x1
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2; // 0x2
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0; // 0x0
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3; // 0x3
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1; // 0x1
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2; // 0x2
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_NONE = 0; // 0x0
   }
 
   public static class PackageInstaller.InstallInfo {
@@ -4635,12 +4646,13 @@
     method @NonNull public android.content.pm.SigningInfo getSigningInfo();
     method @NonNull public android.net.Uri getStagedPackageUri();
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime();
+    method public int getVerificationPolicy();
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int);
+    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR;
-    field public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2; // 0x2
     field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1
     field public static final int VERIFICATION_INCOMPLETE_UNKNOWN = 0; // 0x0
   }
@@ -5695,8 +5707,8 @@
 package android.hardware.input {
 
   public class VirtualDpad implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent);
+    method public void close();
+    method public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent);
   }
 
   public final class VirtualDpadConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
@@ -5747,8 +5759,8 @@
   }
 
   public class VirtualKeyboard implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent);
+    method public void close();
+    method public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent);
   }
 
   public final class VirtualKeyboardConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
@@ -5769,11 +5781,11 @@
   }
 
   public class VirtualMouse implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
-    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.graphics.PointF getCursorPosition();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent);
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent);
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent);
+    method public void close();
+    method @NonNull public android.graphics.PointF getCursorPosition();
+    method public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent);
+    method public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent);
+    method public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent);
   }
 
   public final class VirtualMouseButtonEvent implements android.os.Parcelable {
@@ -5846,8 +5858,8 @@
   }
 
   public class VirtualNavigationTouchpad implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent);
+    method public void close();
+    method public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent);
   }
 
   public final class VirtualNavigationTouchpadConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
@@ -5864,8 +5876,8 @@
   }
 
   @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public class VirtualRotaryEncoder implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualRotaryEncoderScrollEvent);
+    method public void close();
+    method public void sendScrollEvent(@NonNull android.hardware.input.VirtualRotaryEncoderScrollEvent);
   }
 
   @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public final class VirtualRotaryEncoderConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
@@ -5895,9 +5907,9 @@
   }
 
   @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public class VirtualStylus implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent);
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendMotionEvent(@NonNull android.hardware.input.VirtualStylusMotionEvent);
+    method public void close();
+    method public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent);
+    method public void sendMotionEvent(@NonNull android.hardware.input.VirtualStylusMotionEvent);
   }
 
   @FlaggedApi("android.companion.virtual.flags.virtual_stylus") public final class VirtualStylusButtonEvent implements android.os.Parcelable {
@@ -6000,8 +6012,8 @@
   }
 
   public class VirtualTouchscreen implements java.io.Closeable {
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
-    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent);
+    method public void close();
+    method public void sendTouchEvent(@NonNull android.hardware.input.VirtualTouchEvent);
   }
 
   public final class VirtualTouchscreenConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
@@ -7319,6 +7331,7 @@
     field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8
     field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10
     field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1
+    field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_PORT_VOLUME = 64; // 0x40
     field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_MUTED = 4; // 0x4
     field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_VOLUME = 2; // 0x2
     field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_VOLUME_SHAPER = 32; // 0x20
@@ -12321,6 +12334,14 @@
 
 }
 
+package android.security.advancedprotection {
+
+  @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager {
+    method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean);
+  }
+
+}
+
 package android.security.keystore {
 
   public class AndroidKeyStoreProvider extends java.security.Provider {
@@ -13104,7 +13125,7 @@
     method public final void adjustNotification(@NonNull android.service.notification.Adjustment);
     method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>);
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
-    method @Deprecated public void onAllowedAdjustmentsChanged();
+    method public void onAllowedAdjustmentsChanged();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method public void onNotificationClicked(@NonNull String);
     method public void onNotificationDirectReplied(@NonNull String);
@@ -15426,6 +15447,7 @@
     field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7
     field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22
     field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40; // 0x28
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
     field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
     field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24
@@ -15463,6 +15485,12 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
   }
 
+  @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static interface TelephonyCallback.EmergencyCallbackModeListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeRestarted(int, @NonNull java.time.Duration, int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeStarted(int, @NonNull java.time.Duration, int);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeStopped(int, int, int);
+  }
+
   public static interface TelephonyCallback.LinkCapacityEstimateChangedListener {
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List<android.telephony.LinkCapacityEstimate>);
   }
@@ -15724,6 +15752,8 @@
     field public static final int CELL_BROADCAST_RESULT_SUCCESS = 0; // 0x0
     field public static final int CELL_BROADCAST_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int CELL_BROADCAST_RESULT_UNSUPPORTED = 1; // 0x1
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int EMERGENCY_CALLBACK_MODE_CALL = 1; // 0x1
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int EMERGENCY_CALLBACK_MODE_SMS = 2; // 0x2
     field public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; // 0x4
     field public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; // 0x1
     field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; // 0x3
@@ -15781,6 +15811,13 @@
     field public static final int SRVCC_STATE_HANDOVER_FAILED = 2; // 0x2
     field public static final int SRVCC_STATE_HANDOVER_NONE = -1; // 0xffffffff
     field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4; // 0x4
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_NORMAL_SMS_SENT = 2; // 0x2
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3; // 0x3
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1; // 0x1
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_TIMER_EXPIRED = 5; // 0x5
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_UNKNOWN = 0; // 0x0
+    field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_USER_ACTION = 6; // 0x6
     field public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; // 0x3
     field public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; // 0x1
     field public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; // 0x2
@@ -18559,6 +18596,7 @@
     method @Deprecated public abstract void setUserAgent(int);
     method public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean);
     field public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; // 0xcccb1e0L
+    field @FlaggedApi("android.webkit.user_agent_reduction") public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L; // 0x161d88bfL
   }
 
   public class WebStorage {
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index bbfa0ec..78b9994 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -7,8 +7,8 @@
   }
 
   @Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback {
-    ctor public AppOpsManager.AppOpsCollector();
-    method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
+    ctor @Deprecated public AppOpsManager.AppOpsCollector();
+    method @Deprecated @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
   }
 
   public class Notification implements android.os.Parcelable {
@@ -207,7 +207,7 @@
 
   @Deprecated public static interface TranslationService.OnTranslationResultCallback {
     method @Deprecated public void onError();
-    method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
+    method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
   }
 
 }
@@ -261,64 +261,64 @@
   }
 
   @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable {
-    method public boolean containsKey(@NonNull String);
-    method @NonNull public android.os.PersistableBundle copyBundle();
-    method public int describeContents();
-    method public boolean getBoolean(@NonNull String, boolean);
-    method public int getInt(@NonNull String, int);
-    method @Nullable public String getString(@NonNull String);
-    method public long getVersion();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
-    field public static final String IPTYPE_IPV4 = "IPV4";
-    field public static final String IPTYPE_IPV6 = "IPV6";
-    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
-    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
-    field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
-    field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
-    field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
-    field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
-    field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
-    field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
-    field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
-    field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
-    field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
-    field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
-    field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
-    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
-    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
-    field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
-    field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
-    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
-    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
-    field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
-    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
-    field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
-    field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
-    field public static final String SIP_TRANSPORT_TCP = "TCP";
-    field public static final String SIP_TRANSPORT_UDP = "UDP";
+    method @Deprecated public boolean containsKey(@NonNull String);
+    method @Deprecated @NonNull public android.os.PersistableBundle copyBundle();
+    method @Deprecated public int describeContents();
+    method @Deprecated public boolean getBoolean(@NonNull String, boolean);
+    method @Deprecated public int getInt(@NonNull String, int);
+    method @Deprecated @Nullable public String getString(@NonNull String);
+    method @Deprecated public long getVersion();
+    method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+    field @Deprecated public static final String IPTYPE_IPV4 = "IPV4";
+    field @Deprecated public static final String IPTYPE_IPV6 = "IPV6";
+    field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+    field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+    field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
+    field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP";
+    field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP";
   }
 
-  public static final class SipDelegateImsConfiguration.Builder {
-    ctor public SipDelegateImsConfiguration.Builder(int);
-    ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
-    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+  @Deprecated public static final class SipDelegateImsConfiguration.Builder {
+    ctor @Deprecated public SipDelegateImsConfiguration.Builder(int);
+    ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
   }
 
 }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 76e9ca0..1ff8c51 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -393,7 +393,9 @@
 
   public class NotificationManager {
     method @FlaggedApi("android.app.modes_api") @NonNull public String addAutomaticZenRule(@NonNull android.app.AutomaticZenRule, boolean);
+    method @FlaggedApi("android.service.notification.notification_classification") public void allowAssistantAdjustment(@NonNull String);
     method public void cleanUpCallersAfter(long);
+    method @FlaggedApi("android.service.notification.notification_classification") public void disallowAssistantAdjustment(@NonNull String);
     method @FlaggedApi("android.app.modes_api") @NonNull public android.service.notification.ZenPolicy getDefaultZenPolicy();
     method public android.content.ComponentName getEffectsSuppressor();
     method @FlaggedApi("android.service.notification.notification_classification") @NonNull public java.util.Set<java.lang.String> getUnsupportedAdjustmentTypes();
@@ -914,6 +916,7 @@
     ctor public AssociationInfo.Builder(@NonNull android.companion.AssociationInfo);
     method @NonNull public android.companion.AssociationInfo build();
     method @NonNull public android.companion.AssociationInfo.Builder setAssociatedDevice(@Nullable android.companion.AssociatedDevice);
+    method @FlaggedApi("android.companion.association_device_icon") @NonNull public android.companion.AssociationInfo.Builder setDeviceIcon(@Nullable android.graphics.drawable.Icon);
     method @NonNull public android.companion.AssociationInfo.Builder setDeviceMacAddress(@Nullable android.net.MacAddress);
     method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String);
     method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence);
@@ -1098,6 +1101,7 @@
     field public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L; // 0xfd27b38L
     field public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L; // 0xf4156bcL
     field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
+    field public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // 0x15498ba7L
   }
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 1265de1..d12e1bf 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -19,6 +19,7 @@
     srcs: [
         "**/*.java",
         "**/*.aidl",
+        ":systemfeatures-gen-srcs",
         ":framework-nfc-non-updatable-sources",
         ":messagequeue-gen",
         ":ranging_stack_mock_initializer",
@@ -214,6 +215,9 @@
 
 aidl_interface {
     name: "android.os.hintmanager_aidl",
+    defaults: [
+        "android.hardware.power-aidl",
+    ],
     srcs: [
         "android/os/IHintManager.aidl",
         "android/os/IHintSession.aidl",
@@ -231,9 +235,6 @@
             enabled: true,
         },
     },
-    imports: [
-        "android.hardware.power-V5",
-    ],
 }
 
 aidl_library {
@@ -665,3 +666,29 @@
 }
 
 // protolog end
+
+// Whether to enable read-only system feature codegen.
+gen_readonly_feature_apis = select(release_flag("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS"), {
+    true: "true",
+    false: "false",
+    default: "false",
+})
+
+// Generates com.android.internal.pm.RoSystemFeatures, optionally compiling in
+// details about fixed system features defined by build flags. When disabled,
+// the APIs are simply passthrough stubs with no meaningful side effects.
+genrule {
+    name: "systemfeatures-gen-srcs",
+    cmd: "$(location systemfeatures-gen-tool) com.android.internal.pm.RoSystemFeatures " +
+        // --readonly=false (default) makes the codegen an effective no-op passthrough API.
+        " --readonly=" + gen_readonly_feature_apis +
+        // For now, only export "android.hardware.type.*" system features APIs.
+        // TODO(b/203143243): Use an intermediate soong var that aggregates all declared
+        // RELEASE_SYSTEM_FEATURE_* declarations into a single arg.
+        " --feature-apis=AUTOMOTIVE,WATCH,TELEVISION,EMBEDDED,PC" +
+        " > $(out)",
+    out: [
+        "RoSystemFeatures.java",
+    ],
+    tools: ["systemfeatures-gen-tool"],
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 8bb2857..5bc7de9 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -84,19 +84,25 @@
  * @attr ref android.R.styleable#AccessibilityService_accessibilityEventTypes
  * @attr ref android.R.styleable#AccessibilityService_accessibilityFeedbackType
  * @attr ref android.R.styleable#AccessibilityService_accessibilityFlags
+ * @attr ref android.R.styleable#AccessibilityService_animatedImageDrawable
+ * @attr ref android.R.styleable#AccessibilityService_canControlMagnification
+ * @attr ref android.R.styleable#AccessibilityService_canPerformGestures
  * @attr ref android.R.styleable#AccessibilityService_canRequestFilterKeyEvents
+ * @attr ref android.R.styleable#AccessibilityService_canRequestFingerprintGestures
  * @attr ref android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
  * @attr ref android.R.styleable#AccessibilityService_canRetrieveWindowContent
- * @attr ref android.R.styleable#AccessibilityService_intro
+ * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot
  * @attr ref android.R.styleable#AccessibilityService_description
- * @attr ref android.R.styleable#AccessibilityService_summary
+ * @attr ref android.R.styleable#AccessibilityService_htmlDescription
+ * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout
+ * @attr ref android.R.styleable#AccessibilityService_intro
+ * @attr ref android.R.styleable#AccessibilityService_isAccessibilityTool
+ * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
  * @attr ref android.R.styleable#AccessibilityService_notificationTimeout
  * @attr ref android.R.styleable#AccessibilityService_packageNames
  * @attr ref android.R.styleable#AccessibilityService_settingsActivity
+ * @attr ref android.R.styleable#AccessibilityService_summary
  * @attr ref android.R.styleable#AccessibilityService_tileService
- * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
- * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout
- * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot
  * @see AccessibilityService
  * @see android.view.accessibility.AccessibilityEvent
  * @see android.view.accessibility.AccessibilityManager
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7273e64..36fc65a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1031,7 +1031,9 @@
             | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
 
     /**
-     * All implicit capabilities. There are capabilities that process automatically have.
+     * All implicit capabilities. This capability set is currently only used for processes under
+     * active instrumentation. The intent is to allow CTS tests to always have these capabilities
+     * so that every test doesn't need to launch FGS.
      * @hide
      */
     @TestApi
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 3bd121a..f80121d 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1328,7 +1328,8 @@
      * Add a creator token for all embedded intents (stored as extra) of the given intent.
      *
      * @param intent The given intent
+     * @param creatorPackage the package name of the creator app.
      * @hide
      */
-    public abstract void addCreatorToken(Intent intent);
+    public abstract void addCreatorToken(Intent intent, String creatorPackage);
 }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 6ab39b0..832c88a 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -120,7 +120,7 @@
     /**
      * Grants the {@link PendingIntent} background activity start privileges.
      *
-     * This behaves the same as {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOWED_ALWAYS}, except it
+     * This behaves the same as {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS}, except it
      * does not grant background activity launch permissions based on the privileged permission
      * <code>START_ACTIVITIES_FROM_BACKGROUND</code>.
      *
@@ -136,7 +136,6 @@
     /**
      * Denies the {@link PendingIntent} any background activity start privileges.
      */
-    @FlaggedApi(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
     public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2;
     /**
      * Grants the {@link PendingIntent} all background activity start privileges, including
@@ -146,12 +145,12 @@
      * <p><b>Caution:</b> This mode should be used sparingly. Most apps should use
      * {@link #MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE} instead, relying on notifications
      * or foreground services for background interactions to minimize user disruption. However,
-     * this mode  is necessary for specific use cases, such as companion apps responding to
+     * this mode is necessary for specific use cases, such as companion apps responding to
      * prompts from a connected device.
      *
      * <p>For more information on background activity start restrictions, see:
      * <a href="https://developer.android.com/guide/components/activities/background-starts">
-     * Restrictions on starting activities from  the background</a>
+     * Restrictions on starting activities from the background</a>
      */
     @FlaggedApi(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
     public static final int MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS = 3;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0c02ba4..e7f4dbc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3841,6 +3841,18 @@
         return activityRecord != null ? activityRecord.activity : null;
     }
 
+    /**
+     * Returns the most recent created activity that's still running.
+     */
+    @Nullable
+    public Activity getLastCreatedActivity() {
+        if (mActivities.isEmpty()) {
+            return null;
+        }
+
+        return mActivities.valueAt(mActivities.size() - 1).activity;
+    }
+
     @Override
     public ActivityClientRecord getActivityClient(IBinder token) {
         return mActivities.get(token);
@@ -7334,6 +7346,8 @@
             }
         }
 
+        VMDebug.setUserId(UserHandle.myUserId());
+        VMDebug.addApplication(data.appInfo.packageName);
         // send up app name; do this *before* waiting for debugger
         Process.setArgV0(data.processName);
         android.ddm.DdmHandleAppName.setAppName(data.processName,
@@ -7856,9 +7870,20 @@
             file.getParentFile().mkdirs();
             Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
         }
+
+        if (ii.packageName != null) {
+            VMDebug.addApplication(ii.packageName);
+        }
     }
 
     private void handleFinishInstrumentationWithoutRestart() {
+        LoadedApk loadedApk = getApplication().mLoadedApk;
+        // Only remove instrumentation app if this was not a self-testing app.
+        if (mInstrumentationPackageName != null && loadedApk != null && !mInstrumentationPackageName
+                .equals(loadedApk.mPackageName)) {
+            VMDebug.removeApplication(mInstrumentationPackageName);
+        }
+
         mInstrumentation.onDestroy();
         mInstrumentationPackageName = null;
         mInstrumentationAppDir = null;
@@ -8892,6 +8917,11 @@
         return false;
     }
 
+    void addApplication(@NonNull Application app) {
+        mAllApplications.add(app);
+        VMDebug.addApplication(app.mLoadedApk.mPackageName);
+    }
+
     @Override
     public boolean isInDensityCompatMode() {
         return mDensityCompatMode;
diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md
index 7b11a03..535d62c 100644
--- a/core/java/android/app/AppOps.md
+++ b/core/java/android/app/AppOps.md
@@ -119,20 +119,20 @@
 In addition to proc state, the `AppOpsService` also receives process capability update from the
 `ActivityManagerService`. Proc capability specifies what while-in-use(`MODE_FOREGROUND`) operations
  the proc is allowed to perform in its current proc state. There are three proc capabilities
- defined so far: 
+ defined so far:
 `PROCESS_CAPABILITY_FOREGROUND_LOCATION`, `PROCESS_CAPABILITY_FOREGROUND_CAMERA` and
 `PROCESS_CAPABILITY_FOREGROUND_MICROPHONE`, they correspond to the while-in-use operation of
 location, camera and microphone (microphone is `RECORD_AUDIO`).
 
 In `ActivityManagerService`, `PROCESS_STATE_TOP` and `PROCESS_STATE_PERSISTENT` have all
 three capabilities, `PROCESS_STATE_FOREGROUND_SERVICE` has capabilities defined by
- `foregroundServiceType` that is specified in foreground service's manifest file. A client process 
+ `foregroundServiceType` that is specified in foreground service's manifest file. A client process
  can pass its capabilities to service using `BIND_INCLUDE_CAPABILITIES` flag.
 
 The proc state and capability are used for two use cases: Firstly, Tracking remembers the proc state
  for each tracked event. Secondly, `noteOp`/`checkOp` calls for app-op that are set to
  `MODE_FOREGROUND` are translated using the `AppOpsService.UidState.evalMode` method into
- `MODE_ALLOWED` when the app has the capability and `MODE_IGNORED` when the app does not have the 
+ `MODE_ALLOWED` when the app has the capability and `MODE_IGNORED` when the app does not have the
  capability. `checkOpRaw` calls are not affected.
 
 The current proc state and capability for an app can be read from `dumpsys appops`.
@@ -284,7 +284,7 @@
 ##### Self data accesses
 
 This is similar to the [synchronous data access](#synchronous-data-accesses) case only that the data
-provider and client are in the same process. In this case Android's RPC code is no involved and
+provider and client are in the same process. In this case Android's RPC code is not involved and
 `AppOpsManager.noteOp` directly triggers `OnOpNotedCallback.onSelfNoted`. This should be a uncommon
 case as it is uncommon for an app to provide data, esp. to itself.
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0472ff8..2e3d226 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -264,6 +264,13 @@
     private static @Nullable OnOpNotedCallback sOnOpNotedCallback;
 
     /**
+     * Whether OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC was set when sOnOpNotedCallback was registered
+     * last time.
+     */
+    @GuardedBy("sLock")
+    private static boolean sIgnoreAsyncNotedCallback;
+
+    /**
      * Sync note-ops collected from {@link #readAndLogNotedAppops(Parcel)} that have not been
      * delivered to a callback yet.
      *
@@ -10111,6 +10118,22 @@
     private static final int COLLECT_SYNC = 2;
     private static final int COLLECT_ASYNC = 3;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "OP_NOTED_CALLBACK_FLAG_" }, value = {
+            OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC,
+    })
+    private @interface OpNotedCallbackFlags {}
+
+    /**
+     * Ignores async op noted events.
+     *
+     * @see #setOnOpNotedCallback
+     */
+    @FlaggedApi(android.permission.flags.Flags.FLAG_SYNC_ON_OP_NOTED_API)
+    public static final int OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC = 1;
+    private static final int OP_NOTED_CALLBACK_FLAG_ALL = OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC;
+
     /**
      * Mark an app-op as noted.
      */
@@ -10256,6 +10279,12 @@
      * <p>There can only ever be one collector per process. If there currently is another callback
      * set, this will fail.
      *
+     * <p>Note that if an app has multiple processes registering for this callback, the system would
+     * fan out async op noted callbacks to each of the processes, resulting in the same data being
+     * delivered multiple times to an app, which is usually undesired. To avoid this, consider
+     * listening to async ops only in one process. See
+     * {@link #setOnOpNotedCallback(Executor, OnOpNotedCallback, int)} for how to do this.
+     *
      * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code
      * null} to unset
      * @param callback listener to set, {@code null} to unset
@@ -10264,18 +10293,62 @@
      */
     public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor,
             @Nullable OnOpNotedCallback callback) {
+        setOnOpNotedCallback(asyncExecutor, callback, /* flag */ 0);
+    }
+
+    /**
+     * Set a new {@link OnOpNotedCallback}.
+     *
+     * <p>There can only ever be one collector per process. If there currently is another callback
+     * set, this will fail.
+     *
+     * <p>This API allows the caller to listen only to sync and self op noted events, and ignore
+     * async ops. This is useful in the scenario where an app has multiple processes. Consider an
+     * example where an app has two processes, A and B. The op noted events are as follows:
+     * <ul>
+     * <li>op 1: process A, sync
+     * <li>op 2: process A, async
+     * <li>op 3: process B, sync
+     * <li>op 4: process B, async
+     * Any process that listens to async op noted events gets events originating from across ALL
+     * processes (op 2 and op 4 in this example). So if both process A and B register as listeners,
+     * both of them get op 2 and 4 which is not ideal. To avoid duplicates, one of the two processes
+     * should set {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC}. For example
+     * process A sets {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC} and would then only get its own
+     * sync event (op 1). The other process would then listen to all types of events and get op 2, 3
+     * and 4.
+     *
+     * Note that even with {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC},
+     * {@link #OnOpNotedCallback.onAsyncNoted} may still be invoked. This happens for sync events
+     * that were collected before a callback is registered.
+     *
+     * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code
+     * null} to unset
+     * @param callback listener to set, {@code null} to unset
+     * @param flags additional flags to modify the callback behavior, such as
+     * {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC}
+     *
+     * @throws IllegalStateException If another callback is already registered
+     */
+    @FlaggedApi(android.permission.flags.Flags.FLAG_SYNC_ON_OP_NOTED_API)
+    public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor,
+            @Nullable OnOpNotedCallback callback, @OpNotedCallbackFlags int flags) {
         Preconditions.checkState((callback == null) == (asyncExecutor == null));
+        Preconditions.checkFlagsArgument(flags, OP_NOTED_CALLBACK_FLAG_ALL);
 
         synchronized (sLock) {
             if (callback == null) {
+                Preconditions.checkFlagsArgument(flags, 0);
                 Preconditions.checkState(sOnOpNotedCallback != null,
                         "No callback is currently registered");
 
-                try {
-                    mService.stopWatchingAsyncNoted(mContext.getPackageName(),
-                            sOnOpNotedCallback.mAsyncCb);
-                } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
+                if (!sIgnoreAsyncNotedCallback) {
+                    try {
+                        mService.stopWatchingAsyncNoted(mContext.getPackageName(),
+                                sOnOpNotedCallback.mAsyncCb);
+                    } catch (RemoteException e) {
+                        e.rethrowFromSystemServer();
+                    }
                 }
 
                 sOnOpNotedCallback = null;
@@ -10285,14 +10358,17 @@
 
                 callback.mAsyncExecutor = asyncExecutor;
                 sOnOpNotedCallback = callback;
+                sIgnoreAsyncNotedCallback = (flags & OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC) != 0;
 
                 List<AsyncNotedAppOp> missedAsyncOps = null;
-                try {
-                    mService.startWatchingAsyncNoted(mContext.getPackageName(),
-                            sOnOpNotedCallback.mAsyncCb);
-                    missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName());
-                } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
+                if (!sIgnoreAsyncNotedCallback) {
+                    try {
+                        mService.startWatchingAsyncNoted(mContext.getPackageName(),
+                                sOnOpNotedCallback.mAsyncCb);
+                        missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName());
+                    } catch (RemoteException e) {
+                        e.rethrowFromSystemServer();
+                    }
                 }
 
                 // Copy pointer so callback can be dispatched out of lock
@@ -10305,17 +10381,15 @@
                                 () -> onOpNotedCallback.onAsyncNoted(asyncNotedAppOp));
                     }
                 }
-                synchronized (this) {
-                    int numMissedSyncOps = sUnforwardedOps.size();
-                    if (onOpNotedCallback != null) {
-                        for (int i = 0; i < numMissedSyncOps; i++) {
-                            final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i);
-                            onOpNotedCallback.getAsyncNotedExecutor().execute(
-                                    () -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp));
-                        }
+                int numMissedSyncOps = sUnforwardedOps.size();
+                if (onOpNotedCallback != null) {
+                    for (int i = 0; i < numMissedSyncOps; i++) {
+                        final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i);
+                        onOpNotedCallback.getAsyncNotedExecutor().execute(
+                                () -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp));
                     }
-                    sUnforwardedOps.clear();
                 }
+                sUnforwardedOps.clear();
             }
         }
     }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 3b2aab4..a7597b4 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -84,6 +84,8 @@
     boolean isImportanceLocked(String pkg, int uid);
 
     List<String> getAllowedAssistantAdjustments(String pkg);
+    void allowAssistantAdjustment(String adjustmentType);
+    void disallowAssistantAdjustment(String adjustmentType);
 
     boolean shouldHideSilentStatusIcons(String callingPkg);
     void setHideSilentStatusIcons(boolean hide);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 93a9489..7eacaac 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -48,6 +48,9 @@
 import android.os.TestLooperManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.ravenwood.annotation.RavenwoodKeep;
+import android.ravenwood.annotation.RavenwoodKeepPartialClass;
+import android.ravenwood.annotation.RavenwoodReplace;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 import android.view.Display;
@@ -80,7 +83,7 @@
  * implementation is described to the system through an AndroidManifest.xml's
  * &lt;instrumentation&gt; tag.
  */
-@android.ravenwood.annotation.RavenwoodKeepPartialClass
+@RavenwoodKeepPartialClass
 public class Instrumentation {
 
     /**
@@ -136,7 +139,7 @@
     private UiAutomation mUiAutomation;
     private final Object mAnimationCompleteLock = new Object();
 
-    @android.ravenwood.annotation.RavenwoodKeep
+    @RavenwoodKeep
     public Instrumentation() {
     }
 
@@ -147,7 +150,7 @@
      * reflection, but it will serve as noticeable discouragement from
      * doing such a thing.
      */
-    @android.ravenwood.annotation.RavenwoodKeep
+    @RavenwoodKeep
     private void checkInstrumenting(String method) {
         // Check if we have an instrumentation context, as init should only get called by
         // the system in startup processes that are being instrumented.
@@ -162,7 +165,7 @@
      *
      * @hide
      */
-    @android.ravenwood.annotation.RavenwoodKeep
+    @RavenwoodKeep
     public boolean isInstrumenting() {
         // Check if we have an instrumentation context, as init should only get called by
         // the system in startup processes that are being instrumented.
@@ -326,7 +329,7 @@
      * 
      * @see #getTargetContext
      */
-    @android.ravenwood.annotation.RavenwoodKeep
+    @RavenwoodKeep
     public Context getContext() {
         return mInstrContext;
     }
@@ -351,7 +354,7 @@
      * 
      * @see #getContext
      */
-    @android.ravenwood.annotation.RavenwoodKeep
+    @RavenwoodKeep
     public Context getTargetContext() {
         return mAppContext;
     }
@@ -2407,10 +2410,11 @@
      *
      * @hide
      */
-    @android.ravenwood.annotation.RavenwoodKeep
-    public final void basicInit(Context instrContext, Context appContext) {
+    @RavenwoodKeep
+    public final void basicInit(Context instrContext, Context appContext, UiAutomation ui) {
         mInstrContext = instrContext;
         mAppContext = appContext;
+        mUiAutomation = ui;
     }
 
     /** @hide */
@@ -2501,6 +2505,7 @@
      *
      * @see UiAutomation
      */
+    @RavenwoodKeep
     public UiAutomation getUiAutomation() {
         return getUiAutomation(0);
     }
@@ -2539,6 +2544,7 @@
      *
      * @see UiAutomation
      */
+    @RavenwoodReplace
     public UiAutomation getUiAutomation(@UiAutomationFlags int flags) {
         boolean mustCreateNewAutomation = (mUiAutomation == null) || (mUiAutomation.isDestroyed());
 
@@ -2569,11 +2575,15 @@
         return null;
     }
 
+    private UiAutomation getUiAutomation$ravenwood(@UiAutomationFlags int flags) {
+        return mUiAutomation;
+    }
+
     /**
      * Takes control of the execution of messages on the specified looper until
      * {@link TestLooperManager#release} is called.
      */
-    @android.ravenwood.annotation.RavenwoodKeep
+    @RavenwoodKeep
     public TestLooperManager acquireLooperManager(Looper looper) {
         checkInstrumenting("acquireLooperManager");
         return new TestLooperManager(looper);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1df8f63..1e45d6f 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1478,7 +1478,7 @@
                         + " package " + mPackageName + ": " + e.toString(), e);
                 }
             }
-            mActivityThread.mAllApplications.add(app);
+            mActivityThread.addApplication(app);
             mApplication = app;
             if (!allowDuplicateInstances) {
                 synchronized (sApplications) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bc7ebce..64aa705 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -115,6 +115,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.internal.util.NotificationBigTextNormalizer;
+import com.android.internal.widget.NotificationProgressModel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -7318,12 +7319,16 @@
          */
         @VisibleForTesting
         public static int ensureButtonFillContrast(int color, int bg) {
-            return isColorDark(bg)
-                    ? ContrastColorUtil.findContrastColorAgainstDark(color, bg, true, 1.3)
-                    : ContrastColorUtil.findContrastColor(color, bg, true, 1.3);
+            return ensureColorContrast(color, bg, 1.3);
         }
 
 
+        private static int ensureColorContrast(int color, int bg, double contrastRatio) {
+            return isColorDark(bg)
+                    ? ContrastColorUtil.findContrastColorAgainstDark(color, bg, true, contrastRatio)
+                    : ContrastColorUtil.findContrastColor(color, bg, true, contrastRatio);
+        }
+
         /**
          * @return Whether we are currently building a notification from a legacy (an app that
          *         doesn't create material notifications by itself) app.
@@ -11171,7 +11176,7 @@
     }
 
     /**
-     * A Notification Style used to to define a notification whose expanded state includes
+     * A Notification Style used to define a notification whose expanded state includes
      * a highly customizable progress bar with segments, points, a custom tracker icon,
      * and custom icons at the start and end of the progress bar.
      *
@@ -11291,7 +11296,11 @@
          * @see Segment
          */
         public @NonNull ProgressStyle setProgressSegments(@NonNull List<Segment> progressSegments) {
-            mProgressSegments = new ArrayList<>(progressSegments.size());
+            if (mProgressSegments == null) {
+                mProgressSegments = new ArrayList<>();
+            }
+            mProgressSegments.clear();
+            mProgressSegments.addAll(progressSegments);
             return this;
         }
 
@@ -11657,6 +11666,7 @@
             StandardTemplateParams p = mBuilder.mParams.reset()
                     .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
                     .allowTextWithProgress(true)
+                    .hideProgress(true)
                     .fillTextsFrom(mBuilder);
 
             // Replace the text with the big text, but only if the big text is not empty.
@@ -11678,10 +11688,28 @@
                 contentView.setViewVisibility(R.id.notification_progress_end_icon, View.GONE);
             }
 
+            contentView.setViewVisibility(R.id.progress, View.VISIBLE);
+
+            final int backgroundColor = mBuilder.getColors(p).getBackgroundColor();
+            final int defaultProgressColor = mBuilder.getPrimaryAccentColor(p);
+            final NotificationProgressModel model = createProgressModel(
+                    defaultProgressColor, backgroundColor);
+            contentView.setBundle(R.id.progress,
+                    "setProgressModel", model.toBundle());
+
+            if (mTrackerIcon != null) {
+                contentView.setIcon(R.id.progress,
+                        "setProgressTrackerIcon",
+                        mTrackerIcon);
+            }
+
             return contentView;
         }
 
-        private static @NonNull ArrayList<Bundle> getProgressSegmentsAsBundleList(
+        /**
+         * @hide
+         */
+        public static @NonNull ArrayList<Bundle> getProgressSegmentsAsBundleList(
                 @Nullable List<Segment> progressSegments) {
             final ArrayList<Bundle> segments = new ArrayList<>();
             if (progressSegments != null && !progressSegments.isEmpty()) {
@@ -11703,7 +11731,10 @@
             return segments;
         }
 
-        private static @NonNull List<Segment> getProgressSegmentsFromBundleList(
+        /**
+         * @hide
+         */
+        public  static @NonNull List<Segment> getProgressSegmentsFromBundleList(
                 @Nullable List<Bundle> segmentBundleList) {
             final ArrayList<Segment> segments = new ArrayList<>();
             if (segmentBundleList != null && !segmentBundleList.isEmpty()) {
@@ -11726,8 +11757,10 @@
 
             return segments;
         }
-
-        private static @NonNull ArrayList<Bundle> getProgressPointsAsBundleList(
+        /**
+         * @hide
+         */
+        public static @NonNull ArrayList<Bundle> getProgressPointsAsBundleList(
                 @Nullable List<Point> progressPoints) {
             final ArrayList<Bundle> points = new ArrayList<>();
             if (progressPoints != null && !progressPoints.isEmpty()) {
@@ -11749,7 +11782,10 @@
             return points;
         }
 
-        private static @NonNull List<Point> getProgressPointsFromBundleList(
+        /**
+         * @hide
+         */
+        public static @NonNull List<Point> getProgressPointsFromBundleList(
                 @Nullable List<Bundle> pointBundleList) {
             final ArrayList<Point> points = new ArrayList<>();
 
@@ -11771,6 +11807,78 @@
             return points;
         }
 
+        @NonNull
+        private NotificationProgressModel createProgressModel(int defaultProgressColor,
+                int backgroundColor) {
+            final NotificationProgressModel model;
+            if (mIndeterminate) {
+                final int indeterminateColor;
+                if (!mProgressSegments.isEmpty()) {
+                    indeterminateColor = mProgressSegments.get(0).mColor;
+                } else {
+                    indeterminateColor = defaultProgressColor;
+                }
+
+                model = new NotificationProgressModel(
+                        sanitizeProgressColor(indeterminateColor,
+                                backgroundColor, defaultProgressColor));
+            } else {
+
+                // Ensure segment color contrasts.
+                final List<Segment> segments = new ArrayList<>();
+                for (Segment segment : mProgressSegments) {
+                    segments.add(sanitizeSegment(segment, backgroundColor,
+                            defaultProgressColor));
+                }
+
+                // Create default segment when no segments are provided.
+                if (segments.isEmpty()) {
+                    segments.add(sanitizeSegment(new Segment(100), backgroundColor,
+                            defaultProgressColor));
+                }
+
+                // Ensure point color contrasts.
+                final List<Point> points = new ArrayList<>();
+                for (Point point : mProgressPoints) {
+                    points.add(sanitizePoint(point, backgroundColor, defaultProgressColor));
+                }
+
+                model = new NotificationProgressModel(segments, points,
+                        mProgress, mIsStyledByProgress);
+            }
+            return model;
+        }
+
+        private Segment sanitizeSegment(@NonNull Segment segment,
+                @ColorInt int bg,
+                @ColorInt int defaultColor) {
+            return new Segment(segment.getLength())
+                    .setId(segment.getId())
+                    .setColor(sanitizeProgressColor(segment.getColor(), bg, defaultColor));
+        }
+
+        private Point sanitizePoint(@NonNull Point point,
+                @ColorInt int bg,
+                @ColorInt int defaultColor) {
+            return new Point(point.getPosition()).setId(point.getId())
+                    .setColor(sanitizeProgressColor(point.getColor(), bg, defaultColor));
+        }
+
+        /**
+         * Finds steps and points fill color with sufficient contrast over bg (1.3:1) that
+         * has the same hue as the original color, but is lightened or darkened depending on
+         * whether the background is dark or light.
+         *
+         */
+        private int sanitizeProgressColor(@ColorInt int color,
+                @ColorInt int bg,
+                @ColorInt int defaultColor) {
+            return Builder.ensureColorContrast(
+                    Color.alpha(color) == 0 ? defaultColor : color,
+                    bg,
+                    1.3);
+        }
+
         /**
          * A segment of the progress bar, which defines its length and color.
          * Segments allow for creating progress bars with multiple colors or sections
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 4a2b016..ebe7b3a 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -38,6 +38,7 @@
 import android.service.notification.NotificationListenerService;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.Preconditions;
@@ -1369,12 +1370,17 @@
         if (sound == null || Uri.EMPTY.equals(sound)) {
             return null;
         }
-        Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound);
-        if (canonicalSound == null) {
-            // The content provider does not support canonical uris so we backup the default
+        try {
+            Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound);
+            if (canonicalSound == null) {
+                // The content provider does not support canonical uris so we backup the default
+                return Settings.System.DEFAULT_NOTIFICATION_URI;
+            }
+            return canonicalSound;
+        } catch (Exception e) {
+            Slog.e(TAG, "Cannot find file for sound " + sound + " using default");
             return Settings.System.DEFAULT_NOTIFICATION_URI;
         }
-        return canonicalSound;
     }
 
     /**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 41abd68..06bf67c 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1790,6 +1790,34 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public void allowAssistantAdjustment(@NonNull String capability) {
+        INotificationManager service = getService();
+        try {
+            service.allowAssistantAdjustment(capability);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public void disallowAssistantAdjustment(@NonNull String capability) {
+        INotificationManager service = getService();
+        try {
+            service.disallowAssistantAdjustment(capability);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     @TestApi
     public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String pkg) {
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index d363e19..6e4c28f 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -3,50 +3,54 @@
 per-file ContextImpl.java = *
 
 # ActivityManager
-per-file ActivityManager* = file:/services/core/java/com/android/server/am/OWNERS
-per-file *ApplicationStartInfo* = file:/services/core/java/com/android/server/am/OWNERS
-per-file ApplicationErrorReport* = file:/services/core/java/com/android/server/am/OWNERS
-per-file ApplicationExitInfo* = file:/services/core/java/com/android/server/am/OWNERS
-per-file Application.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file ApplicationLoaders.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file ApplicationThreadConstants.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file ContentProviderHolder* = file:/services/core/java/com/android/server/am/OWNERS
-per-file *ForegroundService* = file:/services/core/java/com/android/server/am/OWNERS
-per-file IActivityController.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IAppTraceRetriever.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IForegroundServiceObserver.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IInstrumentationWatcher.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IStopUserCallback.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file IUidObserver.aidl = file:/services/core/java/com/android/server/am/OWNERS
-per-file LoadedApk.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file LocalActivityManager.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file PendingIntent* = file:/services/core/java/com/android/server/am/OWNERS
-per-file *Process* = file:/services/core/java/com/android/server/am/OWNERS
-per-file ProfilerInfo* = file:/services/core/java/com/android/server/am/OWNERS
-per-file Service* = file:/services/core/java/com/android/server/am/OWNERS
-per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
+per-file ActivityManager* = file:/ACTIVITY_MANAGER_OWNERS
+per-file Application.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ApplicationErrorReport* = file:/ACTIVITY_MANAGER_OWNERS
+per-file ApplicationLoaders.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ApplicationThreadConstants.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ContentProviderHolder* = file:/ACTIVITY_MANAGER_OWNERS
+per-file *ForegroundService* = file:/ACTIVITY_MANAGER_OWNERS
+per-file IActivityController.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IActivityManager.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IApplicationThread.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IAppTraceRetriever.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IForegroundServiceObserver.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IInstrumentationWatcher.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IntentService.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IServiceConnection.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IStopUserCallback.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file IUidObserver.aidl = file:/ACTIVITY_MANAGER_OWNERS
+per-file LoadedApk.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file LocalActivityManager.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file PendingIntent* = file:/ACTIVITY_MANAGER_OWNERS
+per-file *Process* = file:/ACTIVITY_MANAGER_OWNERS
+per-file ProfilerInfo* = file:/ACTIVITY_MANAGER_OWNERS
+per-file Service* = file:/ACTIVITY_MANAGER_OWNERS
+per-file SystemServiceRegistry.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file *UserSwitchObserver* = file:/ACTIVITY_MANAGER_OWNERS
+
+# UI Automation
 per-file *UiAutomation* = file:/services/accessibility/OWNERS
 per-file *UiAutomation* = file:/core/java/android/permission/OWNERS
+
+# Game Manager
 per-file GameManager* = file:/GAME_MANAGER_OWNERS
 per-file GameMode* = file:/GAME_MANAGER_OWNERS
 per-file GameState* = file:/GAME_MANAGER_OWNERS
 per-file IGameManager* = file:/GAME_MANAGER_OWNERS
 per-file IGameMode* = file:/GAME_MANAGER_OWNERS
+
+# Background Starts
 per-file BackgroundStartPrivileges.java = file:/BAL_OWNERS
 per-file activity_manager.aconfig = file:/ACTIVITY_MANAGER_OWNERS
 
 # ActivityThread
-per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file ActivityThread.java = file:/ACTIVITY_MANAGER_OWNERS
 per-file ActivityThread.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file ActivityThread.java = file:RESOURCES_OWNERS
 
 # Alarm
-per-file *Alarm* = file:/apex/jobscheduler/OWNERS
+per-file *Alarm* = file:/apex/jobscheduler/ALARM_OWNERS
 
 # AppOps
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
@@ -97,6 +101,9 @@
 
 # Performance
 per-file PropertyInvalidatedCache.java = file:/PERFORMANCE_OWNERS
+per-file *ApplicationStartInfo* = file:/PERFORMANCE_OWNERS
+per-file ApplicationExitInfo* = file:/PERFORMANCE_OWNERS
+per-file performance.aconfig = file:/PERFORMANCE_OWNERS
 
 # Pinner
 per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ea4148c..bd26db5 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -76,7 +76,6 @@
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.EnabledSince;
 import android.content.ClipboardManager;
 import android.content.ContentCaptureOptions;
 import android.content.Context;
@@ -172,8 +171,7 @@
 import android.net.PacProxyManager;
 import android.net.TetheringManager;
 import android.net.VpnManager;
-import android.net.vcn.IVcnManagementService;
-import android.net.vcn.VcnManager;
+import android.net.vcn.VcnFrameworkInitializer;
 import android.net.wifi.WifiFrameworkInitializer;
 import android.net.wifi.nl80211.WifiNl80211Manager;
 import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
@@ -208,7 +206,6 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.StatsFrameworkInitializer;
 import android.os.SystemConfigManager;
-import android.os.SystemProperties;
 import android.os.SystemUpdateManager;
 import android.os.SystemVibrator;
 import android.os.SystemVibratorManager;
@@ -235,8 +232,11 @@
 import android.scheduling.SchedulingFrameworkInitializer;
 import android.security.FileIntegrityManager;
 import android.security.IFileIntegrityService;
+import android.security.advancedprotection.AdvancedProtectionManager;
+import android.security.advancedprotection.IAdvancedProtectionService;
 import android.security.attestationverification.AttestationVerificationManager;
 import android.security.attestationverification.IAttestationVerificationManagerService;
+import android.security.keystore.KeyStoreManager;
 import android.service.oemlock.IOemLockService;
 import android.service.oemlock.OemLockManager;
 import android.service.persistentdata.IPersistentDataBlockService;
@@ -300,18 +300,6 @@
     public static boolean sEnableServiceNotFoundWtf = false;
 
     /**
-     * Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags
-     * (e.g. {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before
-     * returning managers that depend on them. If the feature is missing,
-     * {@link Context#getSystemService} will return null.
-     *
-     * This change is specific to VcnManager.
-     */
-    @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    static final long ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN = 330902016;
-
-    /**
      * After {@link Build.VERSION_CODES.VANILLA_ICE_CREAM}, Wear devices will be allowed to publish
      * no {@link GameManager} instance. This is because the respective system service is no longer
      * started for Wear devices given that the applications of the service do not currently apply to
@@ -321,16 +309,6 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     static final long NULL_GAME_MANAGER_IN_WEAR = 340929737;
 
-    /**
-     * The corresponding vendor API for Android V
-     *
-     * <p>Starting with Android V, the vendor API format has switched to YYYYMM.
-     *
-     * @see <a href="https://preview.source.android.com/docs/core/architecture/api-flags">Vendor API
-     *     level</a>
-     */
-    private static final int VENDOR_API_FOR_ANDROID_V = 202404;
-
     // Service registry information.
     // This information is never changed once static initialization has completed.
     private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES =
@@ -497,22 +475,6 @@
                 return new VpnManager(ctx, service);
             }});
 
-        registerService(Context.VCN_MANAGEMENT_SERVICE, VcnManager.class,
-                new CachedServiceFetcher<VcnManager>() {
-            @Override
-            public VcnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                final String telephonyFeatureToCheck = getVcnFeatureDependency();
-
-                if (telephonyFeatureToCheck != null
-                    && !ctx.getPackageManager().hasSystemFeature(telephonyFeatureToCheck)) {
-                    return null;
-                }
-
-                IBinder b = ServiceManager.getService(Context.VCN_MANAGEMENT_SERVICE);
-                IVcnManagementService service = IVcnManagementService.Stub.asInterface(b);
-                return new VcnManager(ctx, service);
-            }});
-
         registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
                 new StaticServiceFetcher<CountryDetector>() {
             @Override
@@ -1744,6 +1706,17 @@
                     }
                 });
 
+        registerService(Context.KEYSTORE_SERVICE, KeyStoreManager.class,
+                new StaticServiceFetcher<KeyStoreManager>() {
+                    @Override
+                    public KeyStoreManager createService()
+                            throws ServiceNotFoundException {
+                        if (!android.security.Flags.keystoreGrantApi()) {
+                            throw new ServiceNotFoundException("KeyStoreManager is not supported");
+                        }
+                        return KeyStoreManager.getInstance();
+                    }});
+
         registerService(Context.CONTACT_KEYS_SERVICE, E2eeContactKeysManager.class,
                 new CachedServiceFetcher<E2eeContactKeysManager>() {
                     @Override
@@ -1771,6 +1744,21 @@
                         return new SupervisionManager(ctx, service);
                     }
                 });
+        if (android.security.Flags.aapmApi()) {
+            registerService(Context.ADVANCED_PROTECTION_SERVICE, AdvancedProtectionManager.class,
+                    new CachedServiceFetcher<>() {
+                        @Override
+                        public AdvancedProtectionManager createService(ContextImpl ctx)
+                                throws ServiceNotFoundException {
+                            IBinder iBinder = ServiceManager.getServiceOrThrow(
+                                    Context.ADVANCED_PROTECTION_SERVICE);
+                            IAdvancedProtectionService service =
+                                    IAdvancedProtectionService.Stub.asInterface(iBinder);
+                            return new AdvancedProtectionManager(service);
+                        }
+                    });
+        }
+
         // DO NOT do a flag check like this unless the flag is read-only.
         // (because this code is executed during preload in zygote.)
         // If the flag is mutable, the check should be inside CachedServiceFetcher.
@@ -1812,6 +1800,8 @@
             OnDevicePersonalizationFrameworkInitializer.registerServiceWrappers();
             DeviceLockFrameworkInitializer.registerServiceWrappers();
             VirtualizationFrameworkInitializer.registerServiceWrappers();
+            VcnFrameworkInitializer.registerServiceWrappers();
+
             if (com.android.server.telecom.flags.Flags.telecomMainlineBlockedNumbersManager()) {
                 ProviderFrameworkInitializer.registerServiceWrappers();
             }
@@ -1873,30 +1863,6 @@
         return manager.hasSystemFeature(featureName);
     }
 
-    // Suppressing AndroidFrameworkCompatChange because we're querying vendor
-    // partition SDK level, not application's target SDK version (which BTW we
-    // also check through Compatibility framework a few lines below).
-    @SuppressWarnings("AndroidFrameworkCompatChange")
-    @Nullable
-    private static String getVcnFeatureDependency() {
-        // Check SDK version of the client app. Apps targeting pre-V SDK might
-        // have not checked for existence of these features.
-        if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN)) {
-            return null;
-        }
-
-        // Check SDK version of the vendor partition. Pre-V devices might have
-        // incorrectly under-declared telephony features.
-        final int vendorApiLevel = SystemProperties.getInt(
-                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
-        if (vendorApiLevel < VENDOR_API_FOR_ANDROID_V) {
-            return PackageManager.FEATURE_TELEPHONY;
-        } else {
-            return PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION;
-        }
-
-    }
-
     /**
      * Gets a system service from a given context.
      * @hide
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9be928f..102540c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -470,16 +470,9 @@
      * that the user backed-out of provisioning or some precondition for provisioning wasn't met.
      *
      * <p>If a <a href="#roleholder">device policy management role holder</a> updater is present on
-     * the device, an internet connection attempt must be made prior to launching this intent. If
-     * an internet connection can not be established, provisioning will fail unless {@link
-     * #EXTRA_PROVISIONING_ALLOW_OFFLINE} is explicitly set to {@code true}, in which case
-     * provisioning will continue without using the
-     * <a href="#roleholder">device policy management role holder</a>. If an internet connection
-     * has been established, the <a href="#roleholder">device policy management role holder</a>
-     * updater will be launched, which may update the
-     * <a href="#roleholder">device policy management role holder</a> before continuing
-     * provisioning.
+     * the device, an internet connection attempt must be made prior to launching this intent.
      */
+    // See b/365955253 for additional behaviours of this API.
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_PROVISION_MANAGED_PROFILE
         = "android.app.action.PROVISION_MANAGED_PROFILE";
@@ -960,23 +953,8 @@
      * A boolean extra indicating whether offline provisioning should be used.
      *
      * <p>The default value is {@code false}.
-     *
-     * <p>Usually during the <a href="#managedprovisioning">provisioning flow</a>, there will be
-     * an attempt to download and install the latest version of the <a href="#roleholder">device
-     * policy management role holder</a>. The platform will then
-     * delegate provisioning to the <a href="#roleholder">device
-     *      * policy management role holder</a>.
-     *
-     * <p>When this extra is set to {@code true}, the
-     * <a href="#managedprovisioning">provisioning flow</a> will always be handled by the platform
-     * and the <a href="#roleholder">device policy management role holder</a>'s part skipped.
-     *
-     * <p>On Android versions prior to {@link Build.VERSION_CODES#TIRAMISU}, when this extra is
-     * {@code false}, the <a href="#managedprovisioning">provisioning flow</a> will enforce that an
-     * internet connection is established, or otherwise fail. When this extra is {@code true}, a
-     * connection will still be attempted but when it cannot be established provisioning will
-     * continue offline.
      */
+    // See b/365955253 for detailed behaviours of this API.
     public static final String EXTRA_PROVISIONING_ALLOW_OFFLINE =
             "android.app.extra.PROVISIONING_ALLOW_OFFLINE";
 
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 439d988..dca4336 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -110,40 +110,6 @@
      *
      * @param request the request to execute the app function
      * @param executor the executor to run the callback
-     * @param callback the callback to receive the function execution result. if the calling app
-     *     does not own the app function or does not have {@code
-     *     android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
-     *     android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
-     *     ExecuteAppFunctionResponse.RESULT_DENIED}.
-     * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor,
-     *     CancellationSignal, Consumer)} instead. This method will be removed once usage references
-     *     are updated.
-     */
-    @RequiresPermission(
-            anyOf = {
-                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
-                Manifest.permission.EXECUTE_APP_FUNCTIONS
-            },
-            conditional = true)
-    @UserHandleAware
-    @Deprecated
-    public void executeAppFunction(
-            @NonNull ExecuteAppFunctionRequest request,
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
-        executeAppFunction(request, executor, new CancellationSignal(), callback);
-    }
-
-    /**
-     * Executes the app function.
-     *
-     * <p>Note: Applications can execute functions they define. To execute functions defined in
-     * another component, apps would need to have {@code
-     * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
-     * android.permission.EXECUTE_APP_FUNCTIONS}.
-     *
-     * @param request the request to execute the app function
-     * @param executor the executor to run the callback
      * @param cancellationSignal the cancellation signal to cancel the execution.
      * @param callback the callback to receive the function execution result. if the calling app
      *     does not own the app function or does not have {@code
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
index fa77e79..cb21d1f 100644
--- a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
+++ b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
@@ -20,7 +20,6 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.content.pm.PackageManager;
 
 /**
  * Represents the system configuration of support for the {@code AppFunctionManager} and associated
@@ -29,15 +28,13 @@
  * @hide
  */
 public class AppFunctionManagerConfiguration {
-    private final Context mContext;
-
     /**
      * Constructs a new instance of {@code AppFunctionManagerConfiguration}.
      *
      * @param context context
      */
     public AppFunctionManagerConfiguration(@NonNull final Context context) {
-        mContext = context;
+        // Context can be used to access system features, etc.
     }
 
     /**
@@ -46,7 +43,7 @@
      * @return {@code true} if supported; otherwise {@code false}
      */
     public boolean isSupported() {
-        return enableAppFunctionManager() && !isWatch();
+        return enableAppFunctionManager();
     }
 
     /**
@@ -58,8 +55,4 @@
     public static boolean isSupported(@NonNull final Context context) {
         return new AppFunctionManagerConfiguration(context).isSupported();
     }
-
-    private boolean isWatch() {
-        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
-    }
 }
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index 08ecced..06d95f5 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -137,8 +137,10 @@
                                                 .TOKENIZER_TYPE_VERBATIM)
                                 .build())
                 .addProperty(
-                        new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED)
+                        new AppSearchSchema.LongPropertyConfig.Builder(PROPERTY_ENABLED)
                                 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingType(
+                                        AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE)
                                 .build())
                 .addProperty(
                         new AppSearchSchema.StringPropertyConfig.Builder(
@@ -212,19 +214,14 @@
         }
 
         /**
-         * Sets an indicator specifying if the function is enabled or not. This would override the
-         * default enabled state in the static metadata ({@link
-         * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}). Sets this to null
-         * to clear the override.
-         * TODO(369683073) Replace the tristate Boolean with IntDef EnabledState.
+         * Sets an indicator specifying the function enabled state.
          */
         @NonNull
         public Builder setEnabled(@EnabledState int enabledState) {
             if (enabledState != APP_FUNCTION_STATE_DEFAULT
                     && enabledState != APP_FUNCTION_STATE_ENABLED
                     && enabledState != APP_FUNCTION_STATE_DISABLED) {
-                throw new IllegalArgumentException(
-                        "Value of EnabledState is unsupported.");
+                throw new IllegalArgumentException("Value of EnabledState is unsupported.");
             }
             setPropertyLong(PROPERTY_ENABLED, enabledState);
             return this;
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 7a68a65..63d187a 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -29,11 +29,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
-import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
-import android.os.CancellationSignal;
-import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -80,6 +78,7 @@
          */
         void perform(
                 @NonNull ExecuteAppFunctionRequest request,
+                @NonNull String callingPackage,
                 @NonNull CancellationSignal cancellationSignal,
                 @NonNull Consumer<ExecuteAppFunctionResponse> callback);
     }
@@ -92,6 +91,7 @@
             @Override
             public void executeAppFunction(
                     @NonNull ExecuteAppFunctionRequest request,
+                    @NonNull String callingPackage,
                     @NonNull ICancellationCallback cancellationCallback,
                     @NonNull IExecuteAppFunctionCallback callback) {
                 if (context.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
@@ -103,6 +103,7 @@
                 try {
                     onExecuteFunction.perform(
                             request,
+                            callingPackage,
                             buildCancellationSignal(cancellationCallback),
                             safeCallback::onResult);
                 } catch (Exception ex) {
@@ -128,12 +129,11 @@
             throw e.rethrowFromSystemServer();
         }
 
-        return cancellationSignal ;
+        return cancellationSignal;
     }
 
-    private final Binder mBinder = createBinder(
-            AppFunctionService.this,
-            AppFunctionService.this::onExecuteFunction);
+    private final Binder mBinder =
+            createBinder(AppFunctionService.this, AppFunctionService.this::onExecuteFunction);
 
     @NonNull
     @Override
@@ -141,40 +141,6 @@
         return mBinder;
     }
 
-
-    /**
-     * Called by the system to execute a specific app function.
-     *
-     * <p>This method is triggered when the system requests your AppFunctionService to handle a
-     * particular function you have registered and made available.
-     *
-     * <p>To ensure proper routing of function requests, assign a unique identifier to each
-     * function. This identifier doesn't need to be globally unique, but it must be unique within
-     * your app. For example, a function to order food could be identified as "orderFood". In most
-     * cases this identifier should come from the ID automatically generated by the AppFunctions
-     * SDK. You can determine the specific function to invoke by calling {@link
-     * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
-     *
-     * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
-     * thread and dispatch the result with the given callback. You should always report back the
-     * result using the callback, no matter if the execution was successful or not.
-     *
-     * @param request The function execution request.
-     * @param callback A callback to report back the result.
-     *
-     * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal,
-     *     Consumer)} instead. This method will be removed once usage references are updated.
-     */
-    @MainThread
-    @Deprecated
-    public void onExecuteFunction(
-            @NonNull ExecuteAppFunctionRequest request,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
-        Log.w(
-                "AppFunctionService",
-                "Calling deprecated default implementation of onExecuteFunction");
-    }
-
     /**
      * Called by the system to execute a specific app function.
      *
@@ -196,14 +162,14 @@
      * the execution of function if requested by the system.
      *
      * @param request The function execution request.
+     * @param callingPackage The package name of the app that is requesting the execution.
      * @param cancellationSignal A signal to cancel the execution.
      * @param callback A callback to report back the result.
      */
     @MainThread
-    public void onExecuteFunction(
+    public abstract void onExecuteFunction(
             @NonNull ExecuteAppFunctionRequest request,
+            @NonNull String callingPackage,
             @NonNull CancellationSignal cancellationSignal,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
-        onExecuteFunction(request, callback);
-    }
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback);
 }
diff --git a/core/java/android/app/appfunctions/IAppFunctionService.aidl b/core/java/android/app/appfunctions/IAppFunctionService.aidl
index 291f33c..bf935d2 100644
--- a/core/java/android/app/appfunctions/IAppFunctionService.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionService.aidl
@@ -34,11 +34,13 @@
      * Called by the system to execute a specific app function.
      *
      * @param request  the function execution request.
+     * @param callingPackage The package name of the app that is requesting the execution.
      * @param cancellationCallback a callback to send back the cancellation transport.
      * @param callback a callback to report back the result.
      */
     void executeAppFunction(
         in ExecuteAppFunctionRequest request,
+        in String callingPackage,
         in ICancellationCallback cancellationCallback,
         in IExecuteAppFunctionCallback callback
     );
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 508077e..1af2437 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1,5 +1,6 @@
 package android.app.assist;
 
+import static android.app.assist.flags.Flags.addPlaceholderViewForNullChild;
 import static android.credentials.Constants.FAILURE_CREDMAN_SELECTOR;
 import static android.credentials.Constants.SUCCESS_CREDMAN_SELECTOR;
 import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
@@ -284,12 +285,18 @@
             mCurViewStackEntry = entry;
         }
 
-        void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) {
+        void writeView(@Nullable ViewNode child, Parcel out, PooledStringWriter pwriter,
+            int levelAdj) {
             if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
                     + ", windows=" + mNumWrittenWindows
                     + ", views=" + mNumWrittenViews
                     + ", level=" + (mCurViewStackPos+levelAdj));
             out.writeInt(VALIDATE_VIEW_TOKEN);
+            if (addPlaceholderViewForNullChild() && child == null) {
+                if (DEBUG_PARCEL_TREE) Log.d(TAG, "Detected an empty child"
+                            + "; writing a placeholder for the child.");
+                child = new ViewNode();
+            }
             int flags = child.writeSelfToParcel(out, pwriter, mSanitizeOnWrite,
                     mTmpMatrix, /*willWriteChildren=*/true);
             mNumWrittenViews++;
@@ -2545,7 +2552,7 @@
             ensureData();
         }
         Log.i(TAG, "Task id: " + mTaskId);
-        Log.i(TAG, "Activity: " + (mActivityComponent != null 
+        Log.i(TAG, "Activity: " + (mActivityComponent != null
                 ? mActivityComponent.flattenToShortString()
                 : null));
         Log.i(TAG, "Sanitize on write: " + mSanitizeOnWrite);
diff --git a/core/java/android/app/assist/flags.aconfig b/core/java/android/app/assist/flags.aconfig
new file mode 100644
index 0000000..bf0aeac
--- /dev/null
+++ b/core/java/android/app/assist/flags.aconfig
@@ -0,0 +1,13 @@
+package: "android.app.assist.flags"
+container: "system"
+
+flag {
+  name: "add_placeholder_view_for_null_child"
+  namespace: "machine_learning"
+  description: "Flag to add a placeholder view when a child view is null."
+  bug: "369503426"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java
index cfbe741..3438cc8 100644
--- a/core/java/android/app/contextualsearch/ContextualSearchManager.java
+++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java
@@ -102,6 +102,7 @@
      * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
      */
     public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
+
     /**
      * Intent action for contextual search invocation. The app providing the contextual search
      * experience must add this intent filter action to the activity it wants to be launched.
@@ -111,6 +112,14 @@
     public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH =
             "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH";
 
+    /**
+     * System feature declaring that the device supports Contextual Search.
+     *
+     * @hide
+     */
+    public static final String FEATURE_CONTEXTUAL_SEARCH =
+            "com.google.android.feature.CONTEXTUAL_SEARCH";
+
     /** Entrypoint to be used when a user long presses on the nav handle. */
     public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1;
     /** Entrypoint to be used when a user long presses on the home button. */
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index b139017..37fa9a26 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -6,6 +6,14 @@
 # flag relates to.
 
 flag {
+  name: "notifications_redesign_app_icons"
+  namespace: "systemui"
+  description: "Notifications Redesign: Use app icons in notification rows (not to be confused with"
+    " notifications_use_app_icons, notifications_use_app_icon_in_row which are just experiments)."
+  bug: "371174789"
+}
+
+flag {
   name: "modes_api"
   is_exported: true
   namespace: "systemui"
@@ -48,6 +56,16 @@
 }
 
 flag {
+  name: "modes_hsum"
+  namespace: "systemui"
+  description: "Fixes for modes (and DND/Zen in general) with HSUM or secondary users"
+  bug: "366203070"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "api_tvextender"
   is_exported: true
   namespace: "systemui"
@@ -251,7 +269,7 @@
   name: "api_rich_ongoing"
   is_exported: true
   namespace: "systemui"
-  description: "Guards new android.app.richongoingnotification api"
+  description: "[RONs] Guards new RON-related APIs, including Notification.ProgressStyle"
   bug: "337261753"
 }
 
@@ -259,6 +277,13 @@
   name: "ui_rich_ongoing"
   is_exported: true
   namespace: "systemui"
-  description: "Guards new android.app.richongoingnotification promotion and new uis"
-  bug: "337261753"
+  description: "[RONs] Guards new promotion logic and UI, including AOD notification and Colorization"
+  bug: "367705002"
 }
+
+flag {
+  name: "backup_restore_logging"
+  namespace: "systemui"
+  description: "Adds logging for notification/modes backup and restore events"
+  bug: "289524803"
+}
\ No newline at end of file
diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java
index c0f13ca..ecf4cd1 100644
--- a/core/java/android/app/usage/UsageEventsQuery.java
+++ b/core/java/android/app/usage/UsageEventsQuery.java
@@ -75,7 +75,7 @@
     }
 
     /**
-     * Returns the exclusive timpstamp to indicate the end of the range of events.
+     * Returns the exclusive timestamp to indicate the end of the range of events.
      * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
      */
     public @CurrentTimeMillisLong long getEndTimeMillis() {
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index b4b96e2..1249734 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -22,6 +22,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
+import android.graphics.drawable.Icon;
 import android.net.MacAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -86,6 +87,11 @@
     private final int mSystemDataSyncFlags;
 
     /**
+     * A device icon displayed on a selfManaged association dialog.
+     */
+    private final Icon mDeviceIcon;
+
+    /**
      * Creates a new Association.
      *
      * @hide
@@ -95,7 +101,7 @@
             @Nullable CharSequence displayName, @Nullable String deviceProfile,
             @Nullable AssociatedDevice associatedDevice, boolean selfManaged,
             boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs,
-            long lastTimeConnectedMs, int systemDataSyncFlags) {
+            long lastTimeConnectedMs, int systemDataSyncFlags, @Nullable Icon deviceIcon) {
         if (id <= 0) {
             throw new IllegalArgumentException("Association ID should be greater than 0");
         }
@@ -119,6 +125,7 @@
         mTimeApprovedMs = timeApprovedMs;
         mLastTimeConnectedMs = lastTimeConnectedMs;
         mSystemDataSyncFlags = systemDataSyncFlags;
+        mDeviceIcon = deviceIcon;
     }
 
     /**
@@ -278,6 +285,20 @@
     }
 
     /**
+     * Get the device icon of the associated device. The device icon represents the device type.
+     *
+     * @return the device icon, or {@code null} if no device icon has been set for the
+     * associated device.
+     *
+     * @see AssociationRequest.Builder#setDeviceIcon(Icon)
+     */
+    @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
+    @Nullable
+    public Icon getDeviceIcon() {
+        return mDeviceIcon;
+    }
+
+    /**
      * Utility method for checking if the association represents a device with the given MAC
      * address.
      *
@@ -370,14 +391,16 @@
                 && Objects.equals(mDisplayName, that.mDisplayName)
                 && Objects.equals(mDeviceProfile, that.mDeviceProfile)
                 && Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
-                && mSystemDataSyncFlags == that.mSystemDataSyncFlags;
+                && mSystemDataSyncFlags == that.mSystemDataSyncFlags
+                && (mDeviceIcon == null ? that.mDeviceIcon == null
+                : mDeviceIcon.sameAs(that.mDeviceIcon));
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mUserId, mPackageName, mTag, mDeviceMacAddress, mDisplayName,
                 mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
-                mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags);
+                mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon);
     }
 
     @Override
@@ -402,6 +425,12 @@
         dest.writeLong(mTimeApprovedMs);
         dest.writeLong(mLastTimeConnectedMs);
         dest.writeInt(mSystemDataSyncFlags);
+        if (mDeviceIcon != null) {
+            dest.writeInt(1);
+            mDeviceIcon.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
     }
 
     private AssociationInfo(@NonNull Parcel in) {
@@ -420,6 +449,11 @@
         mTimeApprovedMs = in.readLong();
         mLastTimeConnectedMs = in.readLong();
         mSystemDataSyncFlags = in.readInt();
+        if (in.readInt() == 1) {
+            mDeviceIcon = Icon.CREATOR.createFromParcel(in);
+        } else {
+            mDeviceIcon = null;
+        }
     }
 
     @NonNull
@@ -459,6 +493,7 @@
         private long mTimeApprovedMs;
         private long mLastTimeConnectedMs;
         private int mSystemDataSyncFlags;
+        private Icon mDeviceIcon;
 
         /** @hide */
         @TestApi
@@ -486,6 +521,7 @@
             mTimeApprovedMs = info.mTimeApprovedMs;
             mLastTimeConnectedMs = info.mLastTimeConnectedMs;
             mSystemDataSyncFlags = info.mSystemDataSyncFlags;
+            mDeviceIcon = info.mDeviceIcon;
         }
 
         /**
@@ -510,6 +546,7 @@
             mTimeApprovedMs = info.mTimeApprovedMs;
             mLastTimeConnectedMs = info.mLastTimeConnectedMs;
             mSystemDataSyncFlags = info.mSystemDataSyncFlags;
+            mDeviceIcon = info.mDeviceIcon;
         }
 
         /** @hide */
@@ -625,6 +662,16 @@
         /** @hide */
         @TestApi
         @NonNull
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
+        public Builder setDeviceIcon(@Nullable Icon deviceIcon) {
+            mDeviceIcon = deviceIcon;
+            return this;
+        }
+
+        /** @hide */
+        @TestApi
+        @NonNull
         public AssociationInfo build() {
             if (mId <= 0) {
                 throw new IllegalArgumentException("Association ID should be greater than 0");
@@ -648,7 +695,8 @@
                     mPending,
                     mTimeApprovedMs,
                     mLastTimeConnectedMs,
-                    mSystemDataSyncFlags
+                    mSystemDataSyncFlags,
+                    mDeviceIcon
             );
         }
     }
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 2e969f8..f368935 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -23,12 +23,14 @@
 import static java.util.Objects.requireNonNull;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
 import android.annotation.UserIdInt;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.drawable.Icon;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -234,6 +236,13 @@
     private boolean mSkipPrompt;
 
     /**
+     * The device icon displayed in selfManaged association dialog.
+     * @hide
+     */
+    @Nullable
+    private Icon mDeviceIcon;
+
+    /**
      * Creates a new AssociationRequest.
      *
      * @param singleDevice
@@ -258,15 +267,16 @@
             @Nullable @DeviceProfile String deviceProfile,
             @Nullable CharSequence displayName,
             boolean selfManaged,
-            boolean forceConfirmation) {
+            boolean forceConfirmation,
+            @Nullable Icon deviceIcon) {
         mSingleDevice = singleDevice;
         mDeviceFilters = requireNonNull(deviceFilters);
         mDeviceProfile = deviceProfile;
         mDisplayName = displayName;
         mSelfManaged = selfManaged;
         mForceConfirmation = forceConfirmation;
-
         mCreationTime = System.currentTimeMillis();
+        mDeviceIcon = deviceIcon;
     }
 
     /**
@@ -318,6 +328,19 @@
         return mSingleDevice;
     }
 
+    /**
+     * Get the device icon of the self-managed association request.
+     *
+     * @return the device icon, or {@code null} if no device icon has been set.
+     *
+     * @see Builder#setDeviceIcon(Icon)
+     */
+    @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
+    @Nullable
+    public Icon getDeviceIcon() {
+        return mDeviceIcon;
+    }
+
     /** @hide */
     public void setPackageName(@NonNull String packageName) {
         mPackageName = packageName;
@@ -365,6 +388,7 @@
         private CharSequence mDisplayName;
         private boolean mSelfManaged = false;
         private boolean mForceConfirmation = false;
+        private Icon mDeviceIcon = null;
 
         public Builder() {}
 
@@ -450,6 +474,23 @@
             return this;
         }
 
+        /**
+         * Set the device icon for the self-managed device and to display the icon in the
+         * self-managed association dialog.
+         *
+         * @throws IllegalArgumentException if the icon is not exactly 24dp by 24dp
+         * or if it is {@link Icon#TYPE_URI} or {@link Icon#TYPE_URI_ADAPTIVE_BITMAP}.
+         * @see #setSelfManaged(boolean)
+         */
+        @NonNull
+        @RequiresPermission(REQUEST_COMPANION_SELF_MANAGED)
+        @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
+        public Builder setDeviceIcon(@NonNull Icon deviceIcon) {
+            checkNotUsed();
+            mDeviceIcon = requireNonNull(deviceIcon);
+            return this;
+        }
+
         /** @inheritDoc */
         @NonNull
         @Override
@@ -460,7 +501,7 @@
                         + "provide the display name of the device");
             }
             return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters),
-                    mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation);
+                    mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mDeviceIcon);
         }
     }
 
@@ -561,7 +602,9 @@
                 && Objects.equals(mDeviceProfilePrivilegesDescription,
                         that.mDeviceProfilePrivilegesDescription)
                 && mCreationTime == that.mCreationTime
-                && mSkipPrompt == that.mSkipPrompt;
+                && mSkipPrompt == that.mSkipPrompt
+                && (mDeviceIcon == null ? that.mDeviceIcon == null
+                : mDeviceIcon.sameAs(that.mDeviceIcon));
     }
 
     @Override
@@ -579,6 +622,8 @@
         _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
         _hash = 31 * _hash + Long.hashCode(mCreationTime);
         _hash = 31 * _hash + Boolean.hashCode(mSkipPrompt);
+        _hash = 31 * _hash + Objects.hashCode(mDeviceIcon);
+
         return _hash;
     }
 
@@ -606,6 +651,12 @@
             dest.writeString8(mDeviceProfilePrivilegesDescription);
         }
         dest.writeLong(mCreationTime);
+        if (mDeviceIcon != null) {
+            dest.writeInt(1);
+            mDeviceIcon.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
     }
 
     @Override
@@ -650,6 +701,11 @@
         this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
         this.mCreationTime = creationTime;
         this.mSkipPrompt = skipPrompt;
+        if (in.readInt() == 1) {
+            mDeviceIcon = Icon.CREATOR.createFromParcel(in);
+        } else {
+            mDeviceIcon = null;
+        }
     }
 
     @NonNull
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 1cdf3b1..4472c3d 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -20,6 +20,8 @@
 import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION;
 import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER;
 import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH;
+import static android.graphics.drawable.Icon.TYPE_URI;
+import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
 
 
 import android.annotation.CallbackExecutor;
@@ -49,6 +51,11 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.VectorDrawable;
 import android.net.MacAddress;
 import android.os.Binder;
 import android.os.Handler;
@@ -471,6 +478,15 @@
         Objects.requireNonNull(callback, "Callback cannot be null");
         handler = Handler.mainIfNull(handler);
 
+        if (Flags.associationDeviceIcon()) {
+            final Icon deviceIcon = request.getDeviceIcon();
+
+            if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
+                throw new IllegalArgumentException("The size of the device icon must be "
+                        + "24dp x 24dp to ensure proper display");
+            }
+        }
+
         try {
             mService.associate(request, new AssociationRequestCallbackProxy(handler, callback),
                     mContext.getOpPackageName(), mContext.getUserId());
@@ -535,6 +551,15 @@
         Objects.requireNonNull(executor, "Executor cannot be null");
         Objects.requireNonNull(callback, "Callback cannot be null");
 
+        if (Flags.associationDeviceIcon()) {
+            final Icon deviceIcon = request.getDeviceIcon();
+
+            if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
+                throw new IllegalArgumentException("The size of the device icon must be "
+                        + "24dp x 24dp to ensure proper display");
+            }
+        }
+
         try {
             mService.associate(request, new AssociationRequestCallbackProxy(executor, callback),
                     mContext.getOpPackageName(), mContext.getUserId());
@@ -2027,4 +2052,34 @@
             }
         }
     }
+
+    private boolean isValidIcon(Icon icon, Context context) {
+        if (icon.getType() == TYPE_URI_ADAPTIVE_BITMAP || icon.getType() == TYPE_URI) {
+            throw new IllegalArgumentException("The URI based Icon is not supported.");
+        }
+        Drawable drawable = icon.loadDrawable(context);
+        float density = context.getResources().getDisplayMetrics().density;
+
+        if (drawable instanceof BitmapDrawable) {
+            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+
+            float widthDp = bitmap.getWidth() / density;
+            float heightDp = bitmap.getHeight() / density;
+
+            if (widthDp != 24 || heightDp != 24) {
+                return false;
+            }
+        } else if (drawable instanceof VectorDrawable) {
+            VectorDrawable vectorDrawable = (VectorDrawable) drawable;
+            float widthDp = vectorDrawable.getIntrinsicWidth() / density;
+            float heightDp = vectorDrawable.getIntrinsicHeight() / density;
+
+            if (widthDp != 24 || heightDp != 24) {
+                return false;
+            }
+        } else {
+            throw new IllegalArgumentException("The format of the device icon is unsupported.");
+        }
+        return true;
+    }
 }
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index 93d62cf..2539a12 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -55,3 +55,11 @@
     description: "Enable association failure code API"
     bug: "331459560"
 }
+
+flag {
+    name: "association_device_icon"
+    is_exported: true
+    namespace: "companion"
+    description: "Enable set device icon API"
+    bug: "341057668"
+}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 40debe8..d3a1c25 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -98,191 +98,160 @@
     /*
      * Turns off all trusted non-mirror displays of the virtual device.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void goToSleep();
 
     /**
      * Turns on all trusted non-mirror displays of the virtual device.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void wakeUp();
 
     /**
      * Closes the virtual device and frees all associated resources.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void close();
 
     /**
      * Specifies a policy for this virtual device.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void setDevicePolicy(int policyType, int devicePolicy);
 
     /**
      * Adds an exemption to the default activity launch policy.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void addActivityPolicyExemption(in ActivityPolicyExemption exemption);
 
     /**
      * Removes an exemption to the default activity launch policy.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void removeActivityPolicyExemption(in ActivityPolicyExemption exemption);
 
     /**
      * Specifies a policy for this virtual device on the given display.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void setDevicePolicyForDisplay(int displayId, int policyType, int devicePolicy);
 
     /**
      * Notifies that an audio session being started.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void onAudioSessionStarting(int displayId, IAudioRoutingCallback routingCallback,
             IAudioConfigChangedCallback configChangedCallback);
 
     /**
      * Notifies that an audio session has ended.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void onAudioSessionEnded();
 
     /**
      * Creates a virtual display and registers it with the display framework.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
             in IVirtualDisplayCallback callback);
 
     /**
      * Creates a new dpad and registers it with the input framework with the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void createVirtualDpad(in VirtualDpadConfig config, IBinder token);
 
     /**
      * Creates a new keyboard and registers it with the input framework with the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void createVirtualKeyboard(in VirtualKeyboardConfig config, IBinder token);
 
     /**
      * Creates a new mouse and registers it with the input framework with the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void createVirtualMouse(in VirtualMouseConfig config, IBinder token);
 
     /**
      * Creates a new touchscreen and registers it with the input framework with the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void createVirtualTouchscreen(in VirtualTouchscreenConfig config, IBinder token);
 
     /**
      * Creates a new navigation touchpad and registers it with the input framework with the given
      * token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void createVirtualNavigationTouchpad(in VirtualNavigationTouchpadConfig config, IBinder token);
 
     /**
      * Creates a new stylus and registers it with the input framework with the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void createVirtualStylus(in VirtualStylusConfig config, IBinder token);
 
     /**
      * Creates a new rotary encoder and registers it with the input framework with the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void createVirtualRotaryEncoder(in VirtualRotaryEncoderConfig config, IBinder token);
 
     /**
      * Removes the input device corresponding to the given token from the framework.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void unregisterInputDevice(IBinder token);
 
     /**
      * Returns the ID of the device corresponding to the given token, as registered with the input
      * framework.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     int getInputDeviceId(IBinder token);
 
     /**
      * Injects a key event to the virtual dpad corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event);
 
     /**
      * Injects a key event to the virtual keyboard corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event);
 
     /**
      * Injects a button event to the virtual mouse corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event);
 
     /**
      * Injects a relative event to the virtual mouse corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
 
     /**
      * Injects a scroll event to the virtual mouse corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
 
     /**
     * Injects a touch event to the virtual touch input device corresponding to the given token.
     */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
 
     /**
      * Injects a motion event from the virtual stylus input device corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendStylusMotionEvent(IBinder token, in VirtualStylusMotionEvent event);
 
     /**
      * Injects a button event from the virtual stylus input device corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendStylusButtonEvent(IBinder token, in VirtualStylusButtonEvent event);
 
     /**
      * Injects a scroll event from the virtual rotary encoder corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendRotaryEncoderScrollEvent(IBinder token, in VirtualRotaryEncoderScrollEvent event);
 
     /**
      * Returns all virtual sensors created for this device.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     List<VirtualSensor> getVirtualSensorList();
 
     /**
      * Sends an event to the virtual sensor corresponding to the given token.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event);
 
     /**
      * Launches a pending intent on the given display that is owned by this virtual device.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
             in ResultReceiver resultReceiver);
 
@@ -290,15 +259,12 @@
      * Returns the current cursor position of the mouse corresponding to the given token, in x and y
      * coordinates.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     PointF getCursorPosition(IBinder token);
 
     /** Sets whether to show or hide the cursor while this virtual device is active. */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void setShowPointerIcon(boolean showPointerIcon);
 
     /** Sets an IME policy for the given display. */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void setDisplayImePolicy(int displayId, int policy);
 
     /**
@@ -306,33 +272,28 @@
      * when matching the provided IntentFilter and calls the callback with the intercepted
      * intent.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void registerIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor,
             in IntentFilter filter);
 
     /**
      * Unregisters a previously registered intent interceptor.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
 
     /**
      * Creates a new virtual camera and registers it with the virtual camera service.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void registerVirtualCamera(in VirtualCameraConfig camera);
 
     /**
      * Destroys the virtual camera with given config and unregisters it from the virtual camera
      * service.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void unregisterVirtualCamera(in VirtualCameraConfig camera);
 
     /**
      * Returns the id of the virtual camera with given config.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     String getVirtualCameraId(in VirtualCameraConfig camera);
 
     /**
@@ -342,7 +303,6 @@
      * This is needed for virtual devices that are created by the system, as the VirtualDeviceImpl
      * object is created before the returned VirtualDeviceInternal one.
      */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void setListeners(in IVirtualDeviceActivityListener activityListener,
             in IVirtualDeviceSoundEffectListener soundEffectListener);
 }
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 6708cce..d63a443 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -26,7 +26,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.companion.virtual.audio.VirtualAudioDevice;
@@ -242,7 +241,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     List<VirtualSensor> getVirtualSensorList() {
         try {
@@ -252,7 +250,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void goToSleep() {
         try {
             mVirtualDevice.goToSleep();
@@ -261,7 +258,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void wakeUp() {
         try {
             mVirtualDevice.wakeUp();
@@ -270,7 +266,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void launchPendingIntent(
             int displayId,
             @NonNull PendingIntent pendingIntent,
@@ -292,7 +287,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @Nullable
     VirtualDisplay createVirtualDisplay(
             @NonNull VirtualDisplayConfig config,
@@ -310,7 +304,6 @@
         return displayManager.createVirtualDisplayWrapper(config, callbackWrapper, displayId);
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void close() {
         try {
             // This also takes care of unregistering all virtual sensors.
@@ -324,7 +317,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
         switch (policyType) {
@@ -344,7 +336,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void addActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         try {
             mVirtualDevice.addActivityPolicyExemption(exemption);
@@ -353,7 +344,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void removeActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         try {
             mVirtualDevice.removeActivityPolicyExemption(exemption);
@@ -362,7 +352,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setDevicePolicyForDisplay(int displayId,
             @VirtualDeviceParams.DynamicDisplayPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
@@ -382,7 +371,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
         try {
@@ -395,7 +383,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) {
         try {
@@ -408,7 +395,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) {
         try {
@@ -421,7 +407,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualTouchscreen createVirtualTouchscreen(
             @NonNull VirtualTouchscreenConfig config) {
@@ -435,7 +420,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualStylus createVirtualStylus(@NonNull VirtualStylusConfig config) {
         try {
@@ -448,7 +432,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull VirtualRotaryEncoderConfig config) {
         try {
@@ -461,7 +444,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualNavigationTouchpad createVirtualNavigationTouchpad(
             @NonNull VirtualNavigationTouchpadConfig config) {
@@ -476,7 +458,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualAudioDevice createVirtualAudioDevice(
             @NonNull VirtualDisplay display,
@@ -501,7 +482,6 @@
         return mVirtualAudioDevice;
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @NonNull
     VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
         try {
@@ -513,7 +493,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setShowPointerIcon(boolean showPointerIcon) {
         try {
             mVirtualDevice.setShowPointerIcon(showPointerIcon);
@@ -522,7 +501,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
         try {
             mVirtualDevice.setDisplayImePolicy(displayId, policy);
@@ -564,7 +542,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void registerIntentInterceptor(
             @NonNull IntentFilter interceptorFilter,
             @CallbackExecutor @NonNull Executor executor,
@@ -584,7 +561,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void unregisterIntentInterceptor(
             @NonNull VirtualDeviceManager.IntentInterceptorCallback interceptorCallback) {
         Objects.requireNonNull(interceptorCallback);
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 96700a9..6ea7834 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -614,7 +614,6 @@
          *
          * @return A list of all sensors for this device, or an empty list if no sensors exist.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public List<VirtualSensor> getVirtualSensorList() {
             return mVirtualDeviceInternal.getVirtualSensorList();
@@ -635,7 +634,6 @@
          * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
          */
         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void goToSleep() {
             mVirtualDeviceInternal.goToSleep();
         }
@@ -654,7 +652,6 @@
          * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
          */
         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void wakeUp() {
             mVirtualDeviceInternal.wakeUp();
         }
@@ -677,7 +674,6 @@
          *   on the virtual display, or one of the {@code LAUNCH_FAILED} status explaining why it
          *   failed.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void launchPendingIntent(
                 int displayId,
                 @NonNull PendingIntent pendingIntent,
@@ -718,7 +714,6 @@
          * VirtualDisplay.Callback)}
          */
         @Deprecated
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @Nullable
         public VirtualDisplay createVirtualDisplay(
                 @IntRange(from = 1) int width,
@@ -756,7 +751,6 @@
          *
          * @see DisplayManager#createVirtualDisplay
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @Nullable
         public VirtualDisplay createVirtualDisplay(
                 @NonNull VirtualDisplayConfig config,
@@ -770,7 +764,6 @@
          * Closes the virtual device, stopping and tearing down any virtual displays, associated
          * virtual audio device, and event injection that's currently in progress.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void close() {
             mVirtualDeviceInternal.close();
         }
@@ -789,7 +782,6 @@
          * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
          */
         @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
                 @VirtualDeviceParams.DevicePolicy int devicePolicy) {
             mVirtualDeviceInternal.setDevicePolicy(policyType, devicePolicy);
@@ -812,7 +804,6 @@
          * @see #setDevicePolicy
          */
         @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void addActivityPolicyExemption(@NonNull ComponentName componentName) {
             addActivityPolicyExemption(new ActivityPolicyExemption.Builder()
                     .setComponentName(componentName)
@@ -836,7 +827,6 @@
          * @see #setDevicePolicy
          */
         @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
             removeActivityPolicyExemption(new ActivityPolicyExemption.Builder()
                     .setComponentName(componentName)
@@ -861,7 +851,6 @@
          * @see #setDevicePolicy
          */
         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void addActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
             mVirtualDeviceInternal.addActivityPolicyExemption(Objects.requireNonNull(exemption));
         }
@@ -877,7 +866,6 @@
          * @see #setDevicePolicy
          */
         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void removeActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
             mVirtualDeviceInternal.removeActivityPolicyExemption(Objects.requireNonNull(exemption));
         }
@@ -900,7 +888,6 @@
          * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
          */
         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void setDevicePolicy(
                 @VirtualDeviceParams.DynamicDisplayPolicyType int policyType,
                 @VirtualDeviceParams.DevicePolicy int devicePolicy,
@@ -913,7 +900,6 @@
          *
          * @param config the configurations of the virtual dpad.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
             Objects.requireNonNull(config, "config must not be null");
@@ -925,7 +911,6 @@
          *
          * @param config the configurations of the virtual keyboard.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualKeyboardConfig config) {
             Objects.requireNonNull(config, "config must not be null");
@@ -943,7 +928,6 @@
          * @deprecated Use {@link #createVirtualKeyboard(VirtualKeyboardConfig config)} instead
          */
         @Deprecated
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualKeyboard createVirtualKeyboard(@NonNull VirtualDisplay display,
                 @NonNull String inputDeviceName, int vendorId, int productId) {
@@ -962,7 +946,6 @@
          *
          * @param config the configurations of the virtual mouse.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualMouse createVirtualMouse(@NonNull VirtualMouseConfig config) {
             Objects.requireNonNull(config, "config must not be null");
@@ -980,7 +963,6 @@
          * @deprecated Use {@link #createVirtualMouse(VirtualMouseConfig config)} instead
          */
         @Deprecated
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualMouse createVirtualMouse(@NonNull VirtualDisplay display,
                 @NonNull String inputDeviceName, int vendorId, int productId) {
@@ -999,7 +981,6 @@
          *
          * @param config the configurations of the virtual touchscreen.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualTouchscreen createVirtualTouchscreen(
                 @NonNull VirtualTouchscreenConfig config) {
@@ -1019,7 +1000,6 @@
          * instead
          */
         @Deprecated
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualTouchscreen createVirtualTouchscreen(@NonNull VirtualDisplay display,
                 @NonNull String inputDeviceName, int vendorId, int productId) {
@@ -1046,7 +1026,6 @@
          * @param config the configurations of the virtual navigation touchpad.
          * @see android.view.InputDevice#SOURCE_TOUCH_NAVIGATION
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualNavigationTouchpad createVirtualNavigationTouchpad(
                 @NonNull VirtualNavigationTouchpadConfig config) {
@@ -1058,7 +1037,6 @@
          *
          * @param config the touchscreen configurations for the virtual stylus.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         @FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS)
         public VirtualStylus createVirtualStylus(
@@ -1072,7 +1050,6 @@
          * @param config the configuration for the virtual rotary encoder.
          * @see android.view.InputDevice#SOURCE_ROTARY_ENCODER
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_VIRTUAL_ROTARY)
         public VirtualRotaryEncoder createVirtualRotaryEncoder(
@@ -1100,7 +1077,6 @@
          *   applications running on virtual display is changed.
          * @return A {@link VirtualAudioDevice} instance.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         public VirtualAudioDevice createVirtualAudioDevice(
                 @NonNull VirtualDisplay display,
@@ -1121,7 +1097,6 @@
          * @throws UnsupportedOperationException if virtual camera isn't supported on this device.
          * @see VirtualDeviceParams#POLICY_TYPE_CAMERA
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
         @FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA)
         public VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
@@ -1135,10 +1110,12 @@
         /**
          * Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
          *
+         * <p>Only applicable to trusted displays.</p>
+         *
          * @param showPointerIcon True if the pointer should be shown; false otherwise. The default
          *   visibility is true.
+         * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void setShowPointerIcon(boolean showPointerIcon) {
             mVirtualDeviceInternal.setShowPointerIcon(showPointerIcon);
         }
@@ -1153,7 +1130,6 @@
          * @throws SecurityException if the display is not owned by this device or is not
          *                           {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED trusted}
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME)
         public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
             if (Flags.vdmCustomIme()) {
@@ -1217,7 +1193,6 @@
          * is intercepted.
          * @see #unregisterIntentInterceptor(IntentInterceptorCallback)
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void registerIntentInterceptor(
                 @NonNull IntentFilter interceptorFilter,
                 @CallbackExecutor @NonNull Executor executor,
@@ -1230,7 +1205,6 @@
          * Unregisters the intent interceptor previously registered with
          * {@link #registerIntentInterceptor}.
          */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void unregisterIntentInterceptor(
                     @NonNull IntentInterceptorCallback interceptorCallback) {
             mVirtualDeviceInternal.unregisterIntentInterceptor(interceptorCallback);
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 03b72bd..65f9cbe 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -159,7 +159,7 @@
      * @hide
      */
     @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS, POLICY_TYPE_AUDIO,
-            POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CAMERA,
+            POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CLIPBOARD, POLICY_TYPE_CAMERA,
             POLICY_TYPE_BLOCKED_ACTIVITY})
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@@ -220,11 +220,16 @@
      * Tells the activity manager how to handle recents entries for activities run on this device.
      *
      * <ul>
-     *     <li>{@link #DEVICE_POLICY_DEFAULT}: Activities launched on VirtualDisplays owned by this
+     *     <li>{@link #DEVICE_POLICY_DEFAULT}: Activities launched on trusted displays owned by this
      *     device will appear in the host device recents.
-     *     <li>{@link #DEVICE_POLICY_CUSTOM}: Activities launched on VirtualDisplays owned by this
+     *     <li>{@link #DEVICE_POLICY_CUSTOM}: Activities launched on trusted displays owned by this
      *     device will not appear in recents.
      * </ul>
+     *
+     * <p>Activities launched on untrusted displays will always show in the host device recents,
+     * regardless of the policy.</p>
+     *
+     * @see android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED
      */
     public static final int POLICY_TYPE_RECENTS = 2;
 
@@ -254,8 +259,10 @@
      *     not shared with other devices' clipboards, including the clipboard of the default device.
      *     <li>{@link #DEVICE_POLICY_CUSTOM}: The device's clipboard is shared with the default
      *     device's clipboard. Any clipboard operation on the virtual device is as if it was done on
-     *     the default device.
+     *     the default device. Requires all displays of the virtual device to be trusted.
      * </ul>
+     *
+     * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
      */
     @FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD)
     public static final int POLICY_TYPE_CLIPBOARD = 4;
@@ -821,8 +828,8 @@
         }
 
         /**
-         * Specifies a component to be used as input method on all displays owned by this virtual
-         * device.
+         * Specifies a component to be used as input method on all trusted displays owned by this
+         * virtual device.
          *
          * @param inputMethodComponent The component name to be used as input method. Must comply to
          *   all general input method requirements described in the guide to
@@ -831,6 +838,7 @@
          *   may interact with the virtual device, then there will effectively be no IME on this
          *   device's displays for that user.
          *
+         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
          * @see android.inputmethodservice.InputMethodService
          * @attr ref android.R.styleable#InputMethod_isVirtualDeviceOnly
          * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker
diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java
index f727589..ece048d 100644
--- a/core/java/android/companion/virtual/camera/VirtualCamera.java
+++ b/core/java/android/companion/virtual/camera/VirtualCamera.java
@@ -17,7 +17,6 @@
 package android.companion.virtual.camera;
 
 import android.annotation.FlaggedApi;
-import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -94,7 +93,6 @@
     }
 
     @Override
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void close() {
         try {
             mVirtualDevice.unregisterVirtualCamera(mConfig);
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 37e494b..934a1a8 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -17,7 +17,6 @@
 package android.companion.virtual.sensor;
 
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -136,7 +135,6 @@
     /**
      * Send a sensor event to the system.
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendEvent(@NonNull VirtualSensorEvent event) {
         try {
             mVirtualDevice.sendSensorEvent(mToken, event);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 91f7a8b..07106e8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4325,6 +4325,7 @@
            //@hide: ECM_ENHANCED_CONFIRMATION_SERVICE,
             CONTACT_KEYS_SERVICE,
             RANGING_SERVICE,
+            ADVANCED_PROTECTION_SERVICE,
 
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -4769,6 +4770,18 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.security.keystore.KeyStoreManager} for accessing
+     * <a href="/privacy-and-security/keystore">Android Keystore</a>
+     * functions.
+     *
+     * @see #getSystemService(String)
+     * @see android.security.keystore.KeyStoreManager
+     */
+    @FlaggedApi(android.security.Flags.FLAG_KEYSTORE_GRANT_API)
+    public static final String KEYSTORE_SERVICE = "keystore";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.os.storage.StorageManager} for accessing system storage
      * functions.
      *
@@ -5658,6 +5671,14 @@
     public static final String BINARY_TRANSPARENCY_SERVICE = "transparency";
 
     /**
+     * System service name for ForensicService.
+     * The service manages the forensic info on device.
+     * @hide
+     */
+    @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+    public static final String FORENSIC_SERVICE = "forensic";
+
+    /**
      * System service name for the DeviceIdleManager.
      * @see #getSystemService(String)
      * @hide
@@ -6368,6 +6389,15 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.security.advancedprotection.AdvancedProtectionManager}
+     * @see #getSystemService(String)
+     * @see android.security.advancedprotection.AdvancedProtectionManager
+     */
+    @FlaggedApi(android.security.Flags.FLAG_AAPM_API)
+    public static final String ADVANCED_PROTECTION_SERVICE = "advanced_protection";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
      * {@link android.security.FileIntegrityManager}.
      * @see #getSystemService(String)
      * @see android.security.FileIntegrityManager
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0bb0027..f719528 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -888,6 +888,22 @@
     public static final String ACTION_ACTIVITY_RECOGNIZER =
             "android.intent.action.ACTIVITY_RECOGNIZER";
 
+    /** @hide */
+    public static void maybeMarkAsMissingCreatorToken(Object object) {
+        if (object instanceof Intent intent) {
+            maybeMarkAsMissingCreatorTokenInternal(intent);
+        }
+    }
+
+    private static void maybeMarkAsMissingCreatorTokenInternal(Intent intent) {
+        boolean isForeign = (intent.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0;
+        boolean isWithoutTrustedCreatorToken =
+                (intent.mLocalFlags & Intent.LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT) == 0;
+        if (isForeign && isWithoutTrustedCreatorToken) {
+            intent.addExtendedFlags(EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN);
+        }
+    }
+
     /**
      * Represents a shortcut/live folder icon resource.
      *
@@ -7684,10 +7700,8 @@
 
     /**
      * This flag indicates the creator token of this intent has been verified.
-     *
-     * @hide
      */
-    public static final int LOCAL_FLAG_CREATOR_TOKEN_VERIFIED = 1 << 6;
+    private static final int LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT = 1 << 6;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = {
@@ -12243,6 +12257,30 @@
         }
     }
 
+    /** @hide */
+    public void checkCreatorToken() {
+        if (mExtras == null) return;
+        if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) {
+            for (String key : mCreatorTokenInfo.mExtraIntentKeys) {
+                try {
+                    Intent extraIntent = mExtras.getParcelable(key, Intent.class);
+                    if (extraIntent == null) {
+                        Log.w(TAG, "The key {" + key
+                                + "} does not correspond to an intent in the bundle.");
+                        continue;
+                    }
+                    extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
+                } catch (Exception e) {
+                    Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e);
+                }
+            }
+        }
+        // mark the bundle as intent extras after calls to getParcelable.
+        // otherwise, the logic to mark missing token would run before
+        // mark trusted creator token present.
+        mExtras.setIsIntentExtra();
+    }
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeString8(mAction);
         Uri.writeToParcel(out, mData);
@@ -12730,6 +12768,7 @@
         }
 
         mLocalFlags |= localFlags;
+        checkCreatorToken();
 
         // Special attribution fix-up logic for any BluetoothDevice extras
         // passed via Bluetooth intents
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 6d9dc45..392f62a 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -1,11 +1,11 @@
 # Remain no owner because multiple modules may touch this file.
 per-file Context.java = *
 per-file ContextWrapper.java = *
-per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS
-per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Content* = varunshah@google.com, yamasani@google.com
+per-file *Sync* = file:/apex/jobscheduler/JOB_OWNERS
 per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
 per-file UriRelativeFilter* = file:/PACKAGE_MANAGER_OWNERS
-per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
+per-file IntentFilter.java = file:/INTENT_OWNERS
 per-file Intent.java = file:/INTENT_OWNERS
 per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
 per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 481e6b5..ce52825 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -21,10 +21,12 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.Activity;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.Overridable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -1204,6 +1206,18 @@
     public @interface SizeChangesSupportMode {}
 
     /**
+     * This change id makes the restriction of fixed orientation, aspect ratio, and resizability
+     * of the app to be ignored, which means making the app fill the given available area.
+     * @hide
+     */
+    @ChangeId
+    @Overridable
+    @TestApi
+    @SuppressLint("UnflaggedApi") // @TestApi without associated public API.
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // buganizer id
+
+    /**
      * This change id enables compat policy that ignores app requested orientation in
      * response to an app calling {@link android.app.Activity#setRequestedOrientation}. See
      * com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation for
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 34bea1a..cccfdb0 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2334,9 +2334,8 @@
      * Whether an app allows its playback audio to be captured by other apps.
      *
      * @return {@code true} if the app indicates that its audio can be captured by other apps.
-     *
-     * @hide
      */
+    @FlaggedApi(Flags.FLAG_AUDIO_PLAYBACK_CAPTURE_ALLOWANCE)
     public boolean isAudioPlaybackCaptureAllowed() {
         return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0;
     }
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 451c0e5..c911326 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -93,4 +93,10 @@
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
     void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    int getVerificationPolicy();
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    boolean setVerificationPolicy(int policy);
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c673d58..5da1444 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -62,6 +62,8 @@
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.pm.verify.domain.DomainSet;
+import android.content.pm.verify.pkg.VerificationSession;
+import android.content.pm.verify.pkg.VerificationStatus;
 import android.graphics.Bitmap;
 import android.icu.util.ULocale;
 import android.net.Uri;
@@ -418,6 +420,21 @@
     public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
 
     /**
+     * When verification is blocked as part of the installation, additional reason for the block
+     * will be provided to the installer with a {@link VerificationFailedReason} as part of the
+     * installation result returned via the {@link IntentSender} in
+     * {@link Session#commit(IntentSender)}. This extra is provided only when the installation has
+     * failed. Installers can use this extra to check if the installation failure was caused by a
+     * verification failure.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final String EXTRA_VERIFICATION_FAILURE_REASON =
+            "android.content.pm.extra.VERIFICATION_FAILURE_REASON";
+
+    /**
      * Streaming installation pending.
      * Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
      *
@@ -760,6 +777,90 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface UnarchivalStatus {}
 
+    /**
+     * Verification failed because of unknown reasons, such as when the verifier times out or cannot
+     * be connected. It can also corresponds to the status of
+     * {@link VerificationSession#VERIFICATION_INCOMPLETE_UNKNOWN} reported by the verifier via
+     * {@link VerificationSession#reportVerificationIncomplete(int)}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0;
+
+    /**
+     * Verification failed because the network is unavailable. This corresponds to the status of
+     * {@link VerificationSession#VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE} reported by the
+     * verifier via {@link VerificationSession#reportVerificationIncomplete(int)}.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1;
+
+    /**
+     * Verification failed because the package is blocked, as reported by the verifier via
+     * {@link VerificationSession#reportVerificationComplete(VerificationStatus)} or
+     * {@link VerificationSession#reportVerificationComplete(VerificationStatus, PersistableBundle)}
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(value = {
+            VERIFICATION_FAILED_REASON_UNKNOWN,
+            VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE,
+            VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED,
+    })
+    public @interface VerificationFailedReason {
+    }
+
+    /**
+     * Do not block installs, regardless of verification status.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_NONE = 0; // platform default
+    /**
+     * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1;
+    /**
+     * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED} and ask the
+     * user if they'd like to install anyway when the verification is blocked for other reason.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2;
+    /**
+     * Block installations whose verification status is blocked for any reason.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3;
+    /**
+     * @hide
+     */
+    @IntDef(value = {
+            VERIFICATION_POLICY_NONE,
+            VERIFICATION_POLICY_BLOCK_FAIL_OPEN,
+            VERIFICATION_POLICY_BLOCK_FAIL_WARN,
+            VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VerificationPolicy {
+    }
 
     /** Default set of checksums - includes all available checksums.
      * @see Session#requestChecksums  */
@@ -1503,6 +1604,40 @@
     }
 
     /**
+     * Return the current verification enforcement policy. This may only be called by the
+     * package currently set by the system as the verifier agent.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public final @VerificationPolicy int getVerificationPolicy() {
+        try {
+            return mInstaller.getVerificationPolicy();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the current verification enforcement policy which will be applied to all the future
+     * installation sessions. This may only be called by the package currently set by the system as
+     * the verifier agent.
+     * @hide
+     * @return whether the new policy was successfully set.
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public final boolean setVerificationPolicy(@VerificationPolicy int policy) {
+        try {
+            return mInstaller.setVerificationPolicy(policy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * An installation that is being actively staged. For an install to succeed,
      * all existing and new packages must have identical package names, version
      * codes, and signing certificates.
@@ -2799,6 +2934,8 @@
         public int unarchiveId = -1;
         /** {@hide} */
         public @Nullable String dexoptCompilerFilter = null;
+        /** {@hide} */
+        public boolean forceVerification;
 
         private final ArrayMap<String, Integer> mPermissionStates;
 
@@ -2853,6 +2990,7 @@
             developmentInstallFlags = source.readInt();
             unarchiveId = source.readInt();
             dexoptCompilerFilter = source.readString();
+            forceVerification = source.readBoolean();
         }
 
         /** {@hide} */
@@ -2889,6 +3027,7 @@
             ret.developmentInstallFlags = developmentInstallFlags;
             ret.unarchiveId = unarchiveId;
             ret.dexoptCompilerFilter = dexoptCompilerFilter;
+            ret.forceVerification = forceVerification;
             return ret;
         }
 
@@ -3597,6 +3736,14 @@
             return grantedPermissions.toArray(ArrayUtils.emptyArray(String.class));
         }
 
+        /**
+         * Used by adb installations to force enable the verification for this install.
+         * {@hide}
+         */
+        public void setForceVerification() {
+            this.forceVerification = true;
+        }
+
         /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
@@ -3632,6 +3779,7 @@
             pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
             pw.printPair("unarchiveId", unarchiveId);
             pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter);
+            pw.printPair("forceVerification", forceVerification);
             pw.println();
         }
 
@@ -3678,6 +3826,7 @@
             dest.writeInt(developmentInstallFlags);
             dest.writeInt(unarchiveId);
             dest.writeString(dexoptCompilerFilter);
+            dest.writeBoolean(forceVerification);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index d77b2f5..f7191e6 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -209,6 +209,24 @@
     }
 
     /**
+     * @hide
+     * @param name
+     * @param versionMajor
+     */
+    public SharedLibraryInfo(String name, long versionMajor, int type) {
+        mPath = null;
+        mPackageName = null;
+        mName = name;
+        mVersion = versionMajor;
+        mType = type;
+        mDeclaringPackage = null;
+        mDependentPackages = null;
+        mDependencies = null;
+        mIsNative = false;
+        mOptionalDependentPackages = null;
+    }
+
+    /**
      * Gets the type of this library.
      *
      * @return The library type.
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 300740e..5b38942 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -288,6 +288,15 @@
 }
 
 flag {
+    name: "audio_playback_capture_allowance"
+    is_exported: true
+    namespace: "package_manager_service"
+    description: "Feature flag to enable the feature to retrieve info about audio playback capture allowance at manifest level."
+    bug: "362425551"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "get_packages_from_launcher_apps"
     namespace: "package_manager_service"
     description: "Feature flag to provide the new methods within launcher apps class to get packages."
@@ -317,3 +326,19 @@
     bug: "360129103"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "include_feature_flags_in_package_cacher"
+    namespace: "package_manager_service"
+    description: "Include feature flag status when determining hits or misses in PackageCacher."
+    bug: "364771256"
+    is_fixed_read_only: true
+}
+
+flag {
+    name: "reduce_broadcasts_for_component_state_changes"
+    namespace: "package_manager_service"
+    description: "Feature flag to limit sending of the PACKAGE_CHANGED broadcast to only the system and the application itself during component state changes."
+    bug: "292261144"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index fa26837..35f9cff 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -47,13 +47,6 @@
 }
 
 flag {
-    name: "start_user_before_scheduled_alarms"
-    namespace: "multiuser"
-    description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
-    bug: "314907186"
-}
-
-flag {
     name: "add_ui_for_sounds_from_background_users"
     namespace: "multiuser"
     description: "Allow foreground user to dismiss sounds that are coming from background users"
@@ -75,6 +68,16 @@
 }
 
 flag {
+    name: "multiple_alarm_notifications_support"
+    namespace: "multiuser"
+    description: "Implement handling of multiple simultaneous alarms/timers on bg users"
+    bug: "367615180"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
     name: "enable_biometrics_to_unlock_private_space"
     is_exported: true
     namespace: "profile_experiences"
@@ -198,46 +201,6 @@
 }
 
 flag {
-    name: "cache_profile_parent"
-    namespace: "multiuser"
-    description: "Cache getProfileParent to avoid unnecessary binder calls"
-    bug: "350417399"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-    name: "cache_profile_ids"
-    namespace: "multiuser"
-    description: "Cache getProfileIds to avoid unnecessary binder calls"
-    bug: "350421409"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-    name: "cache_profile_type"
-    namespace: "multiuser"
-    description: "Cache getProfileType to avoid unnecessary binder calls"
-    bug: "350417403"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-    name: "cache_profiles"
-    namespace: "multiuser"
-    description: "Cache getProfiles to avoid unnecessary binder calls"
-    bug: "350419395"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
     name: "fix_disabling_of_mu_toggle_when_restriction_applied"
     namespace: "multiuser"
     description: "When no_user_switch is set but no EnforcedAdmin is present, the toggle has to be disabled"
@@ -248,6 +211,62 @@
 }
 
 flag {
+    name: "property_invalidated_cache_bypass_mismatched_uids"
+    namespace: "multiuser"
+    description: "Bypass the cache when the process UID does not match the binder UID."
+    bug: "373752556"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+
+flag {
+    name: "cache_profile_parent_read_only"
+    namespace: "multiuser"
+    description: "Cache getProfileParent to avoid unnecessary binder calls"
+    bug: "350417399"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+flag {
+    name: "cache_profile_ids_read_only"
+    namespace: "multiuser"
+    description: "Cache getProfileIds to avoid unnecessary binder calls"
+    bug: "350421409"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+flag {
+    name: "cache_profile_type_read_only"
+    namespace: "multiuser"
+    description: "Cache getProfileType to avoid unnecessary binder calls"
+    bug: "350417403"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+flag {
+    name: "cache_profiles_read_only"
+    namespace: "multiuser"
+    description: "Cache getProfiles to avoid unnecessary binder calls"
+    bug: "350419395"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+flag {
     name: "cache_quiet_mode_state"
     namespace: "multiuser"
     description: "Optimise quiet mode state retrieval"
@@ -290,6 +309,17 @@
 }
 
 flag {
+    name: "invalidate_cache_on_users_changed_read_only"
+    namespace: "multiuser"
+    description: "Invalidate the cache when users are added or removed to improve caches."
+    bug: "372383485"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+flag {
     name: "caches_not_invalidated_at_start_read_only"
     namespace: "multiuser"
     description: "PIC need to be invalidated at start in order to work properly."
@@ -503,3 +533,13 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "ignore_restrictions_when_deleting_private_profile"
+  namespace: "multiuser"
+  description: "Ignore any user restrictions when deleting private profiles."
+  bug: "350953833"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 74ce62c..19a13db 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -21,6 +21,7 @@
 import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
 
@@ -149,6 +150,8 @@
      */
     private final @Nullable String mEmergencyInstaller;
 
+    private final @NonNull List<SharedLibraryInfo> mDeclaredLibraries;
+
     /**
      * Archival install info.
      */
@@ -165,7 +168,7 @@
             int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
             Set<String> requiredSplitTypes, Set<String> splitTypes,
             boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem,
-            String emergencyInstaller) {
+            String emergencyInstaller, List<SharedLibraryInfo> declaredLibraries) {
         mPath = path;
         mPackageName = packageName;
         mSplitName = splitName;
@@ -202,6 +205,7 @@
         mUpdatableSystem = updatableSystem;
         mEmergencyInstaller = emergencyInstaller;
         mArchivedPackage = null;
+        mDeclaredLibraries = declaredLibraries;
     }
 
     public ApkLite(String path, ArchivedPackageParcel archivedPackage) {
@@ -241,6 +245,7 @@
         mUpdatableSystem = true;
         mEmergencyInstaller = null;
         mArchivedPackage = archivedPackage;
+        mDeclaredLibraries = null;
     }
 
     /**
@@ -565,6 +570,11 @@
         return mEmergencyInstaller;
     }
 
+    @DataClass.Generated.Member
+    public @NonNull List<SharedLibraryInfo> getDeclaredLibraries() {
+        return mDeclaredLibraries;
+    }
+
     /**
      * Archival install info.
      */
@@ -574,10 +584,10 @@
     }
 
     @DataClass.Generated(
-            time = 1706896661616L,
+            time = 1728333566322L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final  int mRollbackDataPolicy\nprivate final  boolean mHasDeviceAdminReceiver\nprivate final  boolean mIsSdkLibrary\nprivate final  boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final  int mRollbackDataPolicy\nprivate final  boolean mHasDeviceAdminReceiver\nprivate final  boolean mIsSdkLibrary\nprivate final  boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index ffb69c0..1a7f628 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -24,6 +24,7 @@
 import android.app.admin.DeviceAdminReceiver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
 import android.content.pm.parsing.result.ParseInput;
@@ -92,6 +93,8 @@
     private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
     private static final String TAG_PROCESSES = "processes";
     private static final String TAG_PROCESS = "process";
+    private static final String TAG_STATIC_LIBRARY = "static-library";
+    private static final String TAG_LIBRARY = "library";
 
     /**
      * Parse only lightweight details about the package at the given location.
@@ -457,6 +460,7 @@
         boolean hasDeviceAdminReceiver = false;
 
         boolean isSdkLibrary = false;
+        List<SharedLibraryInfo> declaredLibraries = new ArrayList<>();
 
         // Only search the tree when the tag is the direct child of <manifest> tag
         int type;
@@ -521,6 +525,51 @@
                             break;
                         case TAG_SDK_LIBRARY:
                             isSdkLibrary = true;
+                            // Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full
+                            // parsing are combined
+                            String sdkLibName = parser.getAttributeValue(
+                                    ANDROID_RES_NAMESPACE, "name");
+                            int sdkLibVersionMajor = parser.getAttributeIntValue(
+                                        ANDROID_RES_NAMESPACE, "versionMajor", -1);
+                            if (sdkLibName == null || sdkLibVersionMajor < 0) {
+                                return input.error(
+                                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                        "Bad uses-sdk-library declaration name: " + sdkLibName
+                                                + " version: " + sdkLibVersionMajor);
+                            }
+                            declaredLibraries.add(new SharedLibraryInfo(
+                                    sdkLibName, sdkLibVersionMajor,
+                                    SharedLibraryInfo.TYPE_SDK_PACKAGE));
+                            break;
+                        case TAG_STATIC_LIBRARY:
+                            // Mirrors ParsingPackageUtils#parseStaticLibrary until lite and full
+                            // parsing are combined
+                            String staticLibName = parser.getAttributeValue(
+                                    ANDROID_RES_NAMESPACE, "name");
+                            int staticLibVersion = parser.getAttributeIntValue(
+                                    ANDROID_RES_NAMESPACE, "version", -1);
+                            int staticLibVersionMajor = parser.getAttributeIntValue(
+                                    ANDROID_RES_NAMESPACE, "versionMajor", 0);
+                            if (staticLibName == null || staticLibVersion < 0) {
+                                return input.error("Bad static-library declaration name: "
+                                        + staticLibName + " version: " + staticLibVersion);
+                            }
+                            declaredLibraries.add(new SharedLibraryInfo(staticLibName,
+                                    PackageInfo.composeLongVersionCode(staticLibVersionMajor,
+                                            staticLibVersion), SharedLibraryInfo.TYPE_STATIC));
+                            break;
+                        case TAG_LIBRARY:
+                            // Mirrors ParsingPackageUtils#parseLibrary until lite and full parsing
+                            // are combined
+                            String libName = parser.getAttributeValue(
+                                    ANDROID_RES_NAMESPACE, "name");
+                            if (libName == null) {
+                                return input.error("Bad library declaration name: null");
+                            }
+                            libName = libName.intern();
+                            declaredLibraries.add(new SharedLibraryInfo(libName,
+                                    SharedLibraryInfo.VERSION_UNDEFINED,
+                                    SharedLibraryInfo.TYPE_DYNAMIC));
                             break;
                         case TAG_PROCESSES:
                             final int processesDepth = parser.getDepth();
@@ -645,7 +694,8 @@
                         overlayIsStatic, overlayPriority, requiredSystemPropertyName,
                         requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
                         rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
-                        hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller));
+                        hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller,
+                        declaredLibraries));
     }
 
     private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 116dd1f..9a2ee7f 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.PackageInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningDetails;
 import android.content.pm.VerifierInfo;
 
@@ -114,6 +115,8 @@
      */
     private final boolean mIsSdkLibrary;
 
+    private final @NonNull List<SharedLibraryInfo> mDeclaredLibraries;
+
     /**
      * Archival install info.
      */
@@ -154,6 +157,7 @@
         mSplitApkPaths = splitApkPaths;
         mSplitRevisionCodes = splitRevisionCodes;
         mTargetSdk = targetSdk;
+        mDeclaredLibraries = baseApk.getDeclaredLibraries();
         mArchivedPackage = baseApk.getArchivedPackage();
     }
 
@@ -433,6 +437,11 @@
         return mIsSdkLibrary;
     }
 
+    @DataClass.Generated.Member
+    public @NonNull List<SharedLibraryInfo> getDeclaredLibraries() {
+        return mDeclaredLibraries;
+    }
+
     /**
      * Archival install info.
      */
@@ -442,10 +451,10 @@
     }
 
     @DataClass.Generated(
-            time = 1694792176268L,
+            time = 1728333569917L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
-            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mTargetSdk\nprivate final  int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final  int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mProfileableByShell\nprivate final  boolean mUseEmbeddedDex\nprivate final  boolean mIsSdkLibrary\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  java.util.List<java.lang.String> getAllApkPaths()\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mTargetSdk\nprivate final  int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final  int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mProfileableByShell\nprivate final  boolean mUseEmbeddedDex\nprivate final  boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic  java.util.List<java.lang.String> getAllApkPaths()\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionCallback.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionCallback.aidl
deleted file mode 100644
index 38a7956..0000000
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionCallback.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm.verify.pkg;
-
-import android.content.pm.verify.pkg.VerificationStatus;
-import android.os.PersistableBundle;
-
-/**
- * Oneway interface that allows the verifier to send response or verification results back to
- * the system.
- * @hide
- */
-oneway interface IVerificationSessionCallback {
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
-    void reportVerificationIncomplete(int verificationId, int reason);
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
-    void reportVerificationComplete(int verificationId, in VerificationStatus status);
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
-    void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response);
-}
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
index 7a9484a..66caf2d 100644
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
+++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
@@ -16,8 +16,11 @@
 
 package android.content.pm.verify.pkg;
 
+import android.content.pm.verify.pkg.VerificationStatus;
+import android.os.PersistableBundle;
+
 /**
- * Non-oneway interface that allows the verifier to retrieve information from the system.
+ * Non-oneway interface that allows the verifier to communicate with the system.
  * @hide
  */
 interface IVerificationSessionInterface {
@@ -25,4 +28,12 @@
     long getTimeoutTime(int verificationId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
     long extendTimeRemaining(int verificationId, long additionalMs);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    boolean setVerificationPolicy(int verificationId, int policy);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    void reportVerificationIncomplete(int verificationId, int reason);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    void reportVerificationComplete(int verificationId, in VerificationStatus status);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response);
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java
index 70b4a02..4ade211 100644
--- a/core/java/android/content/pm/verify/pkg/VerificationSession.java
+++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java
@@ -22,6 +22,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.pm.Flags;
+import android.content.pm.PackageInstaller;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningInfo;
 import android.net.Uri;
@@ -52,17 +53,12 @@
      * The verification cannot be completed because the network is unavailable.
      */
     public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1;
-    /**
-     * The verification cannot be completed because the network is limited.
-     */
-    public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2;
 
     /**
      * @hide
      */
     @IntDef(prefix = {"VERIFICATION_INCOMPLETE_"}, value = {
             VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE,
-            VERIFICATION_INCOMPLETE_NETWORK_LIMITED,
             VERIFICATION_INCOMPLETE_UNKNOWN,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -83,8 +79,15 @@
     private final PersistableBundle mExtensionParams;
     @NonNull
     private final IVerificationSessionInterface mSession;
-    @NonNull
-    private final IVerificationSessionCallback mCallback;
+    /**
+     * The current policy that is active for the session. It might not be
+     * the same as the original policy that was initially assigned for this verification session,
+     * because the active policy can be overridden by {@link #setVerificationPolicy(int)}.
+     * <p>To improve the latency, store the original policy value and any changes made to it,
+     * so that {@link #getVerificationPolicy()} does not need to make a binder call to retrieve the
+     * currently active policy.</p>
+     */
+    private volatile @PackageInstaller.VerificationPolicy int mVerificationPolicy;
 
     /**
      * Constructor used by the system to describe the details of a verification session.
@@ -94,8 +97,8 @@
             @NonNull Uri stagedPackageUri, @NonNull SigningInfo signingInfo,
             @NonNull List<SharedLibraryInfo> declaredLibraries,
             @NonNull PersistableBundle extensionParams,
-            @NonNull IVerificationSessionInterface session,
-            @NonNull IVerificationSessionCallback callback) {
+            @PackageInstaller.VerificationPolicy int defaultPolicy,
+            @NonNull IVerificationSessionInterface session) {
         mId = id;
         mInstallSessionId = installSessionId;
         mPackageName = packageName;
@@ -103,8 +106,8 @@
         mSigningInfo = signingInfo;
         mDeclaredLibraries = declaredLibraries;
         mExtensionParams = extensionParams;
+        mVerificationPolicy = defaultPolicy;
         mSession = session;
-        mCallback = callback;
     }
 
     /**
@@ -144,7 +147,7 @@
 
     /**
      * Returns a mapping of any shared libraries declared in the manifest
-     * to the {@link SharedLibraryInfo#Type} that is declared. This will be an empty
+     * to the {@link SharedLibraryInfo.Type} that is declared. This will be an empty
      * map if no shared libraries are declared by the package.
      */
     @NonNull
@@ -174,6 +177,39 @@
     }
 
     /**
+     * Return the current policy that is active for this session.
+     * <p>If the policy for this session has been changed by {@link #setVerificationPolicy},
+     * the return value of this method is the current policy that is active for this session.
+     * Otherwise, the return value is the same as the initial policy that was assigned to the
+     * session when it was first created.</p>
+     */
+    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+        return mVerificationPolicy;
+    }
+
+    /**
+     * Override the verification policy for this session.
+     * @return True if the override was successful, False otherwise.
+     */
+    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+        if (mVerificationPolicy == policy) {
+            // No effective policy change
+            return true;
+        }
+        try {
+            if (mSession.setVerificationPolicy(mId, policy)) {
+                mVerificationPolicy = policy;
+                return true;
+            } else {
+                return false;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Extend the timeout for this session by the provided additionalMs to
      * fetch relevant information over the network or wait for the network.
      * This may be called multiple times. If the request would bypass any max
@@ -196,7 +232,7 @@
     @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public void reportVerificationIncomplete(@VerificationIncompleteReason int reason) {
         try {
-            mCallback.reportVerificationIncomplete(mId, reason);
+            mSession.reportVerificationIncomplete(mId, reason);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -210,7 +246,7 @@
     @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
     public void reportVerificationComplete(@NonNull VerificationStatus status) {
         try {
-            mCallback.reportVerificationComplete(mId, status);
+            mSession.reportVerificationComplete(mId, status);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -225,7 +261,7 @@
     public void reportVerificationComplete(@NonNull VerificationStatus status,
             @NonNull PersistableBundle response) {
         try {
-            mCallback.reportVerificationCompleteWithExtensionResponse(mId, status, response);
+            mSession.reportVerificationCompleteWithExtensionResponse(mId, status, response);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -239,8 +275,8 @@
         mSigningInfo = SigningInfo.CREATOR.createFromParcel(in);
         mDeclaredLibraries = in.createTypedArrayList(SharedLibraryInfo.CREATOR);
         mExtensionParams = in.readPersistableBundle(getClass().getClassLoader());
+        mVerificationPolicy = in.readInt();
         mSession = IVerificationSessionInterface.Stub.asInterface(in.readStrongBinder());
-        mCallback = IVerificationSessionCallback.Stub.asInterface(in.readStrongBinder());
     }
 
     @Override
@@ -257,8 +293,8 @@
         mSigningInfo.writeToParcel(dest, flags);
         dest.writeTypedList(mDeclaredLibraries);
         dest.writePersistableBundle(mExtensionParams);
+        dest.writeInt(mVerificationPolicy);
         dest.writeStrongBinder(mSession.asBinder());
-        dest.writeStrongBinder(mCallback.asBinder());
     }
 
     @NonNull
diff --git a/core/java/android/content/pm/xr.aconfig b/core/java/android/content/pm/xr.aconfig
new file mode 100644
index 0000000..61835c1
--- /dev/null
+++ b/core/java/android/content/pm/xr.aconfig
@@ -0,0 +1,9 @@
+package: "android.xr"
+container: "system"
+
+flag {
+    namespace: "xr"
+    name: "xr_manifest_entries"
+    description: "Adds manifest entries used by Android XR"
+    bug: "364416355"
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/DataSpace.java b/core/java/android/hardware/DataSpace.java
index 312bfdf..6117384 100644
--- a/core/java/android/hardware/DataSpace.java
+++ b/core/java/android/hardware/DataSpace.java
@@ -15,9 +15,12 @@
  */
 package android.hardware;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.view.SurfaceControl;
 
+import com.android.graphics.flags.Flags;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -639,6 +642,18 @@
      */
     public static final int DATASPACE_SRGB_LINEAR = 138477568;
 
+    /**
+     * Display BT. 2020 encoding.
+     *
+     * <p>Composed of the following -</p>
+     * <pre>
+     *   Primaries: STANDARD_BT2020
+     *   Transfer: TRANSFER_SRGB
+     *   Range: RANGE_FULL</pre>
+     */
+    @FlaggedApi(Flags.FLAG_DISPLAY_BT2020_COLORSPACE)
+    public static final int DATASPACE_DISPLAY_BT2020 = 142999552;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {
@@ -660,7 +675,8 @@
         DATASPACE_BT2020,
         DATASPACE_BT709,
         DATASPACE_DCI_P3,
-        DATASPACE_SRGB_LINEAR
+        DATASPACE_SRGB_LINEAR,
+        DATASPACE_DISPLAY_BT2020
     })
     public @interface NamedDataSpace {};
 
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 8c87ad3..9395844 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -283,7 +283,7 @@
     private static NativeAllocationRegistry getRegistry(long size) {
         final long func = nGetNativeFinalizer();
         final Class cls = HardwareBuffer.class;
-        return com.android.libcore.Flags.nativeMetrics()
+        return com.android.libcore.readonly.Flags.nativeMetrics()
             ? NativeAllocationRegistry.createNonmalloced(cls, func, size)
             : NativeAllocationRegistry.createNonmalloced(cls.getClassLoader(), func, size);
     }
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index a69a371..acb48f3 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2270,7 +2270,17 @@
      * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored. The
      * application has control over the various
      * android.flash.* fields.</p>
+     * <p>If the device supports manual flash strength control, i.e.,
+     * if {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, then
+     * the auto-exposure (AE) precapture metering sequence should be
+     * triggered for the configured flash mode and strength to avoid
+     * the image being incorrectly exposed at different
+     * {@link CaptureRequest#FLASH_STRENGTH_LEVEL android.flash.strengthLevel}.</p>
      *
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL
+     * @see CaptureRequest#FLASH_STRENGTH_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @see CaptureRequest#SENSOR_SENSITIVITY
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3f5ae91..a193ee1 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1358,6 +1358,13 @@
      * camera device auto-exposure routine for the overridden
      * fields for a given capture will be available in its
      * CaptureResult.</p>
+     * <p>When {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is AE_MODE_ON and if the device
+     * supports manual flash strength control, i.e.,
+     * if {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, then
+     * the auto-exposure (AE) precapture metering sequence should be
+     * triggered to avoid the image being incorrectly exposed at
+     * different {@link CaptureRequest#FLASH_STRENGTH_LEVEL android.flash.strengthLevel}.</p>
      * <p><b>Possible values:</b></p>
      * <ul>
      *   <li>{@link #CONTROL_AE_MODE_OFF OFF}</li>
@@ -1373,9 +1380,13 @@
      * <p>This key is available on all devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES
+     * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_MODE
      * @see CameraCharacteristics#FLASH_INFO_AVAILABLE
      * @see CaptureRequest#FLASH_MODE
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL
+     * @see CaptureRequest#FLASH_STRENGTH_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @see CaptureRequest#SENSOR_SENSITIVITY
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index a18a634..e5ca46a 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -759,6 +759,13 @@
      * camera device auto-exposure routine for the overridden
      * fields for a given capture will be available in its
      * CaptureResult.</p>
+     * <p>When {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is AE_MODE_ON and if the device
+     * supports manual flash strength control, i.e.,
+     * if {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1, then
+     * the auto-exposure (AE) precapture metering sequence should be
+     * triggered to avoid the image being incorrectly exposed at
+     * different {@link CaptureRequest#FLASH_STRENGTH_LEVEL android.flash.strengthLevel}.</p>
      * <p><b>Possible values:</b></p>
      * <ul>
      *   <li>{@link #CONTROL_AE_MODE_OFF OFF}</li>
@@ -774,9 +781,13 @@
      * <p>This key is available on all devices.</p>
      *
      * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES
+     * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_MODE
      * @see CameraCharacteristics#FLASH_INFO_AVAILABLE
      * @see CaptureRequest#FLASH_MODE
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL
+     * @see CaptureRequest#FLASH_STRENGTH_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL
      * @see CaptureRequest#SENSOR_EXPOSURE_TIME
      * @see CaptureRequest#SENSOR_FRAME_DURATION
      * @see CaptureRequest#SENSOR_SENSITIVITY
diff --git a/core/java/android/hardware/devicestate/DeviceStateInfo.java b/core/java/android/hardware/devicestate/DeviceStateInfo.java
index 28561ec..fd6f0b8 100644
--- a/core/java/android/hardware/devicestate/DeviceStateInfo.java
+++ b/core/java/android/hardware/devicestate/DeviceStateInfo.java
@@ -28,7 +28,6 @@
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
-
 /**
  * Information about the state of the device.
  *
@@ -63,11 +62,13 @@
      * ignoring any override requests made through a call to {@link DeviceStateManager#requestState(
      * DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
      */
+    @NonNull
     public final DeviceState baseState;
 
     /**
      * The state of the device.
      */
+    @NonNull
     public final DeviceState currentState;
 
     /**
@@ -78,8 +79,9 @@
      */
     // Using the specific types to avoid virtual method calls in binder transactions
     @SuppressWarnings("NonApiType")
-    public DeviceStateInfo(@NonNull ArrayList<DeviceState> supportedStates, DeviceState baseState,
-            DeviceState state) {
+    public DeviceStateInfo(@NonNull ArrayList<DeviceState> supportedStates,
+            @NonNull DeviceState baseState,
+            @NonNull DeviceState state) {
         this.supportedStates = supportedStates;
         this.baseState = baseState;
         this.currentState = state;
@@ -97,7 +99,7 @@
     public boolean equals(@Nullable Object other) {
         if (this == other) return true;
         if (other == null || (getClass() != other.getClass())) return false;
-        DeviceStateInfo that = (DeviceStateInfo) other;
+        final DeviceStateInfo that = (DeviceStateInfo) other;
         return baseState.equals(that.baseState)
                 &&  currentState.equals(that.currentState)
                 && Objects.equals(supportedStates, that.supportedStates);
@@ -126,8 +128,16 @@
         return diff;
     }
 
+    // Parcelable implementation
+
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Writes to Parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(supportedStates.size());
         for (int i = 0; i < supportedStates.size(); i++) {
             dest.writeTypedObject(supportedStates.get(i).getConfiguration(), flags);
@@ -137,28 +147,27 @@
         dest.writeTypedObject(currentState.getConfiguration(), flags);
     }
 
-    @Override
-    public int describeContents() {
-        return 0;
+    /** Reads from Parcel. */
+    private DeviceStateInfo(@NonNull Parcel in) {
+        final int numberOfSupportedStates = in.readInt();
+        final ArrayList<DeviceState> supportedStates = new ArrayList<>(numberOfSupportedStates);
+        for (int i = 0; i < numberOfSupportedStates; i++) {
+            final DeviceState.Configuration configuration =
+                    Objects.requireNonNull(in.readTypedObject(DeviceState.Configuration.CREATOR));
+            supportedStates.add(i, new DeviceState(configuration));
+        }
+        this.supportedStates = supportedStates;
+
+        this.baseState = new DeviceState(
+                Objects.requireNonNull(in.readTypedObject(DeviceState.Configuration.CREATOR)));
+        this.currentState = new DeviceState(
+                Objects.requireNonNull(in.readTypedObject(DeviceState.Configuration.CREATOR)));
     }
 
     public static final @NonNull Creator<DeviceStateInfo> CREATOR = new Creator<>() {
         @Override
-        public DeviceStateInfo createFromParcel(Parcel source) {
-            final int numberOfSupportedStates = source.readInt();
-            final ArrayList<DeviceState> supportedStates = new ArrayList<>(numberOfSupportedStates);
-            for (int i = 0; i < numberOfSupportedStates; i++) {
-                DeviceState.Configuration configuration = source.readTypedObject(
-                        DeviceState.Configuration.CREATOR);
-                supportedStates.add(i, new DeviceState(configuration));
-            }
-
-            final DeviceState baseState = new DeviceState(
-                    source.readTypedObject(DeviceState.Configuration.CREATOR));
-            final DeviceState currentState = new DeviceState(
-                    source.readTypedObject(DeviceState.Configuration.CREATOR));
-
-            return new DeviceStateInfo(supportedStates, baseState, currentState);
+        public DeviceStateInfo createFromParcel(@NonNull Parcel in) {
+            return new DeviceStateInfo(in);
         }
 
         @Override
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index 0c84019..739dbe1 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -32,6 +32,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.window.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -46,6 +47,8 @@
  */
 @VisibleForTesting(visibility = Visibility.PACKAGE)
 public final class DeviceStateManagerGlobal {
+    @Nullable
+    @GuardedBy("DeviceStateManagerGlobal.class")
     private static DeviceStateManagerGlobal sInstance;
     private static final String TAG = "DeviceStateManagerGlobal";
     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
@@ -58,7 +61,7 @@
     public static DeviceStateManagerGlobal getInstance() {
         synchronized (DeviceStateManagerGlobal.class) {
             if (sInstance == null) {
-                IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
+                final IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
                 if (b != null) {
                     sInstance = new DeviceStateManagerGlobal(IDeviceStateManager
                             .Stub.asInterface(b));
@@ -83,10 +86,12 @@
     @GuardedBy("mLock")
     private DeviceStateInfo mLastReceivedInfo;
 
+    // Constructor should be called while holding the lock.
+    // @GuardedBy("DeviceStateManagerGlobal.class") can't be used on constructors.
     @VisibleForTesting
     public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
         mDeviceStateManager = deviceStateManager;
-        registerCallbackIfNeededLocked();
+        registerCallbackLocked();
     }
 
     /**
@@ -94,6 +99,7 @@
      *
      * @see DeviceStateManager#getSupportedDeviceStates()
      */
+    @NonNull
     public List<DeviceState> getSupportedDeviceStates() {
         synchronized (mLock) {
             final DeviceStateInfo currentInfo;
@@ -126,8 +132,8 @@
             conditional = true)
     public void requestState(@NonNull DeviceStateRequest request,
             @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
-        DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
-                executor);
+        final DeviceStateRequestWrapper requestWrapper =
+                new DeviceStateRequestWrapper(request, callback, executor);
         synchronized (mLock) {
             if (findRequestTokenLocked(request) != null) {
                 // This request has already been submitted.
@@ -136,7 +142,7 @@
             // Add the request wrapper to the mRequests array before requesting the state as the
             // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
             // same process as this instance.
-            IBinder token = new Binder();
+            final IBinder token = new Binder();
             mRequests.put(token, requestWrapper);
 
             try {
@@ -176,8 +182,8 @@
     @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
     public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
             @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
-        DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
-                executor);
+        final DeviceStateRequestWrapper requestWrapper =
+                new DeviceStateRequestWrapper(request, callback, executor);
         synchronized (mLock) {
             if (findRequestTokenLocked(request) != null) {
                 // This request has already been submitted.
@@ -186,7 +192,7 @@
             // Add the request wrapper to the mRequests array before requesting the state as the
             // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
             // same process as this instance.
-            IBinder token = new Binder();
+            final IBinder token = new Binder();
             mRequests.put(token, requestWrapper);
 
             try {
@@ -225,7 +231,7 @@
     public void registerDeviceStateCallback(@NonNull DeviceStateCallback callback,
             @NonNull Executor executor) {
         synchronized (mLock) {
-            int index = findCallbackLocked(callback);
+            final int index = findCallbackLocked(callback);
             if (index != -1) {
                 // This callback is already registered.
                 return;
@@ -233,7 +239,8 @@
             // Add the callback wrapper to the mCallbacks array after registering the callback as
             // the callback could be triggered immediately if the mDeviceStateManager IBinder is in
             // the same process as this instance.
-            DeviceStateCallbackWrapper wrapper = new DeviceStateCallbackWrapper(callback, executor);
+            final DeviceStateCallbackWrapper wrapper =
+                    new DeviceStateCallbackWrapper(callback, executor);
             mCallbacks.add(wrapper);
 
             if (mLastReceivedInfo != null) {
@@ -253,7 +260,7 @@
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public void unregisterDeviceStateCallback(@NonNull DeviceStateCallback callback) {
         synchronized (mLock) {
-            int indexToRemove = findCallbackLocked(callback);
+            final int indexToRemove = findCallbackLocked(callback);
             if (indexToRemove != -1) {
                 mCallbacks.remove(indexToRemove);
             }
@@ -276,15 +283,20 @@
         }
     }
 
-    private void registerCallbackIfNeededLocked() {
-        if (mCallback == null) {
-            mCallback = new DeviceStateManagerCallback();
-            try {
+    @GuardedBy("DeviceStateManagerGlobal.class")
+    private void registerCallbackLocked() {
+        mCallback = new DeviceStateManagerCallback();
+        try {
+            if (Flags.wlinfoOncreate()) {
+                synchronized (mLock) {
+                    mLastReceivedInfo = mDeviceStateManager.registerCallback(mCallback);
+                }
+            } else {
                 mDeviceStateManager.registerCallback(mCallback);
-            } catch (RemoteException ex) {
-                mCallback = null;
-                throw ex.rethrowFromSystemServer();
             }
+        } catch (RemoteException ex) {
+            mCallback = null;
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -298,6 +310,7 @@
     }
 
     @Nullable
+    @GuardedBy("mLock")
     private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
         for (int i = 0; i < mRequests.size(); i++) {
             if (mRequests.valueAt(i).mRequest.equals(request)) {
@@ -309,8 +322,8 @@
 
     /** Handles a call from the server that the device state info has changed. */
     private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) {
-        ArrayList<DeviceStateCallbackWrapper> callbacks;
-        DeviceStateInfo oldInfo;
+        final ArrayList<DeviceStateCallbackWrapper> callbacks;
+        final DeviceStateInfo oldInfo;
         synchronized (mLock) {
             oldInfo = mLastReceivedInfo;
             mLastReceivedInfo = info;
@@ -335,8 +348,8 @@
      * Handles a call from the server that a request for the supplied {@code token} has become
      * active.
      */
-    private void handleRequestActive(IBinder token) {
-        DeviceStateRequestWrapper request;
+    private void handleRequestActive(@NonNull IBinder token) {
+        final DeviceStateRequestWrapper request;
         synchronized (mLock) {
             request = mRequests.get(token);
         }
@@ -349,8 +362,8 @@
      * Handles a call from the server that a request for the supplied {@code token} has become
      * canceled.
      */
-    private void handleRequestCanceled(IBinder token) {
-        DeviceStateRequestWrapper request;
+    private void handleRequestCanceled(@NonNull IBinder token) {
+        final DeviceStateRequestWrapper request;
         synchronized (mLock) {
             request = mRequests.remove(token);
         }
@@ -361,17 +374,17 @@
 
     private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
         @Override
-        public void onDeviceStateInfoChanged(DeviceStateInfo info) {
+        public void onDeviceStateInfoChanged(@NonNull DeviceStateInfo info) {
             handleDeviceStateInfoChanged(info);
         }
 
         @Override
-        public void onRequestActive(IBinder token) {
+        public void onRequestActive(@NonNull IBinder token) {
             handleRequestActive(token);
         }
 
         @Override
-        public void onRequestCanceled(IBinder token) {
+        public void onRequestCanceled(@NonNull IBinder token) {
             handleRequestCanceled(token);
         }
     }
@@ -388,7 +401,8 @@
             mExecutor = executor;
         }
 
-        void notifySupportedDeviceStatesChanged(List<DeviceState> newSupportedDeviceStates) {
+        void notifySupportedDeviceStatesChanged(
+                @NonNull List<DeviceState> newSupportedDeviceStates) {
             mExecutor.execute(() ->
                     mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates));
         }
@@ -398,7 +412,7 @@
                     () -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState));
         }
 
-        private void execute(String traceName, Runnable r) {
+        private void execute(@NonNull String traceName, @NonNull Runnable r) {
             mExecutor.execute(() -> {
                 if (DEBUG) {
                     Trace.beginSection(
@@ -416,6 +430,7 @@
     }
 
     private static final class DeviceStateRequestWrapper {
+        @NonNull
         private final DeviceStateRequest mRequest;
         @Nullable
         private final DeviceStateRequest.Callback mCallback;
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index ea4fe26..50d0623 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -32,16 +32,24 @@
     DeviceStateInfo getDeviceStateInfo();
 
     /**
-     * Registers a callback to receive notifications from the device state manager. Only one
-     * callback can be registered per-process.
+     * Registers a callback to receive notifications from the device state manager and returns the
+     * current {@link DeviceStateInfo}. Only one callback can be registered per-process.
      * <p>
      * As the callback mechanism is used to alert the caller of changes to request status a callback
      * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or
      * {@link #cancelRequest(IBinder)}, otherwise an exception will be thrown.
+     * <p>
+     * Upon successful registration, this method returns the committed {@link DeviceStateInfo} if
+     * available, ensuring the availability of the device state after the callback is registered.
+     * This guarantees that the client will have access to the latest device state immediately upon
+     * completion of the two-way IPC call.
      *
+     * @param callback the callback to register for device state notifications.
+     * @return DeviceStateInfo the current device state information including the committed state
+     *         or null if no state has been committed by the {@link DeviceStateProvider} yet.
      * @throws SecurityException if a callback is already registered for the calling process.
      */
-    void registerCallback(in IDeviceStateManagerCallback callback);
+    @nullable DeviceStateInfo registerCallback(in IDeviceStateManagerCallback callback);
 
     /**
      * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 97f6899..b0ea92d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -18,6 +18,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.HdrCapabilities.HdrType;
+import static android.view.Display.INVALID_DISPLAY;
 
 import android.Manifest;
 import android.annotation.FlaggedApi;
@@ -47,6 +48,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserManager;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
@@ -96,6 +98,8 @@
     @GuardedBy("mLock")
     private final WeakDisplayCache mDisplayCache = new WeakDisplayCache();
 
+    private int mDisplayIdToMirror = INVALID_DISPLAY;
+
     /**
      * Broadcast receiver that indicates when the Wifi display status changes.
      * <p>
@@ -1086,6 +1090,7 @@
         if (surface != null) {
             builder.setSurface(surface);
         }
+        builder.setDisplayIdToMirror(getDisplayIdToMirror());
         return createVirtualDisplay(builder.build(), handler, callback);
     }
 
@@ -1163,6 +1168,7 @@
         if (surface != null) {
             builder.setSurface(surface);
         }
+        builder.setDisplayIdToMirror(getDisplayIdToMirror());
         return createVirtualDisplay(projection, builder.build(), callback, handler);
     }
 
@@ -1708,6 +1714,16 @@
         return mGlobal.getDefaultDozeBrightness(displayId);
     }
 
+    private int getDisplayIdToMirror() {
+        if (mDisplayIdToMirror == INVALID_DISPLAY) {
+            final UserManager userManager = mContext.getSystemService(UserManager.class);
+            mDisplayIdToMirror = userManager.isVisibleBackgroundUsersSupported()
+                    ? userManager.getMainDisplayIdAssignedToUser()
+                    : DEFAULT_DISPLAY;
+        }
+        return mDisplayIdToMirror;
+    }
+
     /**
      * Listens for changes in available display devices.
      */
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 6c1aa90..75ffcc3 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -461,6 +461,16 @@
     public abstract void stylusGestureStarted(long eventTime);
 
     /**
+     * Called by {@link com.android.server.wm.ContentRecorder} to verify whether
+     * the display is allowed to mirror primary display's content.
+     * @param displayId the id of the display where we mirror to.
+     * @return true if the mirroring dialog is confirmed (display is enabled), or
+     * {@link com.android.server.display.ExternalDisplayPolicy#ENABLE_ON_CONNECT}
+     * system property is enabled.
+     */
+    public abstract boolean isDisplayReadyForMirroring(int displayId);
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 177ee6f..897ce4a 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -24,6 +24,8 @@
 import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag;
 import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
 import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
+import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling;
+import static com.android.hardware.input.Flags.mouseSwapPrimaryButton;
 import static com.android.hardware.input.Flags.touchpadTapDragging;
 import static com.android.hardware.input.Flags.touchpadVisualizer;
 import static com.android.input.flags.Flags.enableInputFilterRustImpl;
@@ -363,6 +365,22 @@
     }
 
     /**
+     * Returns true if the feature flag for mouse reverse vertical scrolling is enabled.
+     * @hide
+     */
+    public static boolean isMouseReverseVerticalScrollingFeatureFlagEnabled() {
+        return mouseReverseVerticalScrolling();
+    }
+
+    /**
+     * Returns true if the feature flag for mouse swap primary button is enabled.
+     * @hide
+     */
+    public static boolean isMouseSwapPrimaryButtonFeatureFlagEnabled() {
+        return mouseSwapPrimaryButton();
+    }
+
+    /**
      * Returns true if the touchpad visualizer is allowed to appear.
      *
      * @param context The application context.
@@ -501,6 +519,86 @@
     }
 
     /**
+     * Whether mouse vertical scrolling is enabled, this applies only to connected mice.
+     *
+     * @param context The application context.
+     * @return Whether the mouse will have its vertical scrolling reversed
+     * (scroll down to move up).
+     *
+     * @hide
+     */
+    public static boolean isMouseReverseVerticalScrollingEnabled(@NonNull Context context) {
+        if (!isMouseReverseVerticalScrollingFeatureFlagEnabled()) {
+            return false;
+        }
+
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING, 0, UserHandle.USER_CURRENT)
+                != 0;
+    }
+
+    /**
+     * Sets whether the connected mouse will have its vertical scrolling reversed.
+     *
+     * @param context The application context.
+     * @param reverseScrolling Whether reverse scrolling is enabled.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setMouseReverseVerticalScrolling(@NonNull Context context,
+            boolean reverseScrolling) {
+        if (!isMouseReverseVerticalScrollingFeatureFlagEnabled()) {
+            return;
+        }
+
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING, reverseScrolling ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Whether the primary mouse button is swapped on connected mice.
+     *
+     * @param context The application context.
+     * @return Whether mice will have their primary buttons swapped, so that left clicking will
+     * perform the secondary action (e.g. show menu) and right clicking will perform the primary
+     * action.
+     *
+     * @hide
+     */
+    public static boolean isMouseSwapPrimaryButtonEnabled(@NonNull Context context) {
+        if (!isMouseSwapPrimaryButtonFeatureFlagEnabled()) {
+            return false;
+        }
+
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_SWAP_PRIMARY_BUTTON, 0, UserHandle.USER_CURRENT)
+                != 0;
+    }
+
+    /**
+     * Sets whether mice will have their primary buttons swapped between left and right
+     * clicks.
+     *
+     * @param context The application context.
+     * @param swapPrimaryButton Whether swapping the primary button is enabled.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setMouseSwapPrimaryButton(@NonNull Context context,
+            boolean swapPrimaryButton) {
+        if (!isMouseSwapPrimaryButtonFeatureFlagEnabled()) {
+            return;
+        }
+
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_SWAP_PRIMARY_BUTTON, swapPrimaryButton ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
      * Whether Accessibility bounce keys feature is enabled.
      *
      * <p>
diff --git a/core/java/android/hardware/input/VirtualDpad.java b/core/java/android/hardware/input/VirtualDpad.java
index 5985c39..5b08a0e 100644
--- a/core/java/android/hardware/input/VirtualDpad.java
+++ b/core/java/android/hardware/input/VirtualDpad.java
@@ -17,7 +17,6 @@
 package android.hardware.input;
 
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
 import android.os.IBinder;
@@ -72,7 +71,6 @@
      *
      * @param event the event to send
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendKeyEvent(@NonNull VirtualKeyEvent event) {
         try {
             if (!mSupportedKeyCodes.contains(event.getKeyCode())) {
diff --git a/core/java/android/hardware/input/VirtualInputDevice.java b/core/java/android/hardware/input/VirtualInputDevice.java
index affa4ed..8e4e097 100644
--- a/core/java/android/hardware/input/VirtualInputDevice.java
+++ b/core/java/android/hardware/input/VirtualInputDevice.java
@@ -16,7 +16,6 @@
 
 package android.hardware.input;
 
-import android.annotation.RequiresPermission;
 import android.companion.virtual.IVirtualDevice;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -68,7 +67,6 @@
     }
 
     @Override
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void close() {
         Log.d(TAG, "Closing virtual input device " + mConfig.getInputDeviceName());
         try {
diff --git a/core/java/android/hardware/input/VirtualInputDeviceConfig.java b/core/java/android/hardware/input/VirtualInputDeviceConfig.java
index e8ef8cd..3b74d7f 100644
--- a/core/java/android/hardware/input/VirtualInputDeviceConfig.java
+++ b/core/java/android/hardware/input/VirtualInputDeviceConfig.java
@@ -163,7 +163,6 @@
             return self();
         }
 
-
         /**
          * Sets the product id of the device, uniquely identifying the device within the address
          * space of a given vendor, identified by the device's vendor id.
@@ -179,6 +178,10 @@
          *
          * <p>The input device is restricted to the display with the given ID and may not send
          * events to any other display.</p>
+         * <p>The corresponding display must be trusted or mirror display.</p>
+         *
+         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+         * @see android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
          */
         @NonNull
         public T setAssociatedDisplayId(int displayId) {
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
index 6a7d195..9664004 100644
--- a/core/java/android/hardware/input/VirtualKeyboard.java
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -17,7 +17,6 @@
 package android.hardware.input;
 
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -31,8 +30,8 @@
  * A virtual keyboard representing a key input mechanism on a remote device, such as a built-in
  * keyboard on a laptop, a software keyboard on a tablet, or a keypad on a TV remote control.
  *
- * This registers an InputDevice that is interpreted like a physically-connected device and
- * dispatches received events to it.
+ * <p>This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.</p>
  *
  * @hide
  */
@@ -52,7 +51,6 @@
      *
      * @param event the event to send
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendKeyEvent(@NonNull VirtualKeyEvent event) {
         try {
             if (mUnsupportedKeyCode == event.getKeyCode()) {
diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java
index fb0f700..f2d113c 100644
--- a/core/java/android/hardware/input/VirtualMouse.java
+++ b/core/java/android/hardware/input/VirtualMouse.java
@@ -17,7 +17,6 @@
 package android.hardware.input;
 
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
 import android.graphics.PointF;
@@ -30,8 +29,8 @@
  * A virtual mouse representing a relative input mechanism on a remote device, such as a mouse or
  * trackpad.
  *
- * This registers an InputDevice that is interpreted like a physically-connected device and
- * dispatches received events to it.
+ * <p>This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.</p>
  *
  * @hide
  */
@@ -50,7 +49,6 @@
      * @throws IllegalStateException if the display this mouse is associated with is not currently
      * targeted
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) {
         try {
             if (!mVirtualDevice.sendButtonEvent(mToken, event)) {
@@ -70,7 +68,6 @@
      * @throws IllegalStateException if the display this mouse is associated with is not currently
      * targeted
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) {
         try {
             if (!mVirtualDevice.sendScrollEvent(mToken, event)) {
@@ -89,7 +86,6 @@
      * @throws IllegalStateException if the display this mouse is associated with is not currently
      * targeted
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) {
         try {
             if (!mVirtualDevice.sendRelativeEvent(mToken, event)) {
@@ -108,7 +104,6 @@
      * @throws IllegalStateException if the display this mouse is associated with is not currently
      * targeted
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public @NonNull PointF getCursorPosition() {
         try {
             return mVirtualDevice.getCursorPosition(mToken);
diff --git a/core/java/android/hardware/input/VirtualNavigationTouchpad.java b/core/java/android/hardware/input/VirtualNavigationTouchpad.java
index 3dbb385..94e2151 100644
--- a/core/java/android/hardware/input/VirtualNavigationTouchpad.java
+++ b/core/java/android/hardware/input/VirtualNavigationTouchpad.java
@@ -17,7 +17,6 @@
 package android.hardware.input;
 
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
 import android.os.IBinder;
@@ -51,7 +50,6 @@
      *
      * @param event the event to send
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
         try {
             if (!mVirtualDevice.sendTouchEvent(mToken, event)) {
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoder.java b/core/java/android/hardware/input/VirtualRotaryEncoder.java
index bc131df..47c92c8 100644
--- a/core/java/android/hardware/input/VirtualRotaryEncoder.java
+++ b/core/java/android/hardware/input/VirtualRotaryEncoder.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtualdevice.flags.Flags;
@@ -48,7 +47,6 @@
      *
      * @param event the event to send
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendScrollEvent(@NonNull VirtualRotaryEncoderScrollEvent event) {
         try {
             if (!mVirtualDevice.sendRotaryEncoderScrollEvent(mToken, event)) {
diff --git a/core/java/android/hardware/input/VirtualStylus.java b/core/java/android/hardware/input/VirtualStylus.java
index c763f740..4b79bc4 100644
--- a/core/java/android/hardware/input/VirtualStylus.java
+++ b/core/java/android/hardware/input/VirtualStylus.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.flags.Flags;
@@ -30,8 +29,8 @@
  * A virtual stylus which can be used to inject input into the framework that represents a stylus
  * on a remote device.
  *
- * This registers an {@link android.view.InputDevice} that is interpreted like a
- * physically-connected device and dispatches received events to it.
+ * <p>This registers an {@link android.view.InputDevice} that is interpreted like a
+ * physically-connected device and dispatches received events to it.</p>
  *
  * @hide
  */
@@ -49,7 +48,6 @@
      *
      * @param event the event to send
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendMotionEvent(@NonNull VirtualStylusMotionEvent event) {
         try {
             if (!mVirtualDevice.sendStylusMotionEvent(mToken, event)) {
@@ -66,7 +64,6 @@
      *
      * @param event the event to send
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendButtonEvent(@NonNull VirtualStylusButtonEvent event) {
         try {
             if (!mVirtualDevice.sendStylusButtonEvent(mToken, event)) {
diff --git a/core/java/android/hardware/input/VirtualTouchscreen.java b/core/java/android/hardware/input/VirtualTouchscreen.java
index 2c800aa..d0537f0 100644
--- a/core/java/android/hardware/input/VirtualTouchscreen.java
+++ b/core/java/android/hardware/input/VirtualTouchscreen.java
@@ -17,7 +17,6 @@
 package android.hardware.input;
 
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.companion.virtual.IVirtualDevice;
 import android.os.IBinder;
@@ -27,8 +26,8 @@
 /**
  * A virtual touchscreen representing a touch-based display input mechanism on a remote device.
  *
- * This registers an InputDevice that is interpreted like a physically-connected device and
- * dispatches received events to it.
+ * <p>This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.</p>
  *
  * @hide
  */
@@ -45,7 +44,6 @@
      *
      * @param event the event to send
      */
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void sendTouchEvent(@NonNull VirtualTouchEvent event) {
         try {
             if (!mVirtualDevice.sendTouchEvent(mToken, event)) {
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 99eeca9..6669754 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -27,14 +27,6 @@
 
 flag {
     namespace: "input_native"
-    name: "pointer_coords_is_resampled_api"
-    is_exported: true
-    description: "Makes MotionEvent.PointerCoords#isResampled() a public API"
-    bug: "298197511"
-}
-
-flag {
-    namespace: "input_native"
     name: "emoji_and_screenshot_keycodes_available"
     is_exported: true
     description: "Add new KeyEvent keycodes for opening Emoji Picker and Taking Screenshots"
@@ -142,3 +134,10 @@
   description: "Controls whether the connected mice's primary buttons, left and right, can be swapped."
   bug: "352598211"
 }
+
+flag {
+  name: "keyboard_a11y_shortcut_control"
+  namespace: "input"
+  description: "Adds shortcuts to toggle and control a11y features"
+  bug: "373458181"
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/core/java/android/hardware/radio/RadioAlert.aidl
similarity index 76%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to core/java/android/hardware/radio/RadioAlert.aidl
index 2f5daaa..17f4fc7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/core/java/android/hardware/radio/RadioAlert.aidl
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package android.hardware.radio;
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+/** @hide */
+parcelable RadioAlert;
\ No newline at end of file
diff --git a/core/java/android/hardware/radio/RadioAlert.java b/core/java/android/hardware/radio/RadioAlert.java
new file mode 100644
index 0000000..9b93e73
--- /dev/null
+++ b/core/java/android/hardware/radio/RadioAlert.java
@@ -0,0 +1,517 @@
+/**
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Emergency Alert Message
+ *
+ * <p>Alert message can be sent from a radio station of technologies such as HD radio to
+ * the radio users for some emergency events (see ITU-T X.1303 bis for more info).
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+public final class RadioAlert implements Parcelable {
+
+    public static final class Geocode implements Parcelable {
+
+        private final String mValueName;
+        private final String mValue;
+
+        /**
+         * Constructor of geocode.
+         *
+         * @param valueName Name of geocode value
+         * @param value Value of geocode
+         * @hide
+         */
+        public Geocode(@NonNull String valueName, @NonNull String value) {
+            mValueName = Objects.requireNonNull(valueName, "Geocode value name can not be null");
+            mValue = Objects.requireNonNull(value, "Geocode value can not be null");
+        }
+
+        private Geocode(Parcel in) {
+            mValueName = in.readString8();
+            mValue = in.readString8();
+        }
+
+        public static final @NonNull Creator<Geocode> CREATOR = new Creator<Geocode>() {
+            @Override
+            public Geocode createFromParcel(Parcel in) {
+                return new Geocode(in);
+            }
+
+            @Override
+            public Geocode[] newArray(int size) {
+                return new Geocode[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeString8(mValueName);
+            dest.writeString8(mValue);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "Gecode [valueName=" + mValueName + ", value=" + mValue + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mValueName, mValue);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Geocode other)) {
+                return false;
+            }
+
+            return Objects.equals(mValueName, other.mValueName)
+                    && Objects.equals(mValue, other.mValue);
+        }
+    }
+
+    public static final class Coordinate implements Parcelable {
+        private final double mLatitude;
+        private final double mLongitude;
+
+        /**
+         * Constructor of coordinate.
+         *
+         * @param latitude Latitude of the coordinate
+         * @param longitude Longitude of the coordinate
+         * @hide
+         */
+        public Coordinate(double latitude, double longitude) {
+            if (latitude < -90.0 || latitude > 90.0) {
+                throw new IllegalArgumentException("Latitude value should be between -90 and 90");
+            }
+            if (longitude < -180.0 || longitude > 180.0) {
+                throw new IllegalArgumentException(
+                        "Longitude value should be between -180 and 180");
+            }
+            mLatitude = latitude;
+            mLongitude = longitude;
+        }
+
+        private Coordinate(Parcel in) {
+            mLatitude = in.readDouble();
+            mLongitude = in.readDouble();
+        }
+
+        public static final @NonNull Creator<Coordinate> CREATOR = new Creator<Coordinate>() {
+            @Override
+            public Coordinate createFromParcel(Parcel in) {
+                return new Coordinate(in);
+            }
+
+            @Override
+            public Coordinate[] newArray(int size) {
+                return new Coordinate[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeDouble(mLatitude);
+            dest.writeDouble(mLongitude);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "Coordinate [latitude=" + mLatitude + ", longitude=" + mLongitude + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mLatitude, mLongitude);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Coordinate other)) {
+                return false;
+            }
+            return mLatitude == other.mLatitude && mLongitude == other.mLongitude;
+        }
+    }
+
+    public static final class Polygon implements Parcelable {
+
+        private final List<Coordinate> mCoordinates;
+
+        /**
+         * Constructor of polygon.
+         *
+         * @param coordinates Coordinates the polygon is composed of
+         * @hide
+         */
+        public Polygon(@NonNull List<Coordinate> coordinates) {
+            Objects.requireNonNull(coordinates, "Coordinates can not be null");
+            if (coordinates.size() < 4) {
+                throw new IllegalArgumentException("Number of coordinates must be at least 4");
+            }
+            if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) {
+                throw new IllegalArgumentException(
+                        "The last and first coordinates must be the same");
+            }
+            mCoordinates = coordinates;
+        }
+
+        private Polygon(Parcel in) {
+            mCoordinates = new ArrayList<>();
+            in.readTypedList(mCoordinates, Coordinate.CREATOR);
+        }
+
+        public static final @NonNull Creator<Polygon> CREATOR = new Creator<Polygon>() {
+            @Override
+            public Polygon createFromParcel(Parcel in) {
+                return new Polygon(in);
+            }
+
+            @Override
+            public Polygon[] newArray(int size) {
+                return new Polygon[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeTypedList(mCoordinates);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "Polygon [coordinates=" + mCoordinates + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mCoordinates);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Polygon other)) {
+                return false;
+            }
+            return mCoordinates.equals(other.mCoordinates);
+        }
+    }
+
+    public static final class AlertArea implements Parcelable {
+
+        private final List<Polygon> mPolygons;
+        private final List<Geocode> mGeocodes;
+
+        /**
+         * Constructor of alert area.
+         *
+         * @param polygons Polygons used in alert area
+         * @param geocodes Geocodes used in alert area
+         * @hide
+         */
+        public AlertArea(@NonNull List<Polygon> polygons, @NonNull List<Geocode> geocodes) {
+            mPolygons = Objects.requireNonNull(polygons, "Polygons can not be null");
+            mGeocodes = Objects.requireNonNull(geocodes, "Geocodes can not be null");
+        }
+
+        private AlertArea(Parcel in) {
+            mPolygons = new ArrayList<>();
+            mGeocodes = new ArrayList<>();
+            in.readTypedList(mPolygons, Polygon.CREATOR);
+            in.readTypedList(mGeocodes, Geocode.CREATOR);
+        }
+
+        public static final @NonNull Creator<AlertArea> CREATOR = new Creator<AlertArea>() {
+            @Override
+            public AlertArea createFromParcel(Parcel in) {
+                return new AlertArea(in);
+            }
+
+            @Override
+            public AlertArea[] newArray(int size) {
+                return new AlertArea[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeTypedList(mPolygons);
+            dest.writeTypedList(mGeocodes);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "AlertArea [polygons=" + mPolygons + ", geocodes=" + mGeocodes + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPolygons, mGeocodes);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof AlertArea other)) {
+                return false;
+            }
+
+            return mPolygons.equals(other.mPolygons) && mGeocodes.equals(other.mGeocodes);
+        }
+    }
+
+    public static final class AlertInfo implements Parcelable {
+
+        private final List<Integer> mCategoryList;
+        private final int mUrgency;
+        private final int mSeverity;
+        private final int mCertainty;
+        private final String mTextualMessage;
+        private final List<AlertArea> mAreaList;
+        @Nullable private final String mLanguage;
+
+        /**
+         * Constructor for alert info.
+         *
+         * @param categoryList Array of categories of the subject event of the alert message
+         * @param urgency The urgency of the subject event of the alert message
+         * @param severity The severity of the subject event of the alert message
+         * @param certainty The certainty of the subject event of the alert message
+         * @param textualMessage Textual descriptions of the subject event
+         * @param areaList The array of geographic areas to which the alert info segment in which
+         *                 it appears applies
+         * @param language The optional language field of the alert info
+         * @hide
+         */
+        public AlertInfo(@NonNull List<Integer> categoryList, int urgency,
+                int severity, int certainty, String textualMessage,
+                @NonNull List<AlertArea> areaList, @Nullable String language) {
+            mCategoryList = Objects.requireNonNull(categoryList, "Category list can not be null");
+            mUrgency = urgency;
+            mSeverity = severity;
+            mCertainty = certainty;
+            mTextualMessage = textualMessage;
+            mAreaList = Objects.requireNonNull(areaList, "Area list can not be null");
+            mLanguage = language;
+        }
+
+        private AlertInfo(Parcel in) {
+            mCategoryList = in.readArrayList(Integer.class.getClassLoader(), Integer.class);
+            mUrgency = in.readInt();
+            mSeverity = in.readInt();
+            mCertainty = in.readInt();
+            mTextualMessage = in.readString8();
+            mAreaList = new ArrayList<>();
+            in.readTypedList(mAreaList, AlertArea.CREATOR);
+            boolean hasLanguage = in.readBoolean();
+            if (hasLanguage) {
+                mLanguage = in.readString8();
+            } else {
+                mLanguage = null;
+            }
+        }
+
+        public static final @NonNull Creator<AlertInfo> CREATOR = new Creator<AlertInfo>() {
+            @Override
+            public AlertInfo createFromParcel(Parcel in) {
+                return new AlertInfo(in);
+            }
+
+            @Override
+            public AlertInfo[] newArray(int size) {
+                return new AlertInfo[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeList(mCategoryList);
+            dest.writeInt(mUrgency);
+            dest.writeInt(mSeverity);
+            dest.writeInt(mCertainty);
+            dest.writeString8(mTextualMessage);
+            dest.writeTypedList(mAreaList);
+            if (mLanguage == null) {
+                dest.writeBoolean(false);
+            } else {
+                dest.writeBoolean(true);
+                dest.writeString8(mLanguage);
+            }
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "AlertInfo [categoryList=" + mCategoryList + ", urgency=" + mUrgency
+                    + ", severity=" + mSeverity + ", certainty=" + mCertainty
+                    + ", textualMessage=" + mTextualMessage + ", areaList=" + mAreaList
+                    + ", language=" + mLanguage + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mCategoryList, mUrgency, mSeverity, mCertainty, mTextualMessage,
+                    mAreaList, mLanguage);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof AlertInfo other)) {
+                return false;
+            }
+
+            return mCategoryList.equals(other.mCategoryList) && mUrgency == other.mUrgency
+                    && mSeverity == other.mSeverity && mCertainty == other.mCertainty
+                    && mTextualMessage.equals(other.mTextualMessage)
+                    && mAreaList.equals(other.mAreaList)
+                    && Objects.equals(mLanguage, other.mLanguage);
+        }
+    }
+
+    private final int mStatus;
+    private final int mMessageType;
+    private final List<AlertInfo> mInfoList;
+
+    /**
+     * Constructor of radio alert message.
+     *
+     * @param status Status of alert message
+     * @param messageType Message type of alert message
+     * @param infoList List of alert info
+     * @hide
+     */
+    public RadioAlert(int status, int messageType,
+            @NonNull List<AlertInfo> infoList) {
+        mStatus = status;
+        mMessageType = messageType;
+        mInfoList = Objects.requireNonNull(infoList, "Alert info list can not be null");
+    }
+
+    private RadioAlert(Parcel in) {
+        mStatus = in.readInt();
+        mMessageType = in.readInt();
+        mInfoList = in.readParcelableList(new ArrayList<>(), AlertInfo.class.getClassLoader(),
+                AlertInfo.class);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mStatus);
+        dest.writeInt(mMessageType);
+        dest.writeParcelableList(mInfoList, /* flags= */ 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "RadioAlert [status=" + mStatus + ", messageType=" + mMessageType
+                + ", infoList= " + mInfoList + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStatus, mMessageType, mInfoList);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof RadioAlert other)) {
+            return false;
+        }
+
+        return mStatus == other.mStatus && mMessageType == other.mMessageType
+                && mInfoList.equals(other.mInfoList);
+    }
+
+    public static final @NonNull Creator<RadioAlert> CREATOR = new Creator<RadioAlert>() {
+        @Override
+        public RadioAlert createFromParcel(Parcel in) {
+            return new RadioAlert(in);
+        }
+
+        @Override
+        public RadioAlert[] newArray(int size) {
+            return new RadioAlert[size];
+        }
+    };
+}
diff --git a/core/java/android/net/vcn/VcnFrameworkInitializer.java b/core/java/android/net/vcn/VcnFrameworkInitializer.java
new file mode 100644
index 0000000..8cb213b
--- /dev/null
+++ b/core/java/android/net/vcn/VcnFrameworkInitializer.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import android.annotation.Nullable;
+import android.app.SystemServiceRegistry;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.SystemProperties;
+
+/**
+ * Class for performing registration for VCN service.
+ *
+ * @hide
+ */
+// TODO: Expose it as @SystemApi(client = MODULE_LIBRARIES)
+public final class VcnFrameworkInitializer {
+    /**
+     * Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags (e.g. {@link
+     * PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before returning managers
+     * that depend on them. If the feature is missing, {@link Context#getSystemService} will return
+     * null.
+     *
+     * <p>This change is specific to VcnManager.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static final long ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN = 330902016;
+
+    /**
+     * The corresponding vendor API for Android V
+     *
+     * <p>Starting with Android V, the vendor API format has switched to YYYYMM.
+     *
+     * @see <a href="https://preview.source.android.com/docs/core/architecture/api-flags">Vendor API
+     *     level</a>
+     */
+    private static final int VENDOR_API_FOR_ANDROID_V = 202404;
+
+    private VcnFrameworkInitializer() {}
+
+    // Suppressing AndroidFrameworkCompatChange because we're querying vendor
+    // partition SDK level, not application's target SDK version (which BTW we
+    // also check through Compatibility framework a few lines below).
+    @Nullable
+    private static String getVcnFeatureDependency() {
+        // Check SDK version of the client app. Apps targeting pre-V SDK might
+        // have not checked for existence of these features.
+        if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN)) {
+            return null;
+        }
+
+        // Check SDK version of the vendor partition. Pre-V devices might have
+        // incorrectly under-declared telephony features.
+        final int vendorApiLevel =
+                SystemProperties.getInt(
+                        "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
+        if (vendorApiLevel < VENDOR_API_FOR_ANDROID_V) {
+            return PackageManager.FEATURE_TELEPHONY;
+        } else {
+            return PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION;
+        }
+    }
+
+    /**
+     * Register VCN service to {@link Context}, so that {@link Context#getSystemService} can return
+     * a VcnManager.
+     *
+     * @throws IllegalStateException if this is called anywhere besides {@link
+     *     SystemServiceRegistry}.
+     */
+    public static void registerServiceWrappers() {
+        SystemServiceRegistry.registerContextAwareService(
+                VcnManager.VCN_MANAGEMENT_SERVICE_STRING,
+                VcnManager.class,
+                (context, serviceBinder) -> {
+                    final String telephonyFeatureToCheck = getVcnFeatureDependency();
+                    if (telephonyFeatureToCheck != null
+                            && !context.getPackageManager()
+                                    .hasSystemFeature(telephonyFeatureToCheck)) {
+                        return null;
+                    }
+                    IVcnManagementService service =
+                            IVcnManagementService.Stub.asInterface(serviceBinder);
+                    return new VcnManager(context, service);
+                });
+    }
+}
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index dcb363c..5b30624 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -14,45 +14,4 @@
     namespace: "vcn"
     description: "Feature flag for adjustable safe mode timeout"
     bug: "317406085"
-}
-
-flag{
-    name: "network_metric_monitor"
-    namespace: "vcn"
-    description: "Feature flag for enabling network metric monitor"
-    bug: "282996138"
-}
-
-flag{
-    name: "validate_network_on_ipsec_loss"
-    namespace: "vcn"
-    description: "Trigger network validation when IPsec packet loss exceeds the threshold"
-    bug: "329139898"
-}
-
-flag{
-    name: "evaluate_ipsec_loss_on_lp_nc_change"
-    namespace: "vcn"
-    description: "Re-evaluate IPsec packet loss on LinkProperties or NetworkCapabilities change"
-    bug: "323238888"
-}
-
-flag{
-    name: "enforce_main_user"
-    namespace: "vcn"
-    description: "Enforce main user to make VCN HSUM compatible"
-    bug: "310310661"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag{
-    name: "handle_seq_num_leap"
-    namespace: "vcn"
-    description: "Do not report bad network when there is a suspected sequence number leap"
-    bug: "332598276"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
 }
\ No newline at end of file
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 49ab15a..5012127 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.MathUtils;
@@ -401,6 +402,9 @@
             synchronized (this) {
                 object = unwrapLazyValueFromMapLocked(i, clazz, itemTypes);
             }
+            if ((mFlags & Bundle.FLAG_VERIFY_TOKENS_PRESENT) != 0) {
+                Intent.maybeMarkAsMissingCreatorToken(object);
+            }
         }
         return (clazz != null) ? clazz.cast(object) : (T) object;
     }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 479aa98..a894833 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -43,6 +44,8 @@
 
 import dalvik.system.VMRuntime;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -1273,6 +1276,47 @@
         public static final int VANILLA_ICE_CREAM = 35;
     }
 
+    /** @hide */
+    @IntDef(value = {
+        VERSION_CODES_FULL.BASE,
+        VERSION_CODES_FULL.BASE_1_1,
+        VERSION_CODES_FULL.CUPCAKE,
+        VERSION_CODES_FULL.DONUT,
+        VERSION_CODES_FULL.ECLAIR,
+        VERSION_CODES_FULL.ECLAIR_0_1,
+        VERSION_CODES_FULL.ECLAIR_MR1,
+        VERSION_CODES_FULL.FROYO,
+        VERSION_CODES_FULL.GINGERBREAD,
+        VERSION_CODES_FULL.GINGERBREAD_MR1,
+        VERSION_CODES_FULL.HONEYCOMB,
+        VERSION_CODES_FULL.HONEYCOMB_MR1,
+        VERSION_CODES_FULL.HONEYCOMB_MR2,
+        VERSION_CODES_FULL.ICE_CREAM_SANDWICH,
+        VERSION_CODES_FULL.ICE_CREAM_SANDWICH_MR1,
+        VERSION_CODES_FULL.JELLY_BEAN,
+        VERSION_CODES_FULL.JELLY_BEAN_MR1,
+        VERSION_CODES_FULL.JELLY_BEAN_MR2,
+        VERSION_CODES_FULL.KITKAT,
+        VERSION_CODES_FULL.KITKAT_WATCH,
+        VERSION_CODES_FULL.LOLLIPOP,
+        VERSION_CODES_FULL.LOLLIPOP_MR1,
+        VERSION_CODES_FULL.M,
+        VERSION_CODES_FULL.N,
+        VERSION_CODES_FULL.N_MR1,
+        VERSION_CODES_FULL.O,
+        VERSION_CODES_FULL.O_MR1,
+        VERSION_CODES_FULL.P,
+        VERSION_CODES_FULL.Q,
+        VERSION_CODES_FULL.R,
+        VERSION_CODES_FULL.S,
+        VERSION_CODES_FULL.S_V2,
+        VERSION_CODES_FULL.TIRAMISU,
+        VERSION_CODES_FULL.UPSIDE_DOWN_CAKE,
+        VERSION_CODES_FULL.VANILLA_ICE_CREAM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SdkIntFull {}
+
     /**
      * Enumeration of the currently known SDK major and minor version codes.
      * The numbers increase for every release, and are guaranteed to be ordered
@@ -1290,6 +1334,208 @@
         // Use the last 5 digits for the minor version. This allows the
         // minor version to be set to CUR_DEVELOPMENT.
         private static final int SDK_INT_MULTIPLIER = 100000;
+
+        /**
+         * Android 1.0.
+         */
+        public static final int BASE = VERSION_CODES.BASE * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 2.0.
+         */
+        public static final int BASE_1_1 = VERSION_CODES.BASE_1_1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 3.0.
+         */
+        public static final int CUPCAKE = VERSION_CODES.CUPCAKE * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 4.0.
+         */
+        public static final int DONUT = VERSION_CODES.DONUT * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 5.0.
+         */
+        public static final int ECLAIR = VERSION_CODES.ECLAIR * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 6.0.
+         */
+        public static final int ECLAIR_0_1 = VERSION_CODES.ECLAIR_0_1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 7.0.
+         */
+        public static final int ECLAIR_MR1 = VERSION_CODES.ECLAIR_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 8.0.
+         */
+        public static final int FROYO = VERSION_CODES.FROYO * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 9.0.
+         */
+        public static final int GINGERBREAD = VERSION_CODES.GINGERBREAD * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 10.0.
+         */
+        public static final int GINGERBREAD_MR1 =
+                VERSION_CODES.GINGERBREAD_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 11.0.
+         */
+        public static final int HONEYCOMB = VERSION_CODES.HONEYCOMB * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 12.0.
+         */
+        public static final int HONEYCOMB_MR1 = VERSION_CODES.HONEYCOMB_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 13.0.
+         */
+        public static final int HONEYCOMB_MR2 = VERSION_CODES.HONEYCOMB_MR2 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 14.0.
+         */
+        public static final int ICE_CREAM_SANDWICH =
+                VERSION_CODES.ICE_CREAM_SANDWICH * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 15.0.
+         */
+        public static final int ICE_CREAM_SANDWICH_MR1 =
+                VERSION_CODES.ICE_CREAM_SANDWICH_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 16.0.
+         */
+        public static final int JELLY_BEAN = VERSION_CODES.JELLY_BEAN * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 17.0.
+         */
+        public static final int JELLY_BEAN_MR1 = VERSION_CODES.JELLY_BEAN_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 18.0.
+         */
+        public static final int JELLY_BEAN_MR2 = VERSION_CODES.JELLY_BEAN_MR2 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 19.0.
+         */
+        public static final int KITKAT = VERSION_CODES.KITKAT * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 20.0.
+         */
+        public static final int KITKAT_WATCH = VERSION_CODES.KITKAT_WATCH * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 21.0.
+         */
+        public static final int LOLLIPOP = VERSION_CODES.LOLLIPOP * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 22.0.
+         */
+        public static final int LOLLIPOP_MR1 = VERSION_CODES.LOLLIPOP_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 23.0.
+         */
+        public static final int M = VERSION_CODES.M * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 24.0.
+         */
+        public static final int N = VERSION_CODES.N * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 25.0.
+         */
+        public static final int N_MR1 = VERSION_CODES.N_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 26.0.
+         */
+        public static final int O = VERSION_CODES.O * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 27.0.
+         */
+        public static final int O_MR1 = VERSION_CODES.O_MR1 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 28.0.
+         */
+        public static final int P = VERSION_CODES.P * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 29.0.
+         */
+        public static final int Q = VERSION_CODES.Q * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 30.0.
+         */
+        public static final int R = VERSION_CODES.R * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 31.0.
+         */
+        public static final int S = VERSION_CODES.S * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 32.0.
+         */
+        public static final int S_V2 = VERSION_CODES.S_V2 * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 33.0.
+         */
+        public static final int TIRAMISU = VERSION_CODES.TIRAMISU * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 34.0.
+         */
+        public static final int UPSIDE_DOWN_CAKE =
+                VERSION_CODES.UPSIDE_DOWN_CAKE * SDK_INT_MULTIPLIER;
+
+        /**
+         * Android 35.0.
+         */
+        public static final int VANILLA_ICE_CREAM =
+                VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
+    }
+
+    /**
+     * Obtain the major version encoded in a VERSION_CODES_FULL value.
+     * This value is guaranteed to be non-negative.
+     *
+     * @return The major version encoded in a VERSION_CODES_FULL value
+     */
+    @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+    public static int getMajorSdkVersion(@SdkIntFull int sdkIntFull) {
+        return sdkIntFull / VERSION_CODES_FULL.SDK_INT_MULTIPLIER;
+    }
+
+    /**
+     * Obtain the minor version encoded in a VERSION_CODES_FULL value.
+     * This value is guaranteed to be non-negative.
+     *
+     * @return The minor version encoded in a VERSION_CODES_FULL value
+     */
+    @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+    public static int getMinorSdkVersion(@SdkIntFull int sdkIntFull) {
+        return sdkIntFull % VERSION_CODES_FULL.SDK_INT_MULTIPLIER;
     }
 
     /**
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index ed4037c..c18fb0c 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -62,6 +62,12 @@
     @VisibleForTesting
     static final int FLAG_HAS_BINDERS = 1 << 12;
 
+    /**
+     * Indicates there may be intents with creator tokens contained in this bundle. When unparceled,
+     * they should be verified if tokens are missing or invalid.
+     */
+    static final int FLAG_VERIFY_TOKENS_PRESENT = 1 << 13;
+
 
     /**
      * Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain
@@ -274,6 +280,11 @@
         return orig;
     }
 
+    /** {@hide} */
+    public void setIsIntentExtra() {
+        mFlags |= FLAG_VERIFY_TOKENS_PRESENT;
+    }
+
     /**
      * Mark if this Bundle is okay to "defuse." That is, it's okay for system
      * processes to ignore any {@link BadParcelableException} encountered when
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index 77d6cb7..f1d3957 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.vibrator.Flags;
 import android.util.SparseArray;
@@ -28,6 +29,7 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.StringJoiner;
+import java.util.function.Function;
 
 /**
  * A CombinedVibration describes a combination of haptic effects to be performed by one or more
@@ -114,6 +116,17 @@
     public abstract long getDuration();
 
     /**
+     * Gets the estimated duration of the combined vibration in milliseconds.
+     *
+     * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
+     * the estimated duration based on the {@link VibratorInfo}. For all other effects this will
+     * return the same as {@link #getDuration()}.
+     *
+     * @hide
+     */
+    public abstract long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos);
+
+    /**
      * Returns true if this effect could represent a touch haptic feedback.
      *
      * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
@@ -383,6 +396,23 @@
 
         /** @hide */
         @Override
+        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+            if (vibratorInfos == null) {
+                return getDuration();
+            }
+            long maxDuration = 0;
+            for (int i = 0; i < vibratorInfos.size(); i++) {
+                long duration = mEffect.getDuration(vibratorInfos.valueAt(i));
+                if ((duration == Long.MAX_VALUE) || (duration < 0)) {
+                    return duration;
+                }
+                maxDuration = Math.max(maxDuration, duration);
+            }
+            return maxDuration;
+        }
+
+        /** @hide */
+        @Override
         public boolean isHapticFeedbackCandidate() {
             return mEffect.isHapticFeedbackCandidate();
         }
@@ -531,10 +561,27 @@
 
         @Override
         public long getDuration() {
+            return getDuration(idx -> mEffects.valueAt(idx).getDuration());
+        }
+
+        /** @hide */
+        @Override
+        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+            if (vibratorInfos == null) {
+                return getDuration();
+            }
+            return getDuration(idx -> {
+                VibrationEffect effect = mEffects.valueAt(idx);
+                VibratorInfo info = vibratorInfos.get(mEffects.keyAt(idx));
+                return effect.getDuration(info);
+            });
+        }
+
+        private long getDuration(Function<Integer, Long> durationFn) {
             long maxDuration = Long.MIN_VALUE;
             boolean hasUnknownStep = false;
             for (int i = 0; i < mEffects.size(); i++) {
-                long duration = mEffects.valueAt(i).getDuration();
+                long duration = durationFn.apply(i);
                 if (duration == Long.MAX_VALUE) {
                     // If any duration is repeating, this combination duration is also repeating.
                     return duration;
@@ -750,12 +797,21 @@
 
         @Override
         public long getDuration() {
+            return getDuration(CombinedVibration::getDuration);
+        }
+
+        /** @hide */
+        @Override
+        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+            return getDuration(effect -> effect.getDuration(vibratorInfos));
+        }
+
+        private long getDuration(Function<CombinedVibration, Long> durationFn) {
             boolean hasUnknownStep = false;
             long durations = 0;
             final int effectCount = mEffects.size();
             for (int i = 0; i < effectCount; i++) {
-                CombinedVibration effect = mEffects.get(i);
-                long duration = effect.getDuration();
+                long duration = durationFn.apply(mEffects.get(i));
                 if (duration == Long.MAX_VALUE) {
                     // If any duration is repeating, this combination duration is also repeating.
                     return duration;
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index b2d9260..6afb8e0 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -754,7 +754,7 @@
                 // Idle handles only run if the queue is empty or if the first message
                 // in the queue (possibly a barrier) is due to be handled in the future.
                 if (pendingIdleHandlerCount < 0
-                        && mNextPollTimeoutMillis != 0) {
+                        && isIdle()) {
                     pendingIdleHandlerCount = mIdleHandlers.size();
                 }
                 if (pendingIdleHandlerCount <= 0) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index a55398a..ef1e6c94 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -114,6 +114,7 @@
         "opengl-tracing",
         "view-hierarchy",
         "support_boot_stages",
+        "app_info",
     };
 
     /**
@@ -1016,14 +1017,14 @@
         // send VM_START.
         System.out.println("Waiting for debugger first packet");
 
-        mWaiting = true;
+        setWaitingForDebugger(true);
         while (!isDebuggerConnected()) {
             try {
                 Thread.sleep(100);
             } catch (InterruptedException ie) {
             }
         }
-        mWaiting = false;
+        setWaitingForDebugger(false);
 
         System.out.println("Debug.suspendAllAndSentVmStart");
         VMDebug.suspendAllAndSendVmStart();
@@ -1049,12 +1050,12 @@
         Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1);
         DdmServer.sendChunk(waitChunk);
 
-        mWaiting = true;
+        setWaitingForDebugger(true);
         while (!isDebuggerConnected()) {
             try { Thread.sleep(SPIN_DELAY); }
             catch (InterruptedException ie) {}
         }
-        mWaiting = false;
+        setWaitingForDebugger(false);
 
         System.out.println("Debugger has connected");
 
@@ -1112,6 +1113,16 @@
     }
 
     /**
+     * Set whether the app is waiting for a debugger to connect
+     *
+     * @hide
+     */
+    private static void setWaitingForDebugger(boolean waiting) {
+        mWaiting = waiting;
+        VMDebug.setWaitingForDebugger(waiting);
+    }
+
+    /**
      * Returns an array of strings that identify Framework features. This is
      * used by DDMS to determine what sorts of operations the Framework can
      * perform.
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 2d3dd1b..94768d1 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -32,6 +32,8 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.internal.R;
+
 import dalvik.system.VMRuntime;
 
 import java.io.BufferedReader;
@@ -174,19 +176,6 @@
         nativeToggleAngleAsSystemDriver(enabled);
     }
 
-    /**
-     * Query to determine the ANGLE driver choice.
-     */
-    private String queryAngleChoice(Context context, Bundle coreSettings,
-                                               String packageName) {
-        if (TextUtils.isEmpty(packageName)) {
-            Log.v(TAG, "No package name specified; use the system driver");
-            return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
-        }
-
-        return queryAngleChoiceInternal(context, coreSettings, packageName);
-    }
-
     private int getVulkanVersion(PackageManager pm) {
         // PackageManager doesn't have an API to retrieve the version of a specific feature, and we
         // need to avoid retrieving all system features here and looping through them.
@@ -403,11 +392,13 @@
      *    Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES; which corresponds to the
      *    “angle_gl_driver_selection_pkgs” and “angle_gl_driver_selection_values” settings); if it
      *    forces a choice.
+     * 3) The per-application ANGLE allowlist contained in the platform. This is an array of
+     *    strings containing package names that should use ANGLE.
      */
-    private String queryAngleChoiceInternal(Context context, Bundle bundle,
-                                                       String packageName) {
+    private String queryAngleChoice(Context context, Bundle bundle, String packageName) {
         // Make sure we have a good package name
         if (TextUtils.isEmpty(packageName)) {
+            Log.v(TAG, "No package name specified; use the system driver");
             return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
         }
 
@@ -436,8 +427,8 @@
         Log.v(TAG, "  angle_gl_driver_selection_pkgs=" + optInPackages);
         Log.v(TAG, "  angle_gl_driver_selection_values=" + optInValues);
 
-        // Make sure we have good settings to use
-        if (optInPackages.size() == 0 || optInPackages.size() != optInValues.size()) {
+        // Make sure we have valid settings, if any provided
+        if (optInPackages.size() != optInValues.size()) {
             Log.v(TAG,
                     "Global.Settings values are invalid: "
                         + "number of packages: "
@@ -449,24 +440,53 @@
 
         // See if this application is listed in the per-application settings list
         final int pkgIndex = getPackageIndex(packageName, optInPackages);
+        if (pkgIndex >= 0) {
+            mAngleOptInIndex = pkgIndex;
 
-        if (pkgIndex < 0) {
-            Log.v(TAG, packageName + " is not listed in per-application setting");
-            return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
+            // The application IS listed in the per-application settings list; and so use the
+            // setting--choosing the current system driver if the setting is "default"
+            String optInValue = optInValues.get(pkgIndex);
+            Log.v(
+                    TAG,
+                    "ANGLE Developer option for '"
+                            + packageName
+                            + "' "
+                            + "set to: '"
+                            + optInValue
+                            + "'");
+            if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
+                return ANGLE_GL_DRIVER_CHOICE_ANGLE;
+            } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
+                return ANGLE_GL_DRIVER_CHOICE_NATIVE;
+            }
         }
-        mAngleOptInIndex = pkgIndex;
 
-        // The application IS listed in the per-application settings list; and so use the
-        // setting--choosing the current system driver if the setting is "default"
-        String optInValue = optInValues.get(pkgIndex);
-        Log.v(TAG,
-                "ANGLE Developer option for '" + packageName + "' "
-                        + "set to: '" + optInValue + "'");
-        if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
-            return ANGLE_GL_DRIVER_CHOICE_ANGLE;
-        } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
-            return ANGLE_GL_DRIVER_CHOICE_NATIVE;
+        Log.v(TAG, packageName + " is not listed in per-application setting");
+
+        // Check the per-device allowlist shipped in the platform
+        if (android.os.Flags.enableAngleAllowList()) {
+            String[] angleAllowListPackages =
+                    context.getResources().getStringArray(R.array.config_angleAllowList);
+
+            String allowListPackageList = String.join(" ", angleAllowListPackages);
+            Log.v(TAG, "ANGLE allowlist from config: " + allowListPackageList);
+
+            for (String allowedPackage : angleAllowListPackages) {
+                if (allowedPackage.equals(packageName)) {
+                    Log.v(
+                            TAG,
+                            "Package name "
+                                    + packageName
+                                    + " is listed in config_angleAllowList, enabling ANGLE");
+                    return ANGLE_GL_DRIVER_CHOICE_ANGLE;
+                }
+            }
+            Log.v(
+                    TAG,
+                    packageName
+                            + " is not listed in ANGLE allowlist or settings, returning default");
         }
+
         // The user either chose default or an invalid value; go with the default driver.
         return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
     }
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 7d3076d..590ddb4 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -115,3 +115,13 @@
 # MessageQueue
 per-file MessageQueue.java = mfasheh@google.com, shayba@google.com
 per-file Message.java = mfasheh@google.com, shayba@google.com
+
+# Stats
+per-file IStatsBootstrapAtomService.aidl = file:/services/core/java/com/android/server/stats/OWNERS
+per-file StatsBootstrapAtom.aidl = file:/services/core/java/com/android/server/stats/OWNERS
+per-file StatsBootstrapAtomValue.aidl = file:/services/core/java/com/android/server/stats/OWNERS
+per-file StatsBootstrapAtomService.java = file:/services/core/java/com/android/server/stats/OWNERS
+per-file StatsServiceManager.java = file:/services/core/java/com/android/server/stats/OWNERS
+
+# Dropbox
+per-file DropBoxManager* = mwachens@google.com
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 346ee7c..71d29af 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -41,6 +41,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.sdksandbox.flags.Flags;
 
+import dalvik.system.VMDebug;
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
@@ -1410,6 +1411,7 @@
     public static void setArgV0(@NonNull String text) {
         sArgV0 = text;
         setArgV0Native(text);
+        VMDebug.setCurrentProcessName(text);
     }
 
     private static native void setArgV0Native(String text);
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 769cbdd..f82c822 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -16,11 +16,18 @@
 
 package android.os;
 
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArrayMap;
 import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
@@ -30,7 +37,7 @@
  * {@link android.app.Service} to its clients.  In particular, this:
  *
  * <ul>
- * <li> Keeps track of a set of registered {@link IInterface} callbacks,
+ * <li> Keeps track of a set of registered {@link IInterface} objects,
  * taking care to identify them through their underlying unique {@link IBinder}
  * (by calling {@link IInterface#asBinder IInterface.asBinder()}.
  * <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
@@ -47,7 +54,7 @@
  * the registered clients, use {@link #beginBroadcast},
  * {@link #getBroadcastItem}, and {@link #finishBroadcast}.
  *
- * <p>If a registered callback's process goes away, this class will take
+ * <p>If a registered interface's process goes away, this class will take
  * care of automatically removing it from the list.  If you want to do
  * additional work in this situation, you can create a subclass that
  * implements the {@link #onCallbackDied} method.
@@ -56,78 +63,310 @@
 public class RemoteCallbackList<E extends IInterface> {
     private static final String TAG = "RemoteCallbackList";
 
+    private static final int DEFAULT_MAX_QUEUE_SIZE = 1000;
+
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"FROZEN_CALLEE_POLICY_"}, value = {
+            FROZEN_CALLEE_POLICY_UNSET,
+            FROZEN_CALLEE_POLICY_ENQUEUE_ALL,
+            FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT,
+            FROZEN_CALLEE_POLICY_DROP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface FrozenCalleePolicy {
+    }
+
+    /**
+     * Callbacks are invoked immediately regardless of the frozen state of the target process.
+     *
+     * Not recommended. Only exists for backward-compatibility. This represents the behavior up to
+     * SDK 35. Starting with SDK 36, clients should set a policy to govern callback invocations when
+     * recipients are frozen.
+     */
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public static final int FROZEN_CALLEE_POLICY_UNSET = 0;
+
+    /**
+     * When the callback recipient's process is frozen, callbacks are enqueued so they're invoked
+     * after the recipient is unfrozen.
+     *
+     * This is commonly used when the recipient wants to receive all callbacks without losing any
+     * history, e.g. the recipient maintains a running count of events that occurred.
+     *
+     * Queued callbacks are invoked in the order they were originally broadcasted.
+     */
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1;
+
+    /**
+     * When the callback recipient's process is frozen, only the most recent callback is enqueued,
+     * which is later invoked after the recipient is unfrozen.
+     *
+     * This can be used when only the most recent state matters, for instance when clients are
+     * listening to screen brightness changes.
+     */
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2;
+
+    /**
+     * When the callback recipient's process is frozen, callbacks are suppressed as if they never
+     * happened.
+     *
+     * This could be useful in the case where the recipient wishes to react to callbacks only when
+     * they occur while the recipient is not frozen. For example, certain network events are only
+     * worth responding to if the response can be immediate. Another example is recipients having
+     * another way of getting the latest state once it's unfrozen. Therefore there is no need to
+     * save callbacks that happened while the recipient was frozen.
+     */
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public static final int FROZEN_CALLEE_POLICY_DROP = 3;
+
     @UnsupportedAppUsage
-    /*package*/ ArrayMap<IBinder, Callback> mCallbacks
-            = new ArrayMap<IBinder, Callback>();
+    /*package*/ ArrayMap<IBinder, Interface> mInterfaces = new ArrayMap<IBinder, Interface>();
     private Object[] mActiveBroadcast;
     private int mBroadcastCount = -1;
     private boolean mKilled = false;
     private StringBuilder mRecentCallers;
 
-    private final class Callback implements IBinder.DeathRecipient {
-        final E mCallback;
-        final Object mCookie;
+    private final @FrozenCalleePolicy int mFrozenCalleePolicy;
+    private final int mMaxQueueSize;
 
-        Callback(E callback, Object cookie) {
-            mCallback = callback;
+    private final class Interface implements IBinder.DeathRecipient,
+            IBinder.FrozenStateChangeCallback {
+        final IBinder mBinder;
+        final E mInterface;
+        final Object mCookie;
+        final Queue<Consumer<E>> mCallbackQueue;
+        int mCurrentState = IBinder.FrozenStateChangeCallback.STATE_UNFROZEN;
+
+        Interface(E callbackInterface, Object cookie) {
+            mBinder = callbackInterface.asBinder();
+            mInterface = callbackInterface;
             mCookie = cookie;
+            mCallbackQueue = mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_ALL
+                || mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT
+                ? new ConcurrentLinkedQueue<>() : null;
+        }
+
+        @Override
+        public synchronized void onFrozenStateChanged(@NonNull IBinder who, int state) {
+            if (state == STATE_UNFROZEN && mCallbackQueue != null) {
+                while (!mCallbackQueue.isEmpty()) {
+                    Consumer<E> callback = mCallbackQueue.poll();
+                    callback.accept(mInterface);
+                }
+            }
+            mCurrentState = state;
+        }
+
+        void addCallback(@NonNull Consumer<E> callback) {
+            if (mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_UNSET) {
+                callback.accept(mInterface);
+                return;
+            }
+            synchronized (this) {
+                if (mCurrentState == STATE_UNFROZEN) {
+                    callback.accept(mInterface);
+                    return;
+                }
+                switch (mFrozenCalleePolicy) {
+                    case FROZEN_CALLEE_POLICY_ENQUEUE_ALL:
+                        if (mCallbackQueue.size() >= mMaxQueueSize) {
+                            mCallbackQueue.poll();
+                        }
+                        mCallbackQueue.offer(callback);
+                        break;
+                    case FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT:
+                        mCallbackQueue.clear();
+                        mCallbackQueue.offer(callback);
+                        break;
+                    case FROZEN_CALLEE_POLICY_DROP:
+                        // Do nothing. Just ignore the callback.
+                        break;
+                    case FROZEN_CALLEE_POLICY_UNSET:
+                        // Do nothing. Should have returned at the start of the method.
+                        break;
+                }
+            }
+        }
+
+        public void maybeSubscribeToFrozenCallback() throws RemoteException {
+            if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
+                mBinder.addFrozenStateChangeCallback(this);
+            }
+        }
+
+        public void maybeUnsubscribeFromFrozenCallback() {
+            if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
+                mBinder.removeFrozenStateChangeCallback(this);
+            }
         }
 
         public void binderDied() {
-            synchronized (mCallbacks) {
-                mCallbacks.remove(mCallback.asBinder());
+            synchronized (mInterfaces) {
+                mInterfaces.remove(mBinder);
+                maybeUnsubscribeFromFrozenCallback();
             }
-            onCallbackDied(mCallback, mCookie);
+            onCallbackDied(mInterface, mCookie);
         }
     }
 
     /**
+     * Builder for {@link RemoteCallbackList}.
+     *
+     * @param <E> The remote callback interface type.
+     */
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public static final class Builder<E extends IInterface> {
+        private @FrozenCalleePolicy int mFrozenCalleePolicy;
+        private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
+
+        /**
+         * Creates a Builder for {@link RemoteCallbackList}.
+         *
+         * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
+         * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
+         * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
+         * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
+         */
+        public Builder(@FrozenCalleePolicy int frozenCalleePolicy) {
+            mFrozenCalleePolicy = frozenCalleePolicy;
+        }
+
+        /**
+         * Sets the max queue size.
+         *
+         * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
+         * recipient's process is frozen. Once the limit is reached, the oldest callback is dropped
+         * to keep the size under the limit. Should only be called for
+         * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
+         *
+         * @return This builder.
+         * @throws IllegalArgumentException if the maxQueueSize is not positive.
+         * @throws UnsupportedOperationException if frozenCalleePolicy is not
+         * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
+         */
+        public @NonNull Builder setMaxQueueSize(int maxQueueSize) {
+            if (maxQueueSize <= 0) {
+                throw new IllegalArgumentException("maxQueueSize must be positive");
+            }
+            if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_ENQUEUE_ALL) {
+                throw new UnsupportedOperationException(
+                        "setMaxQueueSize can only be called for FROZEN_CALLEE_POLICY_ENQUEUE_ALL");
+            }
+            mMaxQueueSize = maxQueueSize;
+            return this;
+        }
+
+        /**
+         * Builds and returns a {@link RemoteCallbackList}.
+         *
+         * @return The built {@link RemoteCallbackList} object.
+         */
+        public @NonNull RemoteCallbackList<E> build() {
+            return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize);
+        }
+    }
+
+    /**
+     * Returns the frozen callee policy.
+     *
+     * @return The frozen callee policy.
+     */
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public @FrozenCalleePolicy int getFrozenCalleePolicy() {
+        return mFrozenCalleePolicy;
+    }
+
+    /**
+     * Returns the max queue size.
+     *
+     * @return The max queue size.
+     */
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public int getMaxQueueSize() {
+        return mMaxQueueSize;
+    }
+
+    /**
+     * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to
+     * <pre>
+     * new RemoteCallbackList.Build(RemoteCallbackList.FROZEN_CALLEE_POLICY_UNSET).build()
+     * </pre>
+     */
+    public RemoteCallbackList() {
+        this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE);
+    }
+
+    /**
+     * Creates a RemoteCallbackList with the specified frozen callee policy.
+     *
+     * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
+     * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
+     * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
+     * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
+     *
+     * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
+     * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be
+     * dropped to keep the size under limit. Ignored except for
+     * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
+     */
+    private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) {
+        mFrozenCalleePolicy = frozenCalleePolicy;
+        mMaxQueueSize = maxQueueSize;
+    }
+
+    /**
      * Simple version of {@link RemoteCallbackList#register(E, Object)}
      * that does not take a cookie object.
      */
-    public boolean register(E callback) {
-        return register(callback, null);
+    public boolean register(E callbackInterface) {
+        return register(callbackInterface, null);
     }
 
     /**
-     * Add a new callback to the list.  This callback will remain in the list
+     * Add a new interface to the list.  This interface will remain in the list
      * until a corresponding call to {@link #unregister} or its hosting process
-     * goes away. If the callback was already registered (determined by
-     * checking to see if the {@link IInterface#asBinder callback.asBinder()}
-     * object is already in the list), then it will be replaced with the new callback.
+     * goes away.  If the interface was already registered (determined by
+     * checking to see if the {@link IInterface#asBinder callbackInterface.asBinder()}
+     * object is already in the list), then it will be replaced with the new interface.
      * Registrations are not counted; a single call to {@link #unregister}
-     * will remove a callback after any number calls to register it.
+     * will remove an interface after any number calls to register it.
      *
-     * @param callback The callback interface to be added to the list.  Must
+     * @param callbackInterface The callback interface to be added to the list.  Must
      * not be null -- passing null here will cause a NullPointerException.
      * Most services will want to check for null before calling this with
      * an object given from a client, so that clients can't crash the
      * service with bad data.
      *
      * @param cookie Optional additional data to be associated with this
-     * callback.
+     * interface.
      *
-     * @return Returns true if the callback was successfully added to the list.
+     * @return Returns true if the interface was successfully added to the list.
      * Returns false if it was not added, either because {@link #kill} had
-     * previously been called or the callback's process has gone away.
+     * previously been called or the interface's process has gone away.
      *
      * @see #unregister
      * @see #kill
      * @see #onCallbackDied
      */
-    public boolean register(E callback, Object cookie) {
-        synchronized (mCallbacks) {
+    public boolean register(E callbackInterface, Object cookie) {
+        synchronized (mInterfaces) {
             if (mKilled) {
                 return false;
             }
             // Flag unusual case that could be caused by a leak. b/36778087
-            logExcessiveCallbacks();
-            IBinder binder = callback.asBinder();
+            logExcessiveInterfaces();
+            IBinder binder = callbackInterface.asBinder();
             try {
-                Callback cb = new Callback(callback, cookie);
-                unregister(callback);
-                binder.linkToDeath(cb, 0);
-                mCallbacks.put(binder, cb);
+                Interface i = new Interface(callbackInterface, cookie);
+                unregister(callbackInterface);
+                binder.linkToDeath(i, 0);
+                i.maybeSubscribeToFrozenCallback();
+                mInterfaces.put(binder, i);
                 return true;
             } catch (RemoteException e) {
                 return false;
@@ -136,27 +375,28 @@
     }
 
     /**
-     * Remove from the list a callback that was previously added with
+     * Remove from the list an interface that was previously added with
      * {@link #register}.  This uses the
-     * {@link IInterface#asBinder callback.asBinder()} object to correctly
+     * {@link IInterface#asBinder callbackInterface.asBinder()} object to correctly
      * find the previous registration.
      * Registrations are not counted; a single unregister call will remove
-     * a callback after any number calls to {@link #register} for it.
+     * an interface after any number calls to {@link #register} for it.
      *
-     * @param callback The callback to be removed from the list.  Passing
+     * @param callbackInterface The interface to be removed from the list.  Passing
      * null here will cause a NullPointerException, so you will generally want
      * to check for null before calling.
      *
-     * @return Returns true if the callback was found and unregistered.  Returns
-     * false if the given callback was not found on the list.
+     * @return Returns true if the interface was found and unregistered.  Returns
+     * false if the given interface was not found on the list.
      *
      * @see #register
      */
-    public boolean unregister(E callback) {
-        synchronized (mCallbacks) {
-            Callback cb = mCallbacks.remove(callback.asBinder());
-            if (cb != null) {
-                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+    public boolean unregister(E callbackInterface) {
+        synchronized (mInterfaces) {
+            Interface i = mInterfaces.remove(callbackInterface.asBinder());
+            if (i != null) {
+                i.mInterface.asBinder().unlinkToDeath(i, 0);
+                i.maybeUnsubscribeFromFrozenCallback();
                 return true;
             }
             return false;
@@ -164,20 +404,21 @@
     }
 
     /**
-     * Disable this callback list.  All registered callbacks are unregistered,
+     * Disable this interface list.  All registered interfaces are unregistered,
      * and the list is disabled so that future calls to {@link #register} will
      * fail.  This should be used when a Service is stopping, to prevent clients
-     * from registering callbacks after it is stopped.
+     * from registering interfaces after it is stopped.
      *
      * @see #register
      */
     public void kill() {
-        synchronized (mCallbacks) {
-            for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
-                Callback cb = mCallbacks.valueAt(cbi);
-                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
+        synchronized (mInterfaces) {
+            for (int cbi = mInterfaces.size() - 1; cbi >= 0; cbi--) {
+                Interface i = mInterfaces.valueAt(cbi);
+                i.mInterface.asBinder().unlinkToDeath(i, 0);
+                i.maybeUnsubscribeFromFrozenCallback();
             }
-            mCallbacks.clear();
+            mInterfaces.clear();
             mKilled = true;
         }
     }
@@ -186,15 +427,15 @@
      * Old version of {@link #onCallbackDied(E, Object)} that
      * does not provide a cookie.
      */
-    public void onCallbackDied(E callback) {
+    public void onCallbackDied(E callbackInterface) {
     }
     
     /**
-     * Called when the process hosting a callback in the list has gone away.
+     * Called when the process hosting an interface in the list has gone away.
      * The default implementation calls {@link #onCallbackDied(E)}
      * for backwards compatibility.
      * 
-     * @param callback The callback whose process has died.  Note that, since
+     * @param callbackInterface The interface whose process has died.  Note that, since
      * its process has died, you can not make any calls on to this interface.
      * You can, however, retrieve its IBinder and compare it with another
      * IBinder to see if it is the same object.
@@ -203,13 +444,15 @@
      * 
      * @see #register
      */
-    public void onCallbackDied(E callback, Object cookie) {
-        onCallbackDied(callback);
+    public void onCallbackDied(E callbackInterface, Object cookie) {
+        onCallbackDied(callbackInterface);
     }
 
     /**
-     * Prepare to start making calls to the currently registered callbacks.
-     * This creates a copy of the callback list, which you can retrieve items
+     * Use {@link #broadcast(Consumer)} instead to ensure proper handling of frozen processes.
+     *
+     * Prepare to start making calls to the currently registered interfaces.
+     * This creates a copy of the interface list, which you can retrieve items
      * from using {@link #getBroadcastItem}.  Note that only one broadcast can
      * be active at a time, so you must be sure to always call this from the
      * same thread (usually by scheduling with {@link Handler}) or
@@ -219,44 +462,56 @@
      * <p>A typical loop delivering a broadcast looks like this:
      *
      * <pre>
-     * int i = callbacks.beginBroadcast();
+     * int i = interfaces.beginBroadcast();
      * while (i &gt; 0) {
      *     i--;
      *     try {
-     *         callbacks.getBroadcastItem(i).somethingHappened();
+     *         interfaces.getBroadcastItem(i).somethingHappened();
      *     } catch (RemoteException e) {
      *         // The RemoteCallbackList will take care of removing
      *         // the dead object for us.
      *     }
      * }
-     * callbacks.finishBroadcast();</pre>
+     * interfaces.finishBroadcast();</pre>
      *
-     * @return Returns the number of callbacks in the broadcast, to be used
+     * Note that this method is only supported for {@link #FROZEN_CALLEE_POLICY_UNSET}. For other
+     * policies use {@link #broadcast(Consumer)} instead.
+     *
+     * @return Returns the number of interfaces in the broadcast, to be used
      * with {@link #getBroadcastItem} to determine the range of indices you
      * can supply.
      *
+     * @throws UnsupportedOperationException if an frozen callee policy is set.
+     *
      * @see #getBroadcastItem
      * @see #finishBroadcast
      */
     public int beginBroadcast() {
-        synchronized (mCallbacks) {
+        if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
+            throw new UnsupportedOperationException();
+        }
+        return beginBroadcastInternal();
+    }
+
+    private int beginBroadcastInternal() {
+        synchronized (mInterfaces) {
             if (mBroadcastCount > 0) {
                 throw new IllegalStateException(
                         "beginBroadcast() called while already in a broadcast");
             }
             
-            final int N = mBroadcastCount = mCallbacks.size();
-            if (N <= 0) {
+            final int n = mBroadcastCount = mInterfaces.size();
+            if (n <= 0) {
                 return 0;
             }
             Object[] active = mActiveBroadcast;
-            if (active == null || active.length < N) {
-                mActiveBroadcast = active = new Object[N];
+            if (active == null || active.length < n) {
+                mActiveBroadcast = active = new Object[n];
             }
-            for (int i=0; i<N; i++) {
-                active[i] = mCallbacks.valueAt(i);
+            for (int i = 0; i < n; i++) {
+                active[i] = mInterfaces.valueAt(i);
             }
-            return N;
+            return n;
         }
     }
 
@@ -267,24 +522,23 @@
      * calling {@link #finishBroadcast}.
      *
      * <p>Note that it is possible for the process of one of the returned
-     * callbacks to go away before you call it, so you will need to catch
+     * interfaces to go away before you call it, so you will need to catch
      * {@link RemoteException} when calling on to the returned object.
-     * The callback list itself, however, will take care of unregistering
+     * The interface list itself, however, will take care of unregistering
      * these objects once it detects that it is no longer valid, so you can
      * handle such an exception by simply ignoring it.
      *
-     * @param index Which of the registered callbacks you would like to
+     * @param index Which of the registered interfaces you would like to
      * retrieve.  Ranges from 0 to {@link #beginBroadcast}-1, inclusive.
      *
-     * @return Returns the callback interface that you can call.  This will
-     * always be non-null.
+     * @return Returns the interface that you can call.  This will always be non-null.
      *
      * @see #beginBroadcast
      */
     public E getBroadcastItem(int index) {
-        return ((Callback)mActiveBroadcast[index]).mCallback;
+        return ((Interface) mActiveBroadcast[index]).mInterface;
     }
-    
+
     /**
      * Retrieve the cookie associated with the item
      * returned by {@link #getBroadcastItem(int)}.
@@ -292,7 +546,7 @@
      * @see #getBroadcastItem
      */
     public Object getBroadcastCookie(int index) {
-        return ((Callback)mActiveBroadcast[index]).mCookie;
+        return ((Interface) mActiveBroadcast[index]).mCookie;
     }
 
     /**
@@ -303,7 +557,7 @@
      * @see #beginBroadcast
      */
     public void finishBroadcast() {
-        synchronized (mCallbacks) {
+        synchronized (mInterfaces) {
             if (mBroadcastCount < 0) {
                 throw new IllegalStateException(
                         "finishBroadcast() called outside of a broadcast");
@@ -322,16 +576,18 @@
     }
 
     /**
-     * Performs {@code action} on each callback, calling
-     * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
+     * Performs {@code callback} on each registered interface.
      *
-     * @hide
+     * This is equivalent to #beginBroadcast, followed by iterating over the items using
+     * #getBroadcastItem and then @finishBroadcast, except that this method supports
+     * frozen callee policies.
      */
-    public void broadcast(Consumer<E> action) {
-        int itemCount = beginBroadcast();
+    @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
+    public void broadcast(@NonNull Consumer<E> callback) {
+        int itemCount = beginBroadcastInternal();
         try {
             for (int i = 0; i < itemCount; i++) {
-                action.accept(getBroadcastItem(i));
+                ((Interface) mActiveBroadcast[i]).addCallback(callback);
             }
         } finally {
             finishBroadcast();
@@ -339,16 +595,16 @@
     }
 
     /**
-     * Performs {@code action} for each cookie associated with a callback, calling
+     * Performs {@code callback} for each cookie associated with an interface, calling
      * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
      *
      * @hide
      */
-    public <C> void broadcastForEachCookie(Consumer<C> action) {
+    public <C> void broadcastForEachCookie(Consumer<C> callback) {
         int itemCount = beginBroadcast();
         try {
             for (int i = 0; i < itemCount; i++) {
-                action.accept((C) getBroadcastCookie(i));
+                callback.accept((C) getBroadcastCookie(i));
             }
         } finally {
             finishBroadcast();
@@ -356,16 +612,16 @@
     }
 
     /**
-     * Performs {@code action} on each callback and associated cookie, calling {@link
+     * Performs {@code callback} on each interface and associated cookie, calling {@link
      * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
      *
      * @hide
      */
-    public <C> void broadcast(BiConsumer<E, C> action) {
+    public <C> void broadcast(BiConsumer<E, C> callback) {
         int itemCount = beginBroadcast();
         try {
             for (int i = 0; i < itemCount; i++) {
-                action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+                callback.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
             }
         } finally {
             finishBroadcast();
@@ -373,10 +629,10 @@
     }
 
     /**
-     * Returns the number of registered callbacks. Note that the number of registered
-     * callbacks may differ from the value returned by {@link #beginBroadcast()} since
-     * the former returns the number of callbacks registered at the time of the call
-     * and the second the number of callback to which the broadcast will be delivered.
+     * Returns the number of registered interfaces. Note that the number of registered
+     * interfaces may differ from the value returned by {@link #beginBroadcast()} since
+     * the former returns the number of interfaces registered at the time of the call
+     * and the second the number of interfaces to which the broadcast will be delivered.
      * <p>
      * This function is useful to decide whether to schedule a broadcast if this
      * requires doing some work which otherwise would not be performed.
@@ -385,39 +641,39 @@
      * @return The size.
      */
     public int getRegisteredCallbackCount() {
-        synchronized (mCallbacks) {
+        synchronized (mInterfaces) {
             if (mKilled) {
                 return 0;
             }
-            return mCallbacks.size();
+            return mInterfaces.size();
         }
     }
 
     /**
-     * Return a currently registered callback.  Note that this is
+     * Return a currently registered interface.  Note that this is
      * <em>not</em> the same as {@link #getBroadcastItem} and should not be used
-     * interchangeably with it.  This method returns the registered callback at the given
+     * interchangeably with it.  This method returns the registered interface at the given
      * index, not the current broadcast state.  This means that it is not itself thread-safe:
      * any call to {@link #register} or {@link #unregister} will change these indices, so you
      * must do your own thread safety between these to protect from such changes.
      *
-     * @param index Index of which callback registration to return, from 0 to
+     * @param index Index of which interface registration to return, from 0 to
      * {@link #getRegisteredCallbackCount()} - 1.
      *
-     * @return Returns whatever callback is associated with this index, or null if
+     * @return Returns whatever interface is associated with this index, or null if
      * {@link #kill()} has been called.
      */
     public E getRegisteredCallbackItem(int index) {
-        synchronized (mCallbacks) {
+        synchronized (mInterfaces) {
             if (mKilled) {
                 return null;
             }
-            return mCallbacks.valueAt(index).mCallback;
+            return mInterfaces.valueAt(index).mInterface;
         }
     }
 
     /**
-     * Return any cookie associated with a currently registered callback.  Note that this is
+     * Return any cookie associated with a currently registered interface.  Note that this is
      * <em>not</em> the same as {@link #getBroadcastCookie} and should not be used
      * interchangeably with it.  This method returns the current cookie registered at the given
      * index, not the current broadcast state.  This means that it is not itself thread-safe:
@@ -431,25 +687,25 @@
      * {@link #kill()} has been called.
      */
     public Object getRegisteredCallbackCookie(int index) {
-        synchronized (mCallbacks) {
+        synchronized (mInterfaces) {
             if (mKilled) {
                 return null;
             }
-            return mCallbacks.valueAt(index).mCookie;
+            return mInterfaces.valueAt(index).mCookie;
         }
     }
 
     /** @hide */
     public void dump(PrintWriter pw, String prefix) {
-        synchronized (mCallbacks) {
-            pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
+        synchronized (mInterfaces) {
+            pw.print(prefix); pw.print("callbacks: "); pw.println(mInterfaces.size());
             pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
             pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
         }
     }
 
-    private void logExcessiveCallbacks() {
-        final long size = mCallbacks.size();
+    private void logExcessiveInterfaces() {
+        final long size = mInterfaces.size();
         final long TOO_MANY = 3000;
         final long MAX_CHARS = 1000;
         if (size >= TOO_MANY) {
diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
index 80c24a9..02335972 100644
--- a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java
@@ -712,7 +712,7 @@
                 // Idle handles only run if the queue is empty or if the first message
                 // in the queue (possibly a barrier) is due to be handled in the future.
                 if (pendingIdleHandlerCount < 0
-                        && mNextPollTimeoutMillis != 0) {
+                        && isIdle()) {
                     pendingIdleHandlerCount = mIdleHandlers.size();
                 }
                 if (pendingIdleHandlerCount <= 0) {
diff --git a/core/java/android/os/StatsBootstrapAtomValue.aidl b/core/java/android/os/StatsBootstrapAtomValue.aidl
index a90dfa4..b31eb6f 100644
--- a/core/java/android/os/StatsBootstrapAtomValue.aidl
+++ b/core/java/android/os/StatsBootstrapAtomValue.aidl
@@ -19,11 +19,36 @@
  *
  * @hide
  */
-union StatsBootstrapAtomValue {
-    boolean boolValue;
-    int intValue;
-    long longValue;
-    float floatValue;
-    String stringValue;
-    byte[] bytesValue;
-}
\ No newline at end of file
+parcelable StatsBootstrapAtomValue {
+    union Primitive {
+        boolean boolValue;
+        int intValue;
+        long longValue;
+        float floatValue;
+        String stringValue;
+        byte[] bytesValue;
+	String[] stringArrayValue;
+    }
+
+    Primitive value;
+
+    parcelable Annotation {
+        // Match the definitions in
+        // packages/modules/StatsD/framework/java/android/util/StatsLog.java
+        // Only supports UIDs for now.
+        @Backing(type="byte")
+        enum Id {
+            NONE,
+            IS_UID,
+        }
+        Id id;
+
+        union Primitive {
+            boolean boolValue;
+            int intValue;
+        }
+        Primitive value;
+    }
+
+    Annotation[] annotations;
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3ae9511..acf4a2f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -66,6 +66,7 @@
 import android.util.AndroidException;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
@@ -771,9 +772,10 @@
     public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
 
     /**
-     * When set on the admin user this specifies if the user can remove users.
+     * When set on the admin user this specifies if the user can remove secondary users. Managed
+     * profiles and private profiles can still be removed even if this is set on the admin user.
      * When set on a non-admin secondary user, this specifies if the user can remove itself.
-     * This restriction has no effect on managed profiles.
+     * This restriction has no effect when set on managed profiles.
      * The default value is <code>false</code>.
      *
      * <p>Holders of the permission
@@ -792,7 +794,8 @@
      * Specifies if managed profiles of this user can be removed, other than by its profile owner.
      * The default value is <code>false</code>.
      * <p>
-     * This restriction has no effect on managed profiles.
+     * This restriction has no effect on managed profiles, and this restriction does not block the
+     * removal of private profiles of this user.
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
@@ -3908,11 +3911,57 @@
             android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     public @NonNull UserProperties getUserProperties(@NonNull UserHandle userHandle) {
         final int userId = userHandle.getIdentifier();
-        // Avoid calling into system server for invalid user ids.
-        if (android.multiuser.Flags.fixGetUserPropertyCache() && userId < 0) {
+
+        if (userId < 0 && android.multiuser.Flags.fixGetUserPropertyCache()) {
+            // Avoid calling into system server for invalid user ids.
             throw new IllegalArgumentException("Cannot access properties for user " + userId);
         }
-        return mUserPropertiesCache.query(userId);
+
+        if (!android.multiuser.Flags.cacheUserPropertiesCorrectlyReadOnly() || userId < 0) {
+            // This is the historical code path, when all flags are false.
+            try {
+                return mService.getUserPropertiesCopy(userId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+
+        final int callingUid = Binder.getCallingUid();
+        final int processUid = Process.myUid();
+        if (Build.isDebuggable() && callingUid != processUid) {
+            Log.w(TAG, "Uid " + processUid + " is fetching a copy of UserProperties on"
+                            + " behalf of callingUid " + callingUid + ". Possibly"
+                            + " it should carefully first clearCallingIdentity or perhaps use"
+                            + " UserManagerInternal.getUserProperties() instead?",
+                    new Throwable());
+        }
+
+        return getUserPropertiesFromQuery(new QueryUserId(userId));
+    }
+
+    /** @hide */
+    public static final void invalidateUserPropertiesCache() {
+        UserManagerCache.invalidateUserPropertiesFromQuery();
+    }
+
+    /**
+     * Cachable version of {@link #getUserProperties(UserHandle)}, caching the UserProperties
+     * corresponding to the given QueryUserId. The cached copy depends on both the queried userId as
+     * well as the Binder caller querying it, since the result depends on the caller's permissions.
+     */
+    @CachedProperty()
+    private @NonNull UserProperties getUserPropertiesFromQuery(QueryUserId query) {
+        return ((UserManagerCache) mIpcDataCache).getUserPropertiesFromQuery(
+                (QueryUserId q) -> mService.getUserPropertiesCopy(q.getUserId()), query);
+    }
+
+    /** Class keeping track of a userId, as well as the callingUid that is asking about it. */
+    /** @hide */
+    static final class QueryUserId extends Pair<Integer, Integer> {
+        public QueryUserId(@UserIdInt int userId) {
+            super(Binder.getCallingUid(), userId);
+        }
+        public @UserIdInt int getUserId() { return second; }
     }
 
     /**
@@ -6368,8 +6417,12 @@
                 Settings.Global.DEVICE_DEMO_MODE, 0) > 0;
     }
 
-    /** @hide */
-    public static final void invalidateUserSerialNumberCache() {
+
+    /**
+     * This method is used to invalidate caches, when user was added or removed.
+     * @hide
+     */
+    public static final void invalidateCacheOnUserListChange() {
         UserManagerCache.invalidateUserSerialNumber();
     }
 
@@ -6382,7 +6435,7 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @CachedProperty(modsFlagOnOrNone = {})
+    @CachedProperty(modsFlagOnOrNone = {}, api = "user_manager_users")
     public int getUserSerialNumber(@UserIdInt int userId) {
         // Read only flag should is to fix early access to this API
         // cacheUserSerialNumber to be removed after the
@@ -6652,34 +6705,6 @@
         PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES);
     }
 
-    /* Cache key for UserProperties object. */
-    private static final String CACHE_KEY_USER_PROPERTIES =
-        PropertyInvalidatedCache.createPropertyName(
-            PropertyInvalidatedCache.MODULE_SYSTEM, "user_properties");
-
-    // TODO: It would be better to somehow have this as static, so that it can work cross-context.
-    private final PropertyInvalidatedCache<Integer, UserProperties> mUserPropertiesCache =
-            new PropertyInvalidatedCache<Integer, UserProperties>(16, CACHE_KEY_USER_PROPERTIES) {
-                @Override
-                public UserProperties recompute(Integer userId) {
-                    try {
-                        // If the userId doesn't exist, this will throw rather than cache garbage.
-                        return mService.getUserPropertiesCopy(userId);
-                    } catch (RemoteException re) {
-                        throw re.rethrowFromSystemServer();
-                    }
-                }
-                @Override
-                public boolean bypass(Integer query) {
-                    return query < 0;
-                }
-            };
-
-    /** @hide */
-    public static final void invalidateUserPropertiesCache() {
-        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_USER_PROPERTIES);
-    }
-
     /**
      * @hide
      * User that enforces a restriction.
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index ffc58c5..61dd11f 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -55,6 +55,7 @@
 import java.util.Objects;
 import java.util.StringJoiner;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 
 /**
  * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -565,6 +566,19 @@
     public abstract long getDuration();
 
     /**
+     * Gets the estimated duration of the segment for given vibrator, in milliseconds.
+     *
+     * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
+     * the estimated duration based on the given {@link VibratorInfo}. For all other effects this
+     * will return the same as {@link #getDuration()}.
+     *
+     * @hide
+     */
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        return getDuration();
+    }
+
+    /**
      * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
      *
      * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more
@@ -904,13 +918,23 @@
 
         @Override
         public long getDuration() {
+            return getDuration(VibrationEffectSegment::getDuration);
+        }
+
+        /** @hide */
+        @Override
+        public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+            return getDuration(segment -> segment.getDuration(vibratorInfo));
+        }
+
+        private long getDuration(Function<VibrationEffectSegment, Long> durationFn) {
             if (mRepeatIndex >= 0) {
                 return Long.MAX_VALUE;
             }
             int segmentCount = mSegments.size();
             long totalDuration = 0;
             for (int i = 0; i < segmentCount; i++) {
-                long segmentDuration = mSegments.get(i).getDuration();
+                long segmentDuration = durationFn.apply(mSegments.get(i));
                 if (segmentDuration < 0) {
                     return segmentDuration;
                 }
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 7327630..c4c4580 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -34,6 +34,7 @@
 import android.media.AudioAttributes;
 import android.os.vibrator.Flags;
 import android.os.vibrator.VibrationConfig;
+import android.os.vibrator.VibratorFrequencyProfile;
 import android.os.vibrator.VibratorFrequencyProfileLegacy;
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
@@ -303,6 +304,28 @@
     }
 
     /**
+     * Gets the profile that describes the vibrator output across the supported frequency range.
+     *
+     * <p>The profile describes the output acceleration that the device can reach when it
+     * vibrates at different frequencies.
+     *
+     * @return The frequency profile for this vibrator, or null if the vibrator does not have
+     * frequency control. If this vibrator is a composite of multiple physical devices then this
+     * will return a profile supported in all devices, or null if the intersection is empty or not
+     * available.
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    @Nullable
+    public VibratorFrequencyProfile getFrequencyProfile() {
+        VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile();
+        if (frequencyProfile.isEmpty()) {
+            return null;
+        }
+
+        return new VibratorFrequencyProfile(frequencyProfile);
+    }
+
+    /**
      * Return the maximum amplitude the vibrator can play using the audio haptic channels.
      *
      * <p>This is a positive value, or {@link Float#NaN NaN} if it's unknown. If this returns a
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index f7fff39..9419032 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -20,8 +20,10 @@
 import android.annotation.Nullable;
 import android.hardware.vibrator.Braking;
 import android.hardware.vibrator.IVibrator;
+import android.os.vibrator.Flags;
 import android.util.IndentingPrintWriter;
 import android.util.MathUtils;
+import android.util.Pair;
 import android.util.Range;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -30,8 +32,11 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.TreeMap;
 
 /**
  * A VibratorInfo describes the capabilities of a {@link Vibrator}.
@@ -60,6 +65,7 @@
     private final int mPwleSizeMax;
     private final float mQFactor;
     private final FrequencyProfileLegacy mFrequencyProfileLegacy;
+    private final FrequencyProfile mFrequencyProfile;
     private final int mMaxEnvelopeEffectSize;
     private final int mMinEnvelopeEffectControlPointDurationMillis;
     private final int mMaxEnvelopeEffectControlPointDurationMillis;
@@ -76,6 +82,7 @@
         mPwleSizeMax = in.readInt();
         mQFactor = in.readFloat();
         mFrequencyProfileLegacy = FrequencyProfileLegacy.CREATOR.createFromParcel(in);
+        mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
         mMaxEnvelopeEffectSize = in.readInt();
         mMinEnvelopeEffectControlPointDurationMillis = in.readInt();
         mMaxEnvelopeEffectControlPointDurationMillis = in.readInt();
@@ -87,7 +94,7 @@
                 baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
                 baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
                 baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfileLegacy,
-                baseVibratorInfo.mMaxEnvelopeEffectSize,
+                baseVibratorInfo.mFrequencyProfile, baseVibratorInfo.mMaxEnvelopeEffectSize,
                 baseVibratorInfo.mMinEnvelopeEffectControlPointDurationMillis,
                 baseVibratorInfo.mMaxEnvelopeEffectControlPointDurationMillis);
     }
@@ -114,6 +121,17 @@
      * @param qFactor                  The vibrator quality factor.
      * @param frequencyProfileLegacy   The description of the vibrator supported frequencies and max
      *                                 amplitude mappings.
+     * @param frequencyProfile       The description of the vibrator supported frequencies and
+     *                                 output acceleration mappings.
+     * @param maxEnvelopeEffectSize    The maximum number of control points supported for an
+     *                                 envelope effect.
+     * @param minEnvelopeEffectControlPointDurationMillis   The minimum duration supported
+     *                                                      between two control points within an
+     *                                                      envelope effect.
+     * @param maxEnvelopeEffectControlPointDurationMillis   The maximum duration supported
+     *                                                      between two control points within an
+     *                                                      envelope effect.
+     *
      * @hide
      */
     public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
@@ -121,10 +139,12 @@
             @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
             int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
             float qFactor, @NonNull FrequencyProfileLegacy frequencyProfileLegacy,
-            int maxEnvelopeEffectSize, int minEnvelopeEffectControlPointDurationMillis,
+            @NonNull FrequencyProfile frequencyProfile, int maxEnvelopeEffectSize,
+            int minEnvelopeEffectControlPointDurationMillis,
             int maxEnvelopeEffectControlPointDurationMillis) {
         Preconditions.checkNotNull(supportedPrimitives);
         Preconditions.checkNotNull(frequencyProfileLegacy);
+        Preconditions.checkNotNull(frequencyProfile);
         mId = id;
         mCapabilities = capabilities;
         mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
@@ -136,6 +156,7 @@
         mPwleSizeMax = pwleSizeMax;
         mQFactor = qFactor;
         mFrequencyProfileLegacy = frequencyProfileLegacy;
+        mFrequencyProfile = frequencyProfile;
         mMaxEnvelopeEffectSize = maxEnvelopeEffectSize;
         mMinEnvelopeEffectControlPointDurationMillis =
                 minEnvelopeEffectControlPointDurationMillis;
@@ -156,6 +177,7 @@
         dest.writeInt(mPwleSizeMax);
         dest.writeFloat(mQFactor);
         mFrequencyProfileLegacy.writeToParcel(dest, flags);
+        mFrequencyProfile.writeToParcel(dest, flags);
         dest.writeInt(mMaxEnvelopeEffectSize);
         dest.writeInt(mMinEnvelopeEffectControlPointDurationMillis);
         dest.writeInt(mMaxEnvelopeEffectControlPointDurationMillis);
@@ -206,6 +228,7 @@
                 && Objects.equals(mSupportedBraking, that.mSupportedBraking)
                 && Objects.equals(mQFactor, that.mQFactor)
                 && Objects.equals(mFrequencyProfileLegacy, that.mFrequencyProfileLegacy)
+                && Objects.equals(mFrequencyProfile, that.mFrequencyProfile)
                 && mMaxEnvelopeEffectSize == that.mMaxEnvelopeEffectSize
                 && mMinEnvelopeEffectControlPointDurationMillis
                 == that.mMinEnvelopeEffectControlPointDurationMillis
@@ -216,7 +239,7 @@
     @Override
     public int hashCode() {
         int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
-                mQFactor, mFrequencyProfileLegacy);
+                mQFactor, mFrequencyProfileLegacy, mFrequencyProfile);
         for (int i = 0; i < mSupportedPrimitives.size(); i++) {
             hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
             hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
@@ -239,6 +262,7 @@
                 + ", mPwleSizeMax=" + mPwleSizeMax
                 + ", mQFactor=" + mQFactor
                 + ", mFrequencyProfileLegacy=" + mFrequencyProfileLegacy
+                + ", mFrequencyProfile=" + mFrequencyProfile
                 + ", mMaxEnvelopeEffectSize=" + mMaxEnvelopeEffectSize
                 + ", mMinEnvelopeEffectControlPointDurationMillis="
                 + mMinEnvelopeEffectControlPointDurationMillis
@@ -263,6 +287,7 @@
         pw.println("pwleSizeMax = " + mPwleSizeMax);
         pw.println("q-factor = " + mQFactor);
         pw.println("frequencyProfileLegacy = " + mFrequencyProfileLegacy);
+        pw.println("frequencyProfile = " + mFrequencyProfile);
         pw.println("mMaxEnvelopeEffectSize = " + mMaxEnvelopeEffectSize);
         pw.println("mMinEnvelopeEffectControlPointDurationMillis = "
                 + mMinEnvelopeEffectControlPointDurationMillis);
@@ -517,6 +542,9 @@
      * this vibrator is a composite of multiple physical devices.
      */
     public float getResonantFrequencyHz() {
+        if (Flags.normalizedPwleEffects()) {
+            return mFrequencyProfile.mResonantFrequencyHz;
+        }
         return mFrequencyProfileLegacy.mResonantFrequencyHz;
     }
 
@@ -541,6 +569,17 @@
         return mFrequencyProfileLegacy;
     }
 
+    /**
+     * Gets the profile of supported frequencies, including the measurements of maximum
+     * output acceleration for supported vibration frequencies.
+     *
+     * <p>If the devices does not have frequency control then the profile should be empty.
+     */
+    @NonNull
+    public FrequencyProfile getFrequencyProfile() {
+        return mFrequencyProfile;
+    }
+
     /** Returns a single int representing all the capabilities of the vibrator. */
     public long getCapabilities() {
         return mCapabilities;
@@ -623,6 +662,304 @@
     }
 
     /**
+     * Describes the maximum output acceleration that can be achieved for each supported
+     * frequency in a specific vibrator.
+     *
+     * @hide
+     */
+    public static final class FrequencyProfile implements Parcelable {
+
+        private final float[] mFrequenciesHz;
+        private final float[] mOutputAccelerationsGs;
+        private final float mResonantFrequencyHz;
+        private final float mMaxOutputAccelerationGs;
+        private final float mMinFrequencyHz;
+        private final float mMaxFrequencyHz;
+
+        public FrequencyProfile(Parcel in) {
+            this(in.readFloat(), in.createFloatArray(), in.createFloatArray());
+        }
+
+        /**
+         * Default constructor.
+         *
+         * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
+         * @param frequenciesHz         The supported vibration frequencies, in hertz.
+         * @param outputAccelerationsGs The maximum achievable output acceleration (in Gs) the
+         *                              device can reach at the supported frequencies.
+         */
+        public FrequencyProfile(float resonantFrequencyHz, float[] frequenciesHz,
+                float[] outputAccelerationsGs) {
+
+            mResonantFrequencyHz = resonantFrequencyHz;
+
+            boolean isValid = !Float.isNaN(resonantFrequencyHz)
+                    && (resonantFrequencyHz > 0)
+                    && (frequenciesHz != null && outputAccelerationsGs != null)
+                    && (frequenciesHz.length == outputAccelerationsGs.length)
+                    && (frequenciesHz.length > 0);
+
+            if (!isValid) {
+                mFrequenciesHz = null;
+                mOutputAccelerationsGs = null;
+                mMinFrequencyHz = Float.NaN;
+                mMaxFrequencyHz = Float.NaN;
+                mMaxOutputAccelerationGs = Float.NaN;
+                return;
+            }
+
+            TreeMap<Float, Float> frequencyToOutputAccelerationMap = new TreeMap<>();
+
+            for (int i = 0; i < frequenciesHz.length; i++) {
+                frequencyToOutputAccelerationMap.putIfAbsent(frequenciesHz[i],
+                        outputAccelerationsGs[i]);
+            }
+
+            float[] frequencies = new float[frequencyToOutputAccelerationMap.size()];
+            float[] accelerations = new float[frequencyToOutputAccelerationMap.size()];
+            float maxOutputAccelerationGs = 0;
+            int i = 0;
+            for (Map.Entry<Float, Float> entry : frequencyToOutputAccelerationMap.entrySet()) {
+                frequencies[i] = entry.getKey();
+                accelerations[i] = entry.getValue();
+                maxOutputAccelerationGs = Math.max(maxOutputAccelerationGs, entry.getValue());
+                i++;
+            }
+
+            mFrequenciesHz = frequencies;
+            mOutputAccelerationsGs = accelerations;
+            mMinFrequencyHz = mFrequenciesHz[0];
+            mMaxFrequencyHz = mFrequenciesHz[mFrequenciesHz.length - 1];
+            mMaxOutputAccelerationGs = maxOutputAccelerationGs;
+        }
+
+        /** Returns true if the supported frequency range is null. */
+        public boolean isEmpty() {
+            return mFrequenciesHz == null;
+        }
+
+        /**
+         * Returns a list of available frequencies.
+         */
+        @Nullable
+        public float[] getFrequenciesHz() {
+            return mFrequenciesHz;
+        }
+
+        /** Returns the list of available output accelerations */
+        @Nullable
+        public float[] getOutputAccelerationsGs() {
+            return mOutputAccelerationsGs;
+        }
+
+        /** Maximum output acceleration reachable in Gs when amplitude is 1.0f. */
+        public float getMaxOutputAccelerationGs() {
+            return mMaxOutputAccelerationGs;
+        }
+
+        /**
+         * Calculates the maximum output acceleration for a given frequency using linear
+         * interpolation.
+         *
+         * @param frequencyHz frequency, in hertz, for query.
+         * @return the maximum output acceleration for the given frequency.
+         */
+        public float getOutputAccelerationGs(float frequencyHz) {
+            if (mFrequenciesHz == null) {
+                return Float.NaN;
+            }
+
+            if (frequencyHz < mMinFrequencyHz || frequencyHz > mMaxFrequencyHz) {
+                // Outside supported frequency range, not able to vibrate at this frequency.
+                return 0;
+            }
+
+            int idx = Arrays.binarySearch(mFrequenciesHz, frequencyHz);
+            if (idx >= 0) {
+                return mOutputAccelerationsGs[idx];
+            }
+
+            // This indicates that the value was not found in the list. Adjust index of the
+            // insertion point to be at the lower bound.
+            idx = -idx - 2;
+
+            // Linearly interpolate the output acceleration based on the frequency.
+            return MathUtils.constrainedMap(
+                    mOutputAccelerationsGs[idx], mOutputAccelerationsGs[idx + 1],
+                    mFrequenciesHz[idx], mFrequenciesHz[idx + 1],
+                    frequencyHz);
+        }
+
+        /** The minimum frequency supported, in hertz. */
+        public float getMinFrequencyHz() {
+            return mMinFrequencyHz;
+        }
+
+        /** The maximum frequency supported, in hertz. */
+        public float getMaxFrequencyHz() {
+            return mMaxFrequencyHz;
+        }
+
+        /**
+         * Returns the frequency range that supports the specified minimum output
+         * acceleration.
+         *
+         * @return The frequency range, or null if the specified acceleration
+         *         is not achievable on the device.
+         */
+        @Nullable
+        public Range<Float> getFrequencyRangeHz(float minOutputAcceleration) {
+            if (mFrequenciesHz == null || mOutputAccelerationsGs == null
+                    || minOutputAcceleration > mMaxOutputAccelerationGs) {
+                return null; // No frequency range available
+            }
+
+            if (minOutputAcceleration <= 0) {
+                return new Range<>(mMinFrequencyHz, mMaxFrequencyHz);
+            }
+
+            float minFrequency = Float.NaN;
+            float maxFrequency = Float.NaN;
+            int lowerFrequencyBoundIndex = 0;
+            // Find the lower frequency bound
+            for (int i = 0; i < mOutputAccelerationsGs.length; i++) {
+                if (mOutputAccelerationsGs[i] >= minOutputAcceleration) {
+                    if (i == 0) {
+                        minFrequency = mMinFrequencyHz;
+                    } else {
+                        minFrequency = MathUtils.constrainedMap(
+                                mFrequenciesHz[i - 1], mFrequenciesHz[i],
+                                mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i],
+                                minOutputAcceleration);
+                    }
+                    lowerFrequencyBoundIndex = i;
+                    break; // Found the lower bound
+                }
+            }
+
+            if (Float.isNaN(minFrequency)) {
+                // Lower bound was not found
+                return null;
+            }
+
+            // Find the upper frequency bound
+            for (int i = lowerFrequencyBoundIndex; i < mOutputAccelerationsGs.length; i++) {
+                if (mOutputAccelerationsGs[i] <= minOutputAcceleration) {
+                    maxFrequency = MathUtils.constrainedMap(
+                            mFrequenciesHz[i - 1], mFrequenciesHz[i],
+                            mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i],
+                            minOutputAcceleration);
+                    break; // Found the upper bound
+                }
+            }
+
+            if (Float.isNaN(maxFrequency)) {
+                // If the upper bound was not found, the specified output acceleration is
+                // achievable at all remaining frequencies.
+                maxFrequency = mMaxFrequencyHz;
+            }
+
+            return new Range<>(minFrequency, maxFrequency);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeFloat(mResonantFrequencyHz);
+            dest.writeFloatArray(mFrequenciesHz);
+            dest.writeFloatArray(mOutputAccelerationsGs);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof FrequencyProfile that)) {
+                return false;
+            }
+            return Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
+                    && Arrays.equals(mFrequenciesHz, that.mFrequenciesHz)
+                    && Arrays.equals(mOutputAccelerationsGs, that.mOutputAccelerationsGs);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mResonantFrequencyHz, Arrays.hashCode(mFrequenciesHz),
+                    Arrays.hashCode(mOutputAccelerationsGs));
+        }
+
+        @Override
+        public String toString() {
+            return "FrequencyProfile{"
+                    + "mResonantFrequency=" + mResonantFrequencyHz
+                    + ", mFrequenciesHz=" + Arrays.toString(mFrequenciesHz)
+                    + ", mOutputAccelerationsGs=" + Arrays.toString(mOutputAccelerationsGs)
+                    + ", mMinFrequencyHz=" + mMinFrequencyHz
+                    + ", mMaxFrequencyHz=" + mMaxFrequencyHz
+                    + ", mMaxOutputAccelerationGs=" + mMaxOutputAccelerationGs
+                    + '}';
+        }
+
+        @NonNull
+        public static final Creator<FrequencyProfile> CREATOR =
+                new Creator<FrequencyProfile>() {
+                    @Override
+                    public FrequencyProfile createFromParcel(Parcel in) {
+                        return new FrequencyProfile(in);
+                    }
+
+                    @Override
+                    public FrequencyProfile[] newArray(int size) {
+                        return new FrequencyProfile[size];
+                    }
+                };
+
+        private static void deduplicateAndSortList(List<Pair<Float, Float>> list) {
+            if (list == null || list.size() < 2) {
+                return; // Nothing to dedupe
+            }
+
+            list.sort(Comparator.comparing(pair -> pair.first));
+
+            // Remove duplicates from the list
+            int writeIndex = 1;
+            for (int i = 1; i < list.size(); i++) {
+                Pair<Float, Float> currentPair = list.get(i);
+                Pair<Float, Float> previousPair = list.get(writeIndex - 1);
+
+                if (currentPair.first.compareTo(previousPair.first) != 0) {
+                    list.set(writeIndex++, currentPair);
+                }
+            }
+            list.subList(writeIndex, list.size()).clear();
+        }
+
+        private static ArrayList<Pair<Float, Float>> extractFrequencyToOutputAccelerationData(
+                float[] frequencies, float[] outputAccelerations) {
+
+            if (frequencies == null || outputAccelerations == null
+                    || frequencies.length == 0
+                    || frequencies.length != outputAccelerations.length) {
+                return new ArrayList<>(); // Return empty list for invalid or mismatched data
+            }
+
+            ArrayList<Pair<Float, Float>> frequencyToOutputAccelerationList = new ArrayList<>(
+                    frequencies.length);
+            for (int i = 0; i < frequencies.length; i++) {
+                frequencyToOutputAccelerationList.add(
+                        new Pair<>(frequencies[i], outputAccelerations[i]));
+            }
+
+            return frequencyToOutputAccelerationList;
+        }
+    }
+
+    /**
      * Describes the maximum relative output acceleration that can be achieved for each supported
      * frequency in a specific vibrator.
      *
@@ -834,6 +1171,8 @@
         private float mQFactor = Float.NaN;
         private FrequencyProfileLegacy mFrequencyProfileLegacy =
                 new FrequencyProfileLegacy(Float.NaN, Float.NaN, Float.NaN, null);
+        private FrequencyProfile mFrequencyProfile = new FrequencyProfile(Float.NaN, null,
+                null);
         private int mMaxEnvelopeEffectSize;
         private int mMinEnvelopeEffectControlPointDurationMillis;
         private int mMaxEnvelopeEffectControlPointDurationMillis;
@@ -914,6 +1253,16 @@
         }
 
         /**
+         * Configure the vibrator frequency information like resonant frequency and frequency to
+         * output acceleration data.
+         */
+        @NonNull
+        public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) {
+            mFrequencyProfile = frequencyProfile;
+            return this;
+        }
+
+        /**
          * Configure the maximum number of control points supported for envelope effects on this
          * device.
          */
@@ -951,7 +1300,8 @@
             return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
                     mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
                     mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfileLegacy,
-                    mMaxEnvelopeEffectSize, mMinEnvelopeEffectControlPointDurationMillis,
+                    mFrequencyProfile, mMaxEnvelopeEffectSize,
+                    mMinEnvelopeEffectControlPointDurationMillis,
                     mMaxEnvelopeEffectControlPointDurationMillis);
         }
 
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 6a2daea..73a74b2 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -73,6 +73,9 @@
 
     private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 60000;
 
+    // How long we wait for an AppZygote to complete pre-loading; this is a pretty conservative
+    // value that we can probably decrease over time, but we want to be careful here.
+    public static volatile int sAppZygotePreloadTimeoutMs = 15000;
     /**
      * Use a relatively short delay, because for app zygote, this is in the critical path of
      * service launch.
@@ -1100,31 +1103,52 @@
     }
 
     /**
+     * Updates the timeout used when preloading code in the app-zygote
+     *
+     * @param timeoutMs timeout in milliseconds
+     */
+    public static void setAppZygotePreloadTimeout(int timeoutMs) {
+        Slog.i(LOG_TAG, "Changing app-zygote preload timeout to " + timeoutMs + " ms.");
+
+        sAppZygotePreloadTimeoutMs = timeoutMs;
+    }
+
+    /**
      * Instructs the zygote to pre-load the application code for the given Application.
      * Only the app zygote supports this function.
      */
     public boolean preloadApp(ApplicationInfo appInfo, String abi)
             throws ZygoteStartFailedEx, IOException {
         synchronized (mLock) {
+            int ret;
             ZygoteState state = openZygoteSocketIfNeeded(abi);
-            state.mZygoteOutputWriter.write("2");
-            state.mZygoteOutputWriter.newLine();
+            int previousSocketTimeout = state.mZygoteSessionSocket.getSoTimeout();
 
-            state.mZygoteOutputWriter.write("--preload-app");
-            state.mZygoteOutputWriter.newLine();
+            try {
+                state.mZygoteSessionSocket.setSoTimeout(sAppZygotePreloadTimeoutMs);
 
-            // Zygote args needs to be strings, so in order to pass ApplicationInfo,
-            // write it to a Parcel, and base64 the raw Parcel bytes to the other side.
-            Parcel parcel = Parcel.obtain();
-            appInfo.writeToParcel(parcel, 0 /* flags */);
-            String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
-            parcel.recycle();
-            state.mZygoteOutputWriter.write(encodedParcelData);
-            state.mZygoteOutputWriter.newLine();
+                state.mZygoteOutputWriter.write("2");
+                state.mZygoteOutputWriter.newLine();
 
-            state.mZygoteOutputWriter.flush();
+                state.mZygoteOutputWriter.write("--preload-app");
+                state.mZygoteOutputWriter.newLine();
 
-            return (state.mZygoteInputStream.readInt() == 0);
+                // Zygote args needs to be strings, so in order to pass ApplicationInfo,
+                // write it to a Parcel, and base64 the raw Parcel bytes to the other side.
+                Parcel parcel = Parcel.obtain();
+                appInfo.writeToParcel(parcel, 0 /* flags */);
+                String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
+                parcel.recycle();
+                state.mZygoteOutputWriter.write(encodedParcelData);
+                state.mZygoteOutputWriter.newLine();
+
+                state.mZygoteOutputWriter.flush();
+
+                ret = state.mZygoteInputStream.readInt();
+            } finally {
+                state.mZygoteSessionSocket.setSoTimeout(previousSocketTimeout);
+            }
+            return ret == 0;
         }
     }
 
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index a1bfe39..c7cc653 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -2,87 +2,7 @@
 container: "system"
 container: "system"
 
-flag {
-    name: "android_os_build_vanilla_ice_cream"
-    is_exported: true
-    namespace: "build"
-    description: "Feature flag for adding the VANILLA_ICE_CREAM constant."
-    bug: "264658905"
-}
-
-flag {
-    name: "state_of_health_public"
-    is_exported: true
-    namespace: "system_sw_battery"
-    description: "Feature flag for making state_of_health a public api."
-    bug: "288842045"
-}
-
-flag {
-    name: "disallow_cellular_null_ciphers_restriction"
-    namespace: "cellular_security"
-    description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices."
-    bug: "276752881"
-}
-
-flag {
-    name: "remove_app_profiler_pss_collection"
-    is_exported: true
-    namespace: "backstage_power"
-    description: "Replaces background PSS collection in AppProfiler with RSS"
-    bug: "297542292"
-}
-
-flag {
-    name: "allow_thermal_headroom_thresholds"
-    is_exported: true
-    namespace: "game"
-    description: "Enable thermal headroom thresholds API"
-    bug: "288119641"
-}
-
-# This flag guards the private space feature, its APIs, and some of the feature implementations. The flag android.multiuser.Flags.enable_private_space_features exclusively guards all the implementations.
-flag {
-    name: "allow_private_profile"
-    is_exported: true
-    namespace: "profile_experiences"
-    description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
-    bug: "299069460"
-    is_exported: true
-}
-
-flag {
-    name: "adpf_prefer_power_efficiency"
-    is_exported: true
-    namespace: "game"
-    description: "Guards the ADPF power efficiency API"
-    bug: "288117936"
-}
-
-flag {
-    name: "security_state_service"
-    is_exported: true
-    namespace: "dynamic_spl"
-    description: "Guards the Security State API."
-    bug: "302189431"
-}
-
-flag {
-    name: "ordered_broadcast_multiple_permissions"
-    is_exported: true
-    namespace: "bluetooth"
-    description: "Guards the Context.sendOrderedBroadcastMultiplePermissions API"
-    bug: "345802719"
-}
-
-flag {
-    name: "battery_saver_supported_check_api"
-    is_exported: true
-    namespace: "backstage_power"
-    description: "Guards a new API in PowerManager to check if battery saver is supported or not."
-    bug: "305067031"
-}
-
+# keep-sorted start block=yes newline_separated=yes
 flag {
     name: "adpf_gpu_report_actual_work_duration"
     is_exported: true
@@ -92,21 +12,6 @@
 }
 
 flag {
-    name: "adpf_use_fmq_channel"
-    namespace: "game"
-    description: "Guards use of the FMQ channel for ADPF"
-    bug: "315894228"
-}
-
-flag {
-    name: "adpf_use_fmq_channel_fixed"
-    namespace: "game"
-    description: "Guards use of the FMQ channel for ADPF with a readonly flag"
-    is_fixed_read_only: true
-    bug: "315894228"
-}
-
-flag {
     name: "adpf_hwui_gpu"
     namespace: "game"
     description: "Guards use of the FMQ channel for ADPF"
@@ -115,6 +20,13 @@
 }
 
 flag {
+    name: "adpf_measure_during_input_event_boost"
+    namespace: "game"
+    description: "Guards use of a boost when view measures during input events"
+    bug: "256549451"
+}
+
+flag {
     name: "adpf_obtainview_boost"
     namespace: "game"
     description: "Guards use of a boost in response to HWUI obtainView"
@@ -131,41 +43,67 @@
 }
 
 flag {
-    name: "adpf_measure_during_input_event_boost"
-    namespace: "game"
-    description: "Guards use of a boost when view measures during input events"
-    bug: "256549451"
-}
-
-flag {
-    name: "battery_service_support_current_adb_command"
-    namespace: "backstage_power"
-    description: "Whether or not BatteryService supports adb commands for Current values."
-    is_fixed_read_only: true
-    bug: "315037695"
-}
-
-flag {
-    name: "strict_mode_restricted_network"
-    namespace: "backstage_power"
-    description: "Guards StrictMode APIs for detecting restricted network access."
-    bug: "317250784"
-}
-
-flag {
-    name: "binder_frozen_state_change_callback"
+    name: "adpf_prefer_power_efficiency"
     is_exported: true
-    namespace: "system_performance"
-    description: "Guards the frozen state change callback API."
-    bug: "361157077"
+    namespace: "game"
+    description: "Guards the ADPF power efficiency API"
+    bug: "288117936"
 }
 
 flag {
-    name: "message_queue_tail_tracking"
-    namespace: "system_performance"
-    description: "track tail of message queue."
-    bug: "305311707"
+    name: "adpf_use_fmq_channel"
+    namespace: "game"
+    description: "Guards use of the FMQ channel for ADPF"
+    bug: "315894228"
+}
+
+flag {
+    name: "adpf_use_fmq_channel_fixed"
+    namespace: "game"
+    description: "Guards use of the FMQ channel for ADPF with a readonly flag"
     is_fixed_read_only: true
+    bug: "315894228"
+}
+
+flag {
+    name: "allow_consentless_bugreport_delegated_consent"
+    namespace: "crumpet"
+    description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"
+    bug: "324046728"
+}
+
+# This flag guards the private space feature, its APIs, and some of the feature implementations. The flag android.multiuser.Flags.enable_private_space_features exclusively guards all the implementations.
+flag {
+    name: "allow_private_profile"
+    is_exported: true
+    namespace: "profile_experiences"
+    description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
+    bug: "299069460"
+    is_exported: true
+}
+
+flag {
+    name: "allow_thermal_headroom_thresholds"
+    is_exported: true
+    namespace: "game"
+    description: "Enable thermal headroom thresholds API"
+    bug: "288119641"
+}
+
+flag {
+    name: "android_os_build_vanilla_ice_cream"
+    is_exported: true
+    namespace: "build"
+    description: "Feature flag for adding the VANILLA_ICE_CREAM constant."
+    bug: "264658905"
+}
+
+flag {
+    name: "api_for_backported_fixes"
+    namespace: "media_reliability"
+    description: "Public API app developers use to check if a known issue is fixed on a device."
+    bug: "308461809"
+    is_exported: true
 }
 
 flag {
@@ -178,35 +116,42 @@
 }
 
 flag {
-    name: "storage_lifetime_api"
+    name: "battery_saver_supported_check_api"
     is_exported: true
-    namespace: "phoenix"
-    description: "Feature flag for adding storage component health APIs."
+    namespace: "backstage_power"
+    description: "Guards a new API in PowerManager to check if battery saver is supported or not."
+    bug: "305067031"
+}
+
+flag {
+    name: "battery_service_support_current_adb_command"
+    namespace: "backstage_power"
+    description: "Whether or not BatteryService supports adb commands for Current values."
     is_fixed_read_only: true
-    bug: "309792384"
+    bug: "315037695"
 }
 
 flag {
-     namespace: "system_performance"
-     name: "telemetry_apis_framework_initialization"
-     is_exported: true
-     description: "Control framework initialization APIs of telemetry APIs feature."
-     is_fixed_read_only: true
-     bug: "324241334"
+    name: "binder_frozen_state_change_callback"
+    is_exported: true
+    namespace: "system_performance"
+    description: "Guards the frozen state change callback API."
+    bug: "361157077"
 }
 
 flag {
-     namespace: "system_performance"
-     name: "perfetto_sdk_tracing"
-     description: "Tracing using Perfetto SDK."
-     bug: "303199244"
+    name: "disallow_cellular_null_ciphers_restriction"
+    namespace: "cellular_security"
+    description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices."
+    bug: "276752881"
 }
 
 flag {
-    name: "allow_consentless_bugreport_delegated_consent"
-    namespace: "crumpet"
-    description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"
-    bug: "324046728"
+    name: "enable_angle_allow_list"
+    namespace: "gpu"
+    description: "Whether to read from angle allowlist to determine if app should use ANGLE"
+    is_fixed_read_only: true
+    bug: "370845648"
 }
 
 flag {
@@ -226,9 +171,83 @@
 }
 
 flag {
+    name: "message_queue_tail_tracking"
+    namespace: "system_performance"
+    description: "track tail of message queue."
+    bug: "305311707"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "network_time_uses_shared_memory"
     namespace: "system_performance"
     description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory"
     bug: "361329788"
     is_exported: true
 }
+
+flag {
+    name: "ordered_broadcast_multiple_permissions"
+    is_exported: true
+    namespace: "bluetooth"
+    description: "Guards the Context.sendOrderedBroadcastMultiplePermissions API"
+    bug: "345802719"
+}
+
+flag {
+    name: "remove_app_profiler_pss_collection"
+    is_exported: true
+    namespace: "backstage_power"
+    description: "Replaces background PSS collection in AppProfiler with RSS"
+    bug: "297542292"
+}
+
+flag {
+    name: "security_state_service"
+    is_exported: true
+    namespace: "dynamic_spl"
+    description: "Guards the Security State API."
+    bug: "302189431"
+}
+
+flag {
+    name: "state_of_health_public"
+    is_exported: true
+    namespace: "system_sw_battery"
+    description: "Feature flag for making state_of_health a public api."
+    bug: "288842045"
+}
+
+flag {
+    name: "storage_lifetime_api"
+    is_exported: true
+    namespace: "phoenix"
+    description: "Feature flag for adding storage component health APIs."
+    is_fixed_read_only: true
+    bug: "309792384"
+}
+
+flag {
+    name: "strict_mode_restricted_network"
+    namespace: "backstage_power"
+    description: "Guards StrictMode APIs for detecting restricted network access."
+    bug: "317250784"
+}
+
+flag {
+     namespace: "system_performance"
+     name: "perfetto_sdk_tracing"
+     description: "Tracing using Perfetto SDK."
+     bug: "303199244"
+}
+
+flag {
+     namespace: "system_performance"
+     name: "telemetry_apis_framework_initialization"
+     is_exported: true
+     description: "Control framework initialization APIs of telemetry APIs feature."
+     is_fixed_read_only: true
+     bug: "324241334"
+}
+
+# keep-sorted end
diff --git a/core/java/android/os/vibrator/MultiVibratorInfo.java b/core/java/android/os/vibrator/MultiVibratorInfo.java
index 37dae56..1ba8d99 100644
--- a/core/java/android/os/vibrator/MultiVibratorInfo.java
+++ b/core/java/android/os/vibrator/MultiVibratorInfo.java
@@ -27,6 +27,9 @@
 import android.util.SparseIntArray;
 
 import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
 import java.util.function.Function;
 
 /**
@@ -44,13 +47,18 @@
     private static final float EPSILON = 1e-5f;
 
     public MultiVibratorInfo(int id, VibratorInfo[] vibrators) {
-        this(id, vibrators, frequencyProfileIntersection(vibrators));
+        this(id, vibrators, frequencyProfileLegacyIntersection(vibrators),
+                frequencyProfileIntersection(vibrators));
     }
 
     private MultiVibratorInfo(
-            int id, VibratorInfo[] vibrators, FrequencyProfileLegacy mergedProfile) {
+            int id, VibratorInfo[] vibrators,
+            VibratorInfo.FrequencyProfileLegacy mergedLegacyProfile,
+            FrequencyProfile mergedProfile) {
         super(id,
-                capabilitiesIntersection(vibrators, mergedProfile.isEmpty()),
+                capabilitiesIntersection(vibrators,
+                        Flags.normalizedPwleEffects() ? mergedProfile.isEmpty()
+                                : mergedLegacyProfile.isEmpty()),
                 supportedEffectsIntersection(vibrators),
                 supportedBrakingIntersection(vibrators),
                 supportedPrimitivesAndDurationsIntersection(vibrators),
@@ -59,6 +67,7 @@
                 integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
                 integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
                 floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
+                mergedLegacyProfile,
                 mergedProfile,
                 integerLimitIntersection(vibrators,
                         VibratorInfo::getMaxEnvelopeEffectSize),
@@ -209,7 +218,82 @@
     }
 
     @NonNull
-    private static FrequencyProfileLegacy frequencyProfileIntersection(VibratorInfo[] infos) {
+    private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) {
+        if (infos == null || infos.length == 0) {
+            return new FrequencyProfile(Float.NaN,
+                    /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null);
+        }
+
+        float resonantFreq = floatPropertyIntersection(infos, VibratorInfo::getResonantFrequencyHz);
+
+        if (Float.isNaN(resonantFreq)) {
+            return new FrequencyProfile(Float.NaN,
+                    /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null);
+        }
+
+        float minFrequency = 0.0f;
+        float maxFrequency = Float.MAX_VALUE;
+        Set<Float> allFrequencies = new TreeSet<>(); // Using TreeSet for automatic sorting
+
+        for (VibratorInfo info : infos) {
+            float newMinFrequency = info.getFrequencyProfile().getMinFrequencyHz();
+            float newMaxFrequency = info.getFrequencyProfile().getMaxFrequencyHz();
+
+            if (Float.isNaN(newMinFrequency) || Float.isNaN(newMaxFrequency)) {
+                // If one vibrator is undefined then the intersection is undefined.
+                return new FrequencyProfile(Float.NaN,
+                        /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null);
+            }
+
+            minFrequency = Math.max(minFrequency, newMinFrequency);
+            maxFrequency = Math.min(maxFrequency, newMaxFrequency);
+
+            if (info.getFrequencyProfile().getFrequenciesHz() == null) {
+                return new FrequencyProfile(Float.NaN,
+                        /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null);
+            }
+
+            for (float frequency : info.getFrequencyProfile().getFrequenciesHz()) {
+                allFrequencies.add(frequency);
+            }
+        }
+
+        if (minFrequency > maxFrequency) {
+            // If the range and intersection are disjoint then the intersection is undefined
+            return new FrequencyProfile(Float.NaN,
+                    /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null);
+        }
+
+        // Trim frequencies to the min/max range
+        Iterator<Float> iterator = allFrequencies.iterator();
+        while (iterator.hasNext()) {
+            float frequency = iterator.next();
+            if (frequency < minFrequency || frequency > maxFrequency) {
+                iterator.remove();
+            }
+        }
+
+        float[] frequencies = new float[allFrequencies.size()];
+        float[] accelerations = new float[allFrequencies.size()];
+        int idx = 0;
+
+        for (Float frequency : allFrequencies) {
+            float outputAcceleration = Float.MAX_VALUE;
+            for (VibratorInfo info : infos) {
+                // This will find the mapped value or interpolate it if needed.
+                outputAcceleration = Math.min(outputAcceleration,
+                        info.getFrequencyProfile().getOutputAccelerationGs(frequency));
+            }
+            frequencies[idx] = frequency;
+            accelerations[idx] = outputAcceleration;
+            idx++;
+        }
+
+        return new FrequencyProfile(resonantFreq, frequencies, accelerations);
+    }
+
+    @NonNull
+    private static FrequencyProfileLegacy frequencyProfileLegacyIntersection(VibratorInfo[] infos) {
         float freqResolution = floatPropertyIntersection(infos,
                 info -> info.getFrequencyProfileLegacy().getFrequencyResolutionHz());
         float resonantFreq = floatPropertyIntersection(infos,
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 39f8412..b17e82a 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -16,6 +16,17 @@
 
 package android.os.vibrator;
 
+import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_THUD;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
+import static android.os.VibrationEffect.EFFECT_DOUBLE_CLICK;
+import static android.os.VibrationEffect.EFFECT_HEAVY_CLICK;
+import static android.os.VibrationEffect.EFFECT_POP;
+import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
+import static android.os.VibrationEffect.EFFECT_THUD;
+import static android.os.VibrationEffect.EFFECT_TICK;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -78,6 +89,32 @@
 
     /** @hide */
     @Override
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        if (vibratorInfo == null) {
+            return getDuration();
+        }
+        return switch (mEffectId) {
+            case EFFECT_TICK,
+                 EFFECT_CLICK,
+                 EFFECT_HEAVY_CLICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_CLICK);
+            case EFFECT_TEXTURE_TICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_TICK);
+            case EFFECT_THUD -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_THUD);
+            case EFFECT_DOUBLE_CLICK -> {
+                long clickDuration = vibratorInfo.getPrimitiveDuration(PRIMITIVE_CLICK);
+                yield clickDuration > 0 ? 2 * clickDuration : getDuration();
+            }
+            default -> getDuration();
+        };
+    }
+
+    private long estimateFromPrimitiveDuration(VibratorInfo vibratorInfo, int primitiveId) {
+        int duration = vibratorInfo.getPrimitiveDuration(primitiveId);
+        // Unsupported primitives should be ignored here.
+        return duration > 0 ? duration : getDuration();
+    }
+
+    /** @hide */
+    @Override
     public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
         if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
             return true;
@@ -89,34 +126,30 @@
         }
         // The vibrator does not have hardware support for the effect, but the effect has fallback
         // support. Check if a fallback will be available for the effect ID.
-        switch (mEffectId) {
-            case VibrationEffect.EFFECT_CLICK:
-            case VibrationEffect.EFFECT_DOUBLE_CLICK:
-            case VibrationEffect.EFFECT_HEAVY_CLICK:
-            case VibrationEffect.EFFECT_TICK:
-                // Any of these effects are always supported via some form of fallback.
-                return true;
-            default:
-                return false;
-        }
+        return switch (mEffectId) {
+            // Any of these effects are always supported via some form of fallback.
+            case EFFECT_CLICK,
+                 EFFECT_DOUBLE_CLICK,
+                 EFFECT_HEAVY_CLICK,
+                 EFFECT_TICK -> true;
+            default -> false;
+        };
     }
 
     /** @hide */
     @Override
     public boolean isHapticFeedbackCandidate() {
-        switch (mEffectId) {
-            case VibrationEffect.EFFECT_CLICK:
-            case VibrationEffect.EFFECT_DOUBLE_CLICK:
-            case VibrationEffect.EFFECT_HEAVY_CLICK:
-            case VibrationEffect.EFFECT_POP:
-            case VibrationEffect.EFFECT_TEXTURE_TICK:
-            case VibrationEffect.EFFECT_THUD:
-            case VibrationEffect.EFFECT_TICK:
-                return true;
-            default:
-                // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
-                return false;
-        }
+        return switch (mEffectId) {
+            case EFFECT_CLICK,
+                 EFFECT_DOUBLE_CLICK,
+                 EFFECT_HEAVY_CLICK,
+                 EFFECT_POP,
+                 EFFECT_TEXTURE_TICK,
+                 EFFECT_THUD,
+                 EFFECT_TICK -> true;
+            // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
+            default -> false;
+        };
     }
 
     /** @hide */
@@ -153,27 +186,25 @@
     }
 
     private static boolean isValidEffectStrength(int strength) {
-        switch (strength) {
-            case VibrationEffect.EFFECT_STRENGTH_LIGHT:
-            case VibrationEffect.EFFECT_STRENGTH_MEDIUM:
-            case VibrationEffect.EFFECT_STRENGTH_STRONG:
-                return true;
-            default:
-                return false;
-        }
+        return switch (strength) {
+            case VibrationEffect.EFFECT_STRENGTH_LIGHT,
+                 VibrationEffect.EFFECT_STRENGTH_MEDIUM,
+                 VibrationEffect.EFFECT_STRENGTH_STRONG -> true;
+            default -> false;
+        };
     }
 
     /** @hide */
     @Override
     public void validate() {
         switch (mEffectId) {
-            case VibrationEffect.EFFECT_CLICK:
-            case VibrationEffect.EFFECT_DOUBLE_CLICK:
-            case VibrationEffect.EFFECT_HEAVY_CLICK:
-            case VibrationEffect.EFFECT_POP:
-            case VibrationEffect.EFFECT_TEXTURE_TICK:
-            case VibrationEffect.EFFECT_THUD:
-            case VibrationEffect.EFFECT_TICK:
+            case EFFECT_CLICK:
+            case EFFECT_DOUBLE_CLICK:
+            case EFFECT_HEAVY_CLICK:
+            case EFFECT_POP:
+            case EFFECT_TEXTURE_TICK:
+            case EFFECT_THUD:
+            case EFFECT_TICK:
                 break;
             default:
                 int[] ringtones = VibrationEffect.RINGTONES;
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 3c84bcd..91653ed 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -77,6 +77,16 @@
 
     /** @hide */
     @Override
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        if (vibratorInfo == null) {
+            return getDuration();
+        }
+        int duration = vibratorInfo.getPrimitiveDuration(mPrimitiveId);
+        return duration > 0 ? duration + mDelay : getDuration();
+    }
+
+    /** @hide */
+    @Override
     public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
         return vibratorInfo.isPrimitiveSupported(mPrimitiveId);
     }
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index e6e5a27..88be96a 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -86,6 +86,7 @@
     private final int mDefaultKeyboardVibrationIntensity;
 
     private final boolean mKeyboardVibrationSettingsSupported;
+    private final int mVibrationPipelineMaxDurationMs;
 
     /** @hide */
     public VibrationConfig(@Nullable Resources resources) {
@@ -106,6 +107,8 @@
                 com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger);
         mKeyboardVibrationSettingsSupported = loadBoolean(resources,
                 com.android.internal.R.bool.config_keyboardVibrationSettingsSupported);
+        mVibrationPipelineMaxDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationPipelineMaxDuration, 0);
 
         mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
                 com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -221,6 +224,23 @@
     }
 
     /**
+     * The max duration, in milliseconds, allowed for pipelining vibration requests.
+     *
+     * <p>If the ongoing vibration duration is shorter than this threshold then it should be allowed
+     * to finish before the next vibration can start. If the ongoing vibration is longer than this
+     * then it should be cancelled when it's superseded for the new one.
+     *
+     * @return the max duration allowed for vibration effect to finish before the next request, or
+     * zero to disable effect pipelining.
+     */
+    public int getVibrationPipelineMaxDurationMs() {
+        if (mVibrationPipelineMaxDurationMs < 0) {
+            return 0;
+        }
+        return mVibrationPipelineMaxDurationMs;
+    }
+
+    /**
      * Whether or not vibrations are ignored if the device is on a wireless charger.
      *
      * <p>This may be the case if vibration during wireless charging causes unwanted results, like
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index e1fb4e3..dadc849 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -17,6 +17,7 @@
 package android.os.vibrator;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -58,10 +59,23 @@
      */
     public abstract long getDuration();
 
-   /**
-     * Checks if a given {@link Vibrator} can play this segment as intended. See
-     * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about
-     * what counts as supported by a vibrator, and what counts as not.
+    /**
+     * Gets the estimated duration of the segment for given vibrator, in milliseconds.
+     *
+     * <p>For segments with hardware-dependent constants (e.g. primitives), this returns the
+     * estimated duration based on the given {@link VibratorInfo}. For all other effects this will
+     * return the same as {@link #getDuration()}.
+     *
+     * @hide
+     */
+    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+        return getDuration();
+    }
+
+    /**
+     * Checks if a given {@link android.os.Vibrator} can play this segment as intended. See
+     * {@link android.os.Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more
+     * information about what counts as supported by a vibrator, and what counts as not.
      *
      * @hide
      */
diff --git a/core/java/android/os/vibrator/VibratorFrequencyProfile.java b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
new file mode 100644
index 0000000..2b5f9bf
--- /dev/null
+++ b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.VibratorInfo;
+import android.util.Range;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Describes the output of a {@link android.os.Vibrator} for different vibration frequencies.
+ *
+ * <p>The profile contains the vibrator's frequency range (minimum/maximum) and maximum
+ * acceleration, enabling retrieval of supported acceleration levels for specific frequencies, if
+ * the device supports independent frequency control.
+ *
+ * <p>It also describes the max output acceleration (Gs), of a vibration at different supported
+ * frequencies (Hz).
+ *
+ * <p>Vibrators without independent frequency control do not have a frequency profile.
+ */
+@FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+public final class VibratorFrequencyProfile {
+
+    private final VibratorInfo.FrequencyProfile mFrequencyProfile;
+    private final SparseArray<Float> mFrequenciesOutputAcceleration;
+
+    /** @hide */
+    public VibratorFrequencyProfile(@NonNull VibratorInfo.FrequencyProfile frequencyProfile) {
+        Objects.requireNonNull(frequencyProfile);
+        Preconditions.checkArgument(!frequencyProfile.isEmpty(),
+                "Frequency profile must not be empty");
+        mFrequencyProfile = frequencyProfile;
+        mFrequenciesOutputAcceleration = generateFrequencyToAccelerationMap(
+                frequencyProfile.getFrequenciesHz(), frequencyProfile.getOutputAccelerationsGs());
+    }
+
+    /**
+     * Returns a {@link SparseArray} representing the vibrator's output acceleration capabilities
+     * across different frequencies. This map defines the maximum acceleration
+     * the vibrator can achieve at each supported frequency.
+     * <p>The map's keys are frequencies in Hz, and the corresponding values
+     * are the maximum achievable output accelerations in Gs.
+     *
+     * @return A map of frequencies (Hz) to maximum accelerations (Gs).
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    @NonNull
+    public SparseArray<Float> getFrequenciesOutputAcceleration() {
+        return mFrequenciesOutputAcceleration;
+    }
+
+    /**
+     * Returns the maximum output acceleration (in Gs) supported by the vibrator.
+     * This value represents the highest acceleration the vibrator can achieve
+     * across its entire frequency range.
+     *
+     * @return The maximum output acceleration in Gs.
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public float getMaxOutputAccelerationGs() {
+        return mFrequencyProfile.getMaxOutputAccelerationGs();
+    }
+
+    /**
+     * Returns the frequency range (in Hz) where the vibrator can sustain at least
+     * the given minimum output acceleration (Gs).
+     *
+     * @param minOutputAccelerationGs The minimum desired output acceleration in Gs.
+     * @return A {@link Range} object representing the frequency range where the
+     *         vibrator can sustain at least the given minimum acceleration, or null if
+     *         the minimum output acceleration cannot be achieved.
+     *
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    @Nullable
+    public Range<Float> getFrequencyRange(float minOutputAccelerationGs) {
+        return mFrequencyProfile.getFrequencyRangeHz(minOutputAccelerationGs);
+    }
+
+    /**
+     * Returns the output acceleration (in Gs) for the given frequency (Hz).
+     * This method provides the actual acceleration the vibrator will produce
+     * when operating at the specified frequency, using linear interpolation over
+     * the {@link #getFrequenciesOutputAcceleration()}.
+     *
+     * @param frequencyHz The frequency in Hz.
+     * @return The output acceleration in Gs for the given frequency.
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public float getOutputAccelerationGs(float frequencyHz) {
+        return mFrequencyProfile.getOutputAccelerationGs(frequencyHz);
+    }
+
+    /**
+     * Gets the minimum frequency supported by the vibrator.
+     *
+     * @return the minimum frequency supported by the vibrator, in hertz.
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public float getMinFrequencyHz() {
+        return mFrequencyProfile.getMinFrequencyHz();
+    }
+
+    /**
+     * Gets the maximum frequency supported by the vibrator.
+     *
+     * @return the maximum frequency supported by the vibrator, in hertz.
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public float getMaxFrequencyHz() {
+        return mFrequencyProfile.getMaxFrequencyHz();
+    }
+
+    private static SparseArray<Float> generateFrequencyToAccelerationMap(
+            float[] frequencies, float[] accelerations) {
+        SparseArray<Float> sparseArray = new SparseArray<>(frequencies.length);
+
+        for (int i = 0; i < frequencies.length; i++) {
+            int frequency = (int) frequencies[i];
+            float acceleration = accelerations[i];
+
+            sparseArray.put(frequency,
+                    Math.min(acceleration, sparseArray.get(frequency, Float.MAX_VALUE)));
+
+        }
+
+        return sparseArray;
+    }
+}
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index e3b1221..7ceb948 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -123,4 +123,25 @@
     metadata {
         purpose: PURPOSE_FEATURE
     }
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "haptics"
+    name: "primitive_composition_absolute_delay"
+    is_exported: true
+    description: "Enables functionality to create primitive compositions with absolute delays"
+    bug: "373357740"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
+
+flag {
+    namespace: "haptics"
+    name: "vibration_pipeline_enabled"
+    description: "Enables functionality to pipeline vibration effects to avoid cancelling short vibrations"
+    bug: "344494220"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index bca5bcc..bfefba5b 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -206,17 +206,6 @@
 }
 
 flag {
-    name: "apex_signature_permission_allowlist_enabled"
-    is_fixed_read_only: true
-    namespace: "permissions"
-    description: "Enable reading signature permission allowlist from APEXes"
-    bug: "308573169"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "check_op_validate_package"
     namespace: "permissions"
     description: "Validate package/uid match in checkOp similar to noteOp"
@@ -243,6 +232,13 @@
 }
 
 flag {
+  name: "sync_on_op_noted_api"
+  namespace: "permissions"
+  description: "New setOnOpNotedCallback API to allow subscribing to only sync ops."
+  bug: "372910217"
+}
+
+flag {
     name: "wallet_role_icon_property_enabled"
     is_exported: true
     namespace: "wallet_integration"
@@ -266,3 +262,23 @@
     description: "This fixed read-only flag is used to enable replacing permission BODY_SENSORS (and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE (and READ_HEALTH_DATA_IN_BACKGROUND)"
     bug: "364638912"
 }
+
+flag {
+    name: "delay_uid_state_changes_from_capability_updates"
+    is_fixed_read_only: true
+    namespace: "permissions"
+    description: "If proc state is decreasing over the restriction threshold and capability is changed, delay if no new capabilities are added"
+    bug: "347891382"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "platform_skin_temperature_enabled"
+    is_fixed_read_only: true
+    is_exported: true
+    namespace: "android_health_services"
+    description: "This fixed read-only flag is used to enable platform support for Skin Temperature."
+    bug: "369872443"
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 98d58d0..5ecf361 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3209,7 +3209,11 @@
                     return new DefaultAccountAndState(DEFAULT_ACCOUNT_STATE_NOT_SET, null);
                 }
 
-                private static boolean isCloudOrSimAccount(@DefaultAccountState int state) {
+                /**
+                 *
+                 * @hide
+                 */
+                public static boolean isCloudOrSimAccount(@DefaultAccountState int state) {
                     return state == DEFAULT_ACCOUNT_STATE_CLOUD
                             || state == DEFAULT_ACCOUNT_STATE_SIM;
                 }
@@ -3287,23 +3291,20 @@
                 Bundle response = nullSafeCall(resolver, ContactsContract.AUTHORITY_URI,
                         QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD, null, null);
 
-                int defaultContactsAccountState = response.getInt(KEY_DEFAULT_ACCOUNT_STATE, -1);
-                if (defaultContactsAccountState
-                        == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD) {
+                int defaultAccountState = response.getInt(KEY_DEFAULT_ACCOUNT_STATE, -1);
+                if (DefaultAccountAndState.isCloudOrSimAccount(defaultAccountState)) {
                     String accountName = response.getString(Settings.ACCOUNT_NAME);
                     String accountType = response.getString(Settings.ACCOUNT_TYPE);
                     if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
                         throw new IllegalStateException(
                                 "account name and type cannot be null or empty");
                     }
-                    return new DefaultAccountAndState(
-                            DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD,
+                    return new DefaultAccountAndState(defaultAccountState,
                             new Account(accountName, accountType));
-                } else if (defaultContactsAccountState
-                        == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_LOCAL
-                        || defaultContactsAccountState
+                } else if (defaultAccountState == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_LOCAL
+                        || defaultAccountState
                         == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_NOT_SET) {
-                    return new DefaultAccountAndState(defaultContactsAccountState, /*cloudAccount=*/
+                    return new DefaultAccountAndState(defaultAccountState, /*account=*/
                             null);
                 } else {
                     throw new IllegalStateException("Invalid default account state");
@@ -3348,12 +3349,11 @@
                 Bundle extras = new Bundle();
 
                 extras.putInt(KEY_DEFAULT_ACCOUNT_STATE, defaultAccountAndState.getState());
-                if (defaultAccountAndState.getState()
-                        == DefaultAccountAndState.DEFAULT_ACCOUNT_STATE_CLOUD) {
-                    Account cloudAccount = defaultAccountAndState.getAccount();
-                    assert cloudAccount != null;
-                    extras.putString(Settings.ACCOUNT_NAME, cloudAccount.name);
-                    extras.putString(Settings.ACCOUNT_TYPE, cloudAccount.type);
+                if (DefaultAccountAndState.isCloudOrSimAccount(defaultAccountAndState.getState())) {
+                    Account account = defaultAccountAndState.getAccount();
+                    assert account != null;
+                    extras.putString(Settings.ACCOUNT_NAME, account.name);
+                    extras.putString(Settings.ACCOUNT_TYPE, account.type);
                 }
                 nullSafeCall(resolver, ContactsContract.AUTHORITY_URI,
                         SET_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD, null, extras);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1a15d09..0be5584 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2351,6 +2351,11 @@
 
     /**
      * Activity Action: Show the permission screen for allowing apps to post promoted notifications.
+     * Properly formatted priority notifications are elevated in appearance. For example they may be
+     * able to use colors, have richer progress bars, show as chips in the status bar, and/or
+     * permanently appear on always-on-displays. This functionality is intended to be reserved for
+     * user initiated ongoing activities like navigation, phone calls, and ride sharing.
+     *
      * <p>
      *     Input: {@link #EXTRA_APP_PACKAGE}, the package to display.
      * <p>
@@ -6205,6 +6210,25 @@
         public static final String TOUCHPAD_RIGHT_CLICK_ZONE = "touchpad_right_click_zone";
 
         /**
+         * Whether to enable reversed vertical scrolling for connected mice.
+         *
+         * When enabled, scrolling down on the mouse wheel will move the screen up and vice versa.
+         * @hide
+         */
+        public static final String MOUSE_REVERSE_VERTICAL_SCROLLING =
+                "mouse_reverse_vertical_scrolling";
+
+        /**
+         * Whether to enable swapping the primary button for connected mice.
+         *
+         * When enabled, right clicking will be the primary button and left clicking will be the
+         * secondary button (e.g. show menu).
+         * @hide
+         */
+        public static final String MOUSE_SWAP_PRIMARY_BUTTON =
+                "mouse_swap_primary_button";
+
+        /**
          * Pointer fill style, specified by
          * {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants.
          *
@@ -6442,6 +6466,8 @@
             PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION);
             PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION_COLOR);
             PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE);
+            PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING);
+            PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON);
         }
 
         /**
@@ -12819,6 +12845,12 @@
          */
         @Readable
         public static final String CONTEXTUAL_SEARCH_PACKAGE = "contextual_search_package";
+
+        /**
+         * Inetger property which determines whether advanced protection is on or not.
+         * @hide
+         */
+        public static final String ADVANCED_PROTECTION_MODE = "advanced_protection_mode";
     }
 
     /**
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
new file mode 100644
index 0000000..59dd680f
--- /dev/null
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.advancedprotection;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.security.Flags;
+import android.util.Log;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * <p>Advanced Protection is a mode that users can enroll their device into, that enhances security
+ * by enabling features and restrictions across both the platform and user apps.
+ *
+ * <p>This class provides methods to query and control the advanced protection mode
+ * for the device.
+ */
+@FlaggedApi(Flags.FLAG_AAPM_API)
+@SystemService(Context.ADVANCED_PROTECTION_SERVICE)
+public class AdvancedProtectionManager {
+    private static final String TAG = "AdvancedProtectionM";
+
+    private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback>
+            mCallbackMap = new ConcurrentHashMap<>();
+
+    @NonNull
+    private final IAdvancedProtectionService mService;
+
+    /** @hide */
+    public AdvancedProtectionManager(@NonNull IAdvancedProtectionService service) {
+        mService = service;
+    }
+
+    /**
+     * Checks if advanced protection is enabled on the device.
+     *
+     * @return {@code true} if advanced protection is enabled, {@code false} otherwise.
+     */
+    @RequiresPermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE)
+    public boolean isAdvancedProtectionEnabled() {
+        try {
+            return mService.isAdvancedProtectionEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers a {@link Callback} to be notified of changes to the Advanced Protection state.
+     *
+     * <p>The provided callback will be called on the specified executor with the updated
+     * {@link AdvancedProtectionState}. Methods are called when the state changes, as well as once
+     * on initial registration.
+     *
+     * @param executor The executor of where the callback will execute.
+     * @param callback The {@link Callback} object to register..
+     */
+    @RequiresPermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE)
+    public void registerAdvancedProtectionCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Callback callback) {
+        if (mCallbackMap.get(callback) != null) {
+            Log.d(TAG, "registerAdvancedProtectionCallback callback already present");
+            return;
+        }
+
+        IAdvancedProtectionCallback delegate = new IAdvancedProtectionCallback.Stub() {
+            @Override
+            public void onAdvancedProtectionChanged(boolean enabled) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callback.onAdvancedProtectionChanged(enabled));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        };
+
+        try {
+            mService.registerAdvancedProtectionCallback(delegate);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        mCallbackMap.put(callback, delegate);
+    }
+
+    /**
+     * Unregister an existing {@link Callback}.
+     *
+     * @param callback The {@link Callback} object to unregister.
+     */
+    @RequiresPermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE)
+    public void unregisterAdvancedProtectionCallback(@NonNull Callback callback) {
+        IAdvancedProtectionCallback delegate = mCallbackMap.get(callback);
+        if (delegate == null) {
+            Log.d(TAG, "unregisterAdvancedProtectionCallback callback not present");
+            return;
+        }
+
+        try {
+            mService.unregisterAdvancedProtectionCallback(delegate);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        mCallbackMap.remove(callback);
+    }
+
+    /**
+     * Enables or disables advanced protection on the device.
+     *
+     * @param enabled {@code true} to enable advanced protection, {@code false} to disable it.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE)
+    public void setAdvancedProtectionEnabled(boolean enabled) {
+        try {
+            mService.setAdvancedProtectionEnabled(enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * A callback class for monitoring changes to Advanced Protection state
+     *
+     * <p>To register a callback, implement this interface, and register it with
+     * {@link AdvancedProtectionManager#registerAdvancedProtectionCallback(Executor, Callback)}.
+     * Methods are called when the state changes, as well as once on initial registration.
+     */
+    @FlaggedApi(Flags.FLAG_AAPM_API)
+    public interface Callback {
+        /**
+         * Called when advanced protection state changes
+         * @param enabled the new state
+         */
+        void onAdvancedProtectionChanged(boolean enabled);
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/core/java/android/security/advancedprotection/IAdvancedProtectionCallback.aidl
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to core/java/android/security/advancedprotection/IAdvancedProtectionCallback.aidl
index 2f5daaa..828ce47 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionCallback.aidl
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package android.security.advancedprotection;
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+/**
+ * A callback class for monitoring changes to Advanced Protection state
+ * @hide
+ */
+oneway interface IAdvancedProtectionCallback {
+    void onAdvancedProtectionChanged(boolean enabled);
+}
\ No newline at end of file
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
new file mode 100644
index 0000000..ef0abf4
--- /dev/null
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.advancedprotection;
+
+import android.security.advancedprotection.IAdvancedProtectionCallback;
+
+/**
+ * Binder interface for apps to communicate with system server implementations of
+ * AdvancedProtectionService.
+ * @hide
+ */
+interface IAdvancedProtectionService {
+    @EnforcePermission("QUERY_ADVANCED_PROTECTION_MODE")
+    boolean isAdvancedProtectionEnabled();
+    @EnforcePermission("QUERY_ADVANCED_PROTECTION_MODE")
+    void registerAdvancedProtectionCallback(IAdvancedProtectionCallback callback);
+    @EnforcePermission("QUERY_ADVANCED_PROTECTION_MODE")
+    void unregisterAdvancedProtectionCallback(IAdvancedProtectionCallback callback);
+    @EnforcePermission("SET_ADVANCED_PROTECTION_MODE")
+    void setAdvancedProtectionEnabled(boolean enabled);
+}
\ No newline at end of file
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index aedf8e0..1d35344 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -113,3 +113,10 @@
     description: "AFL feature"
     bug: "365994454"
 }
+
+flag {
+    name: "keystore_grant_api"
+    namespace: "hardware_backed_security"
+    description: "Feature flag for exposing KeyStore grant APIs"
+    bug: "351158708"
+}
diff --git a/core/java/android/security/forensic/IForensicService.aidl b/core/java/android/security/forensic/IForensicService.aidl
new file mode 100644
index 0000000..a944b18
--- /dev/null
+++ b/core/java/android/security/forensic/IForensicService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.forensic;
+
+import android.security.forensic.IForensicServiceCommandCallback;
+import android.security.forensic.IForensicServiceStateCallback;
+
+/**
+ * Binder interface to communicate with ForensicService.
+ * @hide
+ */
+interface IForensicService {
+    void monitorState(IForensicServiceStateCallback callback);
+    void makeVisible(IForensicServiceCommandCallback callback);
+    void makeInvisible(IForensicServiceCommandCallback callback);
+    void enable(IForensicServiceCommandCallback callback);
+    void disable(IForensicServiceCommandCallback callback);
+}
diff --git a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
new file mode 100644
index 0000000..7fa0c7f
--- /dev/null
+++ b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.forensic;
+
+/**
+ * @hide
+ */
+ oneway interface IForensicServiceCommandCallback {
+     @Backing(type="int")
+     enum ErrorCode{
+         UNKNOWN = 0,
+         PERMISSION_DENIED = 1,
+         INVALID_STATE_TRANSITION = 2,
+         BACKUP_TRANSPORT_UNAVAILABLE = 3,
+         DATA_SOURCE_UNAVAILABLE = 3,
+     }
+    void onSuccess();
+    void onFailure(ErrorCode error);
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to core/java/android/security/forensic/IForensicServiceStateCallback.aidl
index 2f5daaa..0cda350 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package android.security.forensic;
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+/**
+ * @hide
+ */
+ oneway interface IForensicServiceStateCallback {
+    @Backing(type="int")
+    enum State{
+        UNKNOWN = 0,
+        INVISIBLE = 1,
+        VISIBLE = 2,
+        ENABLED = 3,
+    }
+    void onStateChange(State state);
+ }
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 5457bbe..b593902a9 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -33,7 +33,6 @@
   }
 }
 
-
 flag {
     name: "content_uri_permission_apis"
     is_exported: true
@@ -65,6 +64,14 @@
 }
 
 flag {
+    name: "aapm_api"
+    namespace: "responsible_apis"
+    description: "Android Advanced Protection Mode Service and Manager"
+    bug: "352420507"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "prevent_intent_redirect"
     namespace: "responsible_apis"
     description: "Prevent intent redirect attacks"
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 37a91e7..b384b66 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -58,7 +58,6 @@
     void onSuggestedReplySent(String key, in CharSequence reply, int source);
     void onActionClicked(String key, in Notification.Action action, int source);
     void onNotificationClicked(String key);
-    // @deprecated changing allowed adjustments is no longer supported.
     void onAllowedAdjustmentsChanged();
     void onNotificationFeedbackReceived(String key, in NotificationRankingUpdate update, in Bundle feedback);
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 7b7058e..091b25a 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -319,10 +319,7 @@
      * their notifications the assistant can modify.
      * <p> Query {@link NotificationManager#getAllowedAssistantAdjustments()} to see what
      * {@link Adjustment adjustments} you are currently allowed to make.</p>
-     *
-     * @deprecated changing allowed adjustments is no longer supported.
      */
-    @Deprecated
     public void onAllowedAdjustmentsChanged() {
     }
 
diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp
deleted file mode 100644
index 552a07d..0000000
--- a/core/java/android/service/wallpaper/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-package {
-    default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_library {
-
-    name: "WallpaperSharedLib",
-    srcs: [
-        "*.java",
-        "I*.aidl",
-    ],
-
-    libs: ["unsupportedappusage"],
-
-    // Enforce that the library is built against java 8 so that there are
-    // no compatibility issues with launcher
-    java_version: "1.8",
-}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 5ac0c50..e8ef9d6 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1671,14 +1671,22 @@
         }
 
         /** @hide */
-        public final void onCallBackModeStarted(
-                @TelephonyManager.EmergencyCallbackModeType int type) {
+        public final void onCallbackModeStarted(
+                @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis,
+                int subId) {
             // not support. Can't override. Use TelephonyCallback.
         }
 
         /** @hide */
-        public final void onCallBackModeStopped(@EmergencyCallbackModeType int type,
-                @EmergencyCallbackModeStopReason int reason) {
+        public final void onCallbackModeRestarted(
+                @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis,
+                int subId) {
+            // not support. Can't override. Use TelephonyCallback.
+        }
+
+        /** @hide */
+        public final void onCallbackModeStopped(@EmergencyCallbackModeType int type,
+                @EmergencyCallbackModeStopReason int reason, int subId) {
             // not support. Can't override. Use TelephonyCallback.
         }
 
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index c360e64..14d5800 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -41,6 +41,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.time.Duration;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -619,16 +620,20 @@
 
 
     /**
-     * Event for changes to the Emergency callback mode
+     * Event for changes to the emergency callback mode
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
-     * @see EmergencyCallbackModeListener#onCallbackModeStarted(int)
-     * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int)
+     * @see EmergencyCallbackModeListener#onCallbackModeStarted(int, Duration, int)
+     * @see EmergencyCallbackModeListener#onCallbackModeRestarted(int, Duration, int)
+     * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int, int)
      *
      * @hide
      */
+
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
     public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40;
 
     /**
@@ -1671,39 +1676,64 @@
     }
 
     /**
-     * Interface for emergency callback mode listener.
+     * Interface for the emergency callback mode listener.
      *
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public interface EmergencyCallbackModeListener {
         /**
-         * Indicates that Callback Mode has been started.
+         * Indicates that emergency callback mode has been started.
          * <p>
-         * This method will be called when an emergency sms/emergency call is sent
-         * and the callback mode is supported by the carrier.
-         * If an emergency SMS is transmitted during callback mode for SMS, this API will be called
-         * once again with TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS.
+         * This method will be called when an emergency SMS or emergency call is ended and
+         * the emergency callback mode is supported by the carrier.
+         * If the emergency callback mode was started for an emergency call and an emergency SMS is
+         * transmitted during callback mode for SMS then this API will be called once again with
+         * TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS.
          *
-         * @param type for callback mode entry
+         * @param type for the emergency callback mode entry
          *             See {@link TelephonyManager.EmergencyCallbackModeType}.
          * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL
          * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS
+         *
+         * @param timerDuration is the time remaining in the emergency callback mode.
+         * @param subId The subscription ID used to start the emergency callback mode.
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type);
+        void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type,
+                @NonNull Duration timerDuration, int subId);
 
         /**
-         * Indicates that Callback Mode has been stopped.
+         * Indicates that emergency callback mode has been re-started.
          * <p>
-         * This method will be called when the callback mode timer expires or when
-         * a normal call/SMS is sent
+         * This method will be called when an emergency SMS or emergency call is ended
+         * in the emergency callback mode.
+         * This is used to restart the emergency callback mode when it is already in progress.
          *
-         * @param type for callback mode entry
+         * @param type for the emergency callback mode entry
+         *             See {@link TelephonyManager.EmergencyCallbackModeType}.
          * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL
          * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS
          *
-         * @param reason for changing callback mode
+         * @param timerDuration is the time remaining in the emergency callback mode.
+         * @param subId The subscription ID used to restart the emergency callback mode.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type,
+                @NonNull Duration timerDuration, int subId);
+
+        /**
+         * Indicates that emergency callback mode has been stopped.
+         * <p>
+         * This method will be called when the emergency callback mode timer expires or when
+         * a normal call/SMS is sent
          *
+         * @param type for the emergency callback mode entry
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS
+         *
+         * @param reason for changing emergency callback mode
          * @see TelephonyManager#STOP_REASON_UNKNOWN
          * @see TelephonyManager#STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED
          * @see TelephonyManager#STOP_REASON_NORMAL_SMS_SENT
@@ -1711,10 +1741,12 @@
          * @see TelephonyManager#STOP_REASON_EMERGENCY_SMS_SENT
          * @see TelephonyManager#STOP_REASON_TIMER_EXPIRED
          * @see TelephonyManager#STOP_REASON_USER_ACTION
+         *
+         * @param subId is the current subscription used the emergency callback mode.
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
-                @TelephonyManager.EmergencyCallbackModeStopReason int reason);
+        void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
+                @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId);
     }
 
     /**
@@ -2132,18 +2164,43 @@
                             mediaQualityStatus)));
         }
 
-        public void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type) {
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        public void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type,
+                long durationMillis, int subId) {
+            if (!Flags.emergencyCallbackModeNotification()) return;
+
             EmergencyCallbackModeListener listener =
                     (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get();
             Log.d(LOG_TAG, "onCallBackModeStarted:type=" + type + ", listener=" + listener);
             if (listener == null) return;
 
+            final Duration timerDuration = Duration.ofMillis(durationMillis);
             Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> listener.onCallBackModeStarted(type)));
+                    () -> mExecutor.execute(() -> listener.onCallbackModeStarted(type,
+                            timerDuration, subId)));
         }
 
-        public void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
-                @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        public void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type,
+                long durationMillis, int subId) {
+            if (!Flags.emergencyCallbackModeNotification()) return;
+
+            EmergencyCallbackModeListener listener =
+                    (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get();
+            Log.d(LOG_TAG, "onCallbackModeRestarted:type=" + type + ", listener=" + listener);
+            if (listener == null) return;
+
+            final Duration timerDuration = Duration.ofMillis(durationMillis);
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallbackModeRestarted(type,
+                            timerDuration, subId)));
+        }
+
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        public void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
+                @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId) {
+            if (!Flags.emergencyCallbackModeNotification()) return;
+
             EmergencyCallbackModeListener listener =
                     (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get();
             Log.d(LOG_TAG, "onCallBackModeStopped:type=" + type
@@ -2151,7 +2208,8 @@
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> listener.onCallBackModeStopped(type, reason)));
+                    () -> mExecutor.execute(() -> listener.onCallbackModeStopped(type, reason,
+                            subId)));
         }
 
         public void onCarrierRoamingNtnModeChanged(boolean active) {
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 10f03c1..3c7e924 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -115,7 +115,6 @@
                 ICarrierConfigChangeListener>
             mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>();
 
-
     /** @hide **/
     public TelephonyRegistryManager(@NonNull Context context) {
         mContext = context;
@@ -1721,13 +1720,36 @@
      * @param subId Sender subscription ID.
      * @param type for callback mode entry.
      *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+     * @param durationMillis is the number of milliseconds remaining in the emergency callback
+     *                        mode.
      * @hide
      */
-    public void notifyCallBackModeStarted(int phoneId, int subId,
-            @TelephonyManager.EmergencyCallbackModeType int type) {
+    public void notifyCallbackModeStarted(int phoneId, int subId,
+            @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis) {
         try {
-            Log.d(TAG, "notifyCallBackModeStarted:type=" + type);
-            sRegistry.notifyCallbackModeStarted(phoneId, subId, type);
+            Log.d(TAG, "notifyCallbackModeStarted:type=" + type);
+            sRegistry.notifyCallbackModeStarted(phoneId, subId, type, durationMillis);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify Callback Mode has been restarted.
+     * @param phoneId Sender phone ID.
+     * @param subId Sender subscription ID.
+     * @param type for callback mode entry.
+     *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+     * @param durationMillis is the number of milliseconds remaining in the emergency callback
+     *                        mode.
+     * @hide
+     */
+    public void notifyCallbackModeRestarted(int phoneId, int subId,
+            @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis) {
+        try {
+            Log.d(TAG, "notifyCallbackModeRestarted:type=" + type);
+            sRegistry.notifyCallbackModeRestarted(phoneId, subId, type, durationMillis);
         } catch (RemoteException ex) {
             // system process is dead
             throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 0dec13f..e254bf3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -73,8 +73,11 @@
 public abstract class Layout {
 
     // These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
-    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 4f;
-    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0.2f;
+    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 0f;
+    private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0f;
+    private static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP = 5f;
+    // since we're not using soft light yet, this needs to be much lower than the spec'd 0.8
+    private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.5f;
 
     /** @hide */
     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
@@ -1025,11 +1028,18 @@
 
         var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX,
                 mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR);
+        var cornerRadius = mPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP;
+
+        // We set the alpha on the color itself instead of Paint.setAlpha(), because that function
+        // actually mutates the color in... *ehem* very strange ways. Also the color might get reset
+        // for various reasons, which also resets the alpha.
+        var white = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 1f, 1f, 1f);
+        var black = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 0f, 0f, 0f);
 
         var originalTextColor = mPaint.getColor();
         var bgPaint = mWorkPlainPaint;
         bgPaint.reset();
-        bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? Color.WHITE : Color.BLACK);
+        bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? white : black);
         bgPaint.setStyle(Paint.Style.FILL);
 
         int start = getLineStart(firstLine);
@@ -1082,7 +1092,12 @@
                     private void drawRect() {
                         if (!mLineBackground.isEmpty()) {
                             mLineBackground.inset(-padding, -padding);
-                            canvas.drawRect(mLineBackground, bgPaint);
+                            canvas.drawRoundRect(
+                                    mLineBackground,
+                                    cornerRadius,
+                                    cornerRadius,
+                                    bgPaint
+                            );
                         }
                     }
 
@@ -1104,7 +1119,7 @@
                         if (hasColorChanged) {
                             mLastColor = textColor;
 
-                            return isHighContrastTextDark(textColor) ? Color.WHITE : Color.BLACK;
+                            return isHighContrastTextDark(textColor) ? white : black;
                         }
 
                         return bgPaint.getColor();
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 9f54d9f..3adbd68 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -24,7 +24,7 @@
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.Network;
-import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
 import android.net.SntpClient;
 import android.os.Build;
 import android.os.SystemClock;
@@ -687,16 +687,8 @@
             if (connectivityManager == null) {
                 return false;
             }
-            final NetworkCapabilities networkCapabilities =
-                    connectivityManager.getNetworkCapabilities(network);
-            if (networkCapabilities == null) {
-                if (LOGD) Log.d(TAG, "getNetwork: failed to get network capabilities");
-                return false;
-            }
-            final boolean isConnectedToInternet = networkCapabilities.hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                    && networkCapabilities.hasCapability(
-                    NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+            final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
+
             // This connectivity check is to avoid performing a DNS lookup for the time server on a
             // unconnected network. There are races to obtain time in Android when connectivity
             // changes, which means that forceRefresh() can be called by various components before
@@ -706,8 +698,8 @@
             // A side effect of check is that tests that run a fake NTP server on the device itself
             // will only be able to use it if the active network is connected, even though loopback
             // addresses are actually reachable.
-            if (!isConnectedToInternet) {
-                if (LOGD) Log.d(TAG, "getNetwork: no internet connectivity");
+            if (ni == null || !ni.isConnected()) {
+                if (LOGD) Log.d(TAG, "getNetwork: no connectivity");
                 return false;
             }
             return true;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8a8022c..e940e55bd 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -21,6 +21,7 @@
 import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API;
 
 import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API;
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT;
 
 import android.Manifest;
 import android.annotation.FlaggedApi;
@@ -1266,6 +1267,18 @@
     }
 
     /**
+     * Returns whether display supports adaptive refresh rate or not.
+     */
+    // TODO(b/372526856) Add a link to the documentation for ARR.
+    @FlaggedApi(FLAG_ENABLE_HAS_ARR_SUPPORT)
+    public boolean hasArrSupport() {
+        synchronized (mLock) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.hasArrSupport;
+        }
+    }
+
+    /**
      * <p> Returns true if the connected display can be switched into a mode with minimal
      * post processing. </p>
      *
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index cac3e3c..26fce90 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -198,6 +198,12 @@
     public float renderFrameRate;
 
     /**
+     * If {@code true} this Display supports adaptive refresh rates.
+     * // TODO(b/372526856) Add a link to the documentation for ARR.
+     */
+    public boolean hasArrSupport;
+
+    /**
      * The default display mode.
      */
     public int defaultModeId;
@@ -436,6 +442,7 @@
                 && rotation == other.rotation
                 && modeId == other.modeId
                 && renderFrameRate == other.renderFrameRate
+                && hasArrSupport == other.hasArrSupport
                 && defaultModeId == other.defaultModeId
                 && userPreferredModeId == other.userPreferredModeId
                 && Arrays.equals(supportedModes, other.supportedModes)
@@ -497,6 +504,7 @@
         rotation = other.rotation;
         modeId = other.modeId;
         renderFrameRate = other.renderFrameRate;
+        hasArrSupport = other.hasArrSupport;
         defaultModeId = other.defaultModeId;
         userPreferredModeId = other.userPreferredModeId;
         supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
@@ -553,6 +561,7 @@
         rotation = source.readInt();
         modeId = source.readInt();
         renderFrameRate = source.readFloat();
+        hasArrSupport = source.readBoolean();
         defaultModeId = source.readInt();
         userPreferredModeId = source.readInt();
         int nModes = source.readInt();
@@ -626,6 +635,7 @@
         dest.writeInt(rotation);
         dest.writeInt(modeId);
         dest.writeFloat(renderFrameRate);
+        dest.writeBoolean(hasArrSupport);
         dest.writeInt(defaultModeId);
         dest.writeInt(userPreferredModeId);
         dest.writeInt(supportedModes.length);
@@ -871,6 +881,8 @@
         sb.append(modeId);
         sb.append(", renderFrameRate ");
         sb.append(renderFrameRate);
+        sb.append(", hasArrSupport ");
+        sb.append(hasArrSupport);
         sb.append(", defaultMode ");
         sb.append(defaultModeId);
         sb.append(", userPreferredModeId ");
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 229e8ee7..4f74198 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -221,13 +221,13 @@
 
     @Override
     public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes,
-            int[] hideTypes) {
+            int[] hideTypes, int[] cancelTypes) {
         if (Flags.refactorInsetsController()) {
-            return super.setControl(control, showTypes, hideTypes);
+            return super.setControl(control, showTypes, hideTypes, cancelTypes);
         } else {
             ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl",
                     mController.getHost().getInputMethodManager(), null /* icProto */);
-            if (!super.setControl(control, showTypes, hideTypes)) {
+            if (!super.setControl(control, showTypes, hideTypes, cancelTypes)) {
                 return false;
             }
             if (control == null && !mIsRequestedVisibleAwaitingLeash) {
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 97facc1..4fead2a 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -371,6 +371,7 @@
         mPendingInsets = mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN
                 ? mShownInsets : mHiddenInsets;
         mPendingAlpha = 1f;
+        mPendingFraction = 1f;
         applyChangeInsets(null);
         mCancelled = true;
         mListener.onCancelled(mReadyDispatched ? this : null);
@@ -486,6 +487,17 @@
         if (controls == null) {
             return;
         }
+
+        final boolean visible = mPendingFraction == 0
+                // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is
+                // animated from the hidden state.
+                ? mAnimationType != ANIMATION_TYPE_SHOW
+                : mPendingFraction < 1f || (mFinished
+                        ? mShownOnFinish
+                        // If the animation is cancelled, mFinished and mShownOnFinish are not set.
+                        // Here uses mLayoutInsetsDuringAnimation to decide if it should be visible.
+                        : mLayoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
+
         // TODO: Implement behavior when inset spans over multiple types
         for (int i = controls.size() - 1; i >= 0; i--) {
             final InsetsSourceControl control = controls.valueAt(i);
@@ -498,12 +510,6 @@
             }
             addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame);
 
-            // The first frame of ANIMATION_TYPE_SHOW should be invisible since it is animated from
-            // the hidden state.
-            final boolean visible = mPendingFraction == 0
-                    ? mAnimationType != ANIMATION_TYPE_SHOW
-                    : !mFinished || mShownOnFinish;
-
             if (outState != null && source != null) {
                 outState.addSource(new InsetsSource(source)
                         .setVisible(visible)
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8ac5532..59c6653 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -22,6 +22,7 @@
 import static android.view.InsetsControllerProto.STATE;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
+import static android.view.ViewProtoLogGroups.IME_INSETS_CONTROLLER;
 import static android.view.WindowInsets.Type.FIRST;
 import static android.view.WindowInsets.Type.LAST;
 import static android.view.WindowInsets.Type.all;
@@ -69,6 +70,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.ImeTracing;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.TriFunction;
 
 import java.io.PrintWriter;
@@ -957,6 +959,7 @@
         int consumedControlCount = 0;
         final @InsetsType int[] showTypes = new int[1];
         final @InsetsType int[] hideTypes = new int[1];
+        final @InsetsType int[] cancelTypes = new int[1];
         ImeTracker.Token statsToken = null;
 
         // Ensure to update all existing source consumers
@@ -982,7 +985,7 @@
 
             // control may be null, but we still need to update the control to null if it got
             // revoked.
-            consumer.setControl(control, showTypes, hideTypes);
+            consumer.setControl(control, showTypes, hideTypes, cancelTypes);
         }
 
         // Ensure to create source consumers if not available yet.
@@ -990,7 +993,7 @@
             for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
                 final InsetsSourceControl control = mTmpControlArray.valueAt(i);
                 getSourceConsumer(control.getId(), control.getType())
-                        .setControl(control, showTypes, hideTypes);
+                        .setControl(control, showTypes, hideTypes, cancelTypes);
             }
         }
 
@@ -1002,6 +1005,10 @@
         }
         mTmpControlArray.clear();
 
+        if (cancelTypes[0] != 0) {
+            cancelExistingControllers(cancelTypes[0]);
+        }
+
         // Do not override any animations that the app started in the OnControllableInsetsChanged
         // listeners.
         int animatingTypes = invokeControllableInsetsChangedListeners();
@@ -1915,6 +1922,8 @@
         final @InsetsType int requestedVisibleTypes =
                 (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
         if (mRequestedVisibleTypes != requestedVisibleTypes) {
+            ProtoLog.d(IME_INSETS_CONTROLLER, "Setting requestedVisibleTypes to %d (was %d)",
+                    requestedVisibleTypes, mRequestedVisibleTypes);
             mRequestedVisibleTypes = requestedVisibleTypes;
         }
     }
@@ -2154,12 +2163,12 @@
                         new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(),
                                 null /* leash */, false /* initialVisible */,
                                 new Point(), Insets.NONE),
-                        new int[1], new int[1]);
+                        new int[1], new int[1], new int[1]);
             } else {
                 mState.removeSource(ID_IME_CAPTION_BAR);
                 InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR);
                 if (sourceConsumer != null) {
-                    sourceConsumer.setControl(null, new int[1], new int[1]);
+                    sourceConsumer.setControl(null, new int[1], new int[1], new int[1]);
                 }
             }
             mHost.notifyInsetsChanged();
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index da788a7..17f33c1 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -122,7 +122,7 @@
 
     /**
      * Updates the control delivered from the server.
-
+     *
      * @param showTypes An integer array with a single entry that determines which types a show
      *                  animation should be run after setting the control.
      * @param hideTypes An integer array with a single entry that determines which types a hide
@@ -130,7 +130,7 @@
      * @return Whether the control has changed from the server
      */
     public boolean setControl(@Nullable InsetsSourceControl control,
-            @InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
+            @InsetsType int[] showTypes, @InsetsType int[] hideTypes, int[] cancelTypes) {
         if (Objects.equals(mSourceControl, control)) {
             if (mSourceControl != null && mSourceControl != control) {
                 mSourceControl.release(SurfaceControl::release);
@@ -165,6 +165,12 @@
             // Reset the applier to the default one which has the most lightweight implementation.
             setSurfaceParamsApplier(InsetsAnimationControlRunner.SurfaceParamsApplier.DEFAULT);
         } else {
+            if (lastControl != null && InsetsSource.getInsetSide(lastControl.getInsetsHint())
+                    != InsetsSource.getInsetSide(control.getInsetsHint())) {
+                // The source has been moved to a different side. The coordinates are stale.
+                // Canceling existing animation if there is any.
+                cancelTypes[0] |= mType;
+            }
             final boolean requestedVisible = isRequestedVisibleAwaitingControl();
             final SurfaceControl oldLeash = lastControl != null ? lastControl.getLeash() : null;
             final SurfaceControl newLeash = control.getLeash();
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 5c41516..67050e0 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -38,7 +38,7 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
+import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 326e34b..bd6ff4c 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -20,7 +20,6 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -36,8 +35,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import com.android.hardware.input.Flags;
-
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
@@ -4436,7 +4433,6 @@
          * @see android.view.View#requestUnbufferedDispatch(int)
          * @see android.view.View#requestUnbufferedDispatch(MotionEvent)
          */
-        @FlaggedApi(Flags.FLAG_POINTER_COORDS_IS_RESAMPLED_API)
         public boolean isResampled() {
             return isResampled;
         }
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 1ea58bc..80484a6 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -88,6 +88,7 @@
 per-file OnReceiveContentListener.java = file:/core/java/android/widget/OWNERS
 per-file ContentInfo.java = file:/core/java/android/service/autofill/OWNERS
 per-file ContentInfo.java = file:/core/java/android/widget/OWNERS
+per-file view_flags.aconfig = file:/services/core/java/com/android/server/wm/OWNERS
 
 # WindowManager
 per-file ContentRecordingSession.aidl = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java
index 5f6d5e2..59c2598 100644
--- a/core/java/android/view/RoundScrollbarRenderer.java
+++ b/core/java/android/view/RoundScrollbarRenderer.java
@@ -16,17 +16,26 @@
 
 package android.view;
 
+import static android.util.MathUtils.acos;
+
+import static java.lang.Math.sin;
+
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.SystemProperties;
 import android.util.DisplayMetrics;
+import android.view.flags.Flags;
 
 /**
  * Helper class for drawing round scroll bars on round Wear devices.
+ *
+ * @hide
  */
-class RoundScrollbarRenderer {
+public class RoundScrollbarRenderer {
+    private static final String BLUECHIP_ENABLED_SYSPROP = "persist.cw_build.bluechip.enabled";
     // The range of the scrollbar position represented as an angle in degrees.
     private static final float SCROLLBAR_ANGLE_RANGE = 28.8f;
     private static final float MAX_SCROLLBAR_ANGLE_SWIPE = 26.3f; // 90%
@@ -45,12 +54,15 @@
     private final Paint mTrackPaint = new Paint();
     private final RectF mRect = new RectF();
     private final View mParent;
-    private final int mMaskThickness;
+    private final float mInset;
 
     private float mPreviousMaxScroll = 0;
     private float mMaxScrollDiff = 0;
     private float mPreviousCurrentScroll = 0;
     private float mCurrentScrollDiff = 0;
+    private float mThumbStrokeWidthAsDegrees = 0;
+    private boolean mDrawToLeft;
+    private boolean mUseRefactoredRoundScrollbar;
 
     public RoundScrollbarRenderer(View parent) {
         // Paints for the round scrollbar.
@@ -69,29 +81,36 @@
         // Fetch the resource indicating the thickness of CircularDisplayMask, rounding in the same
         // way WindowManagerService.showCircularMask does. The scroll bar is inset by this amount so
         // that it doesn't get clipped.
-        mMaskThickness = parent.getContext().getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.circular_display_mask_thickness);
+        int maskThickness =
+                parent.getContext()
+                        .getResources()
+                        .getDimensionPixelSize(
+                                com.android.internal.R.dimen.circular_display_mask_thickness);
+
+        float thumbWidth = dpToPx(THUMB_WIDTH_DP);
+        mThumbPaint.setStrokeWidth(thumbWidth);
+        mTrackPaint.setStrokeWidth(thumbWidth);
+        mInset = thumbWidth / 2 + maskThickness;
+
+        mUseRefactoredRoundScrollbar =
+                Flags.useRefactoredRoundScrollbar()
+                        && SystemProperties.getBoolean(BLUECHIP_ENABLED_SYSPROP, false);
     }
 
-    public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds, boolean drawToLeft) {
-        if (alpha == 0) {
-            return;
-        }
-        // Get information about the current scroll state of the parent view.
-        float maxScroll = mParent.computeVerticalScrollRange();
-        float scrollExtent = mParent.computeVerticalScrollExtent();
-        float newScroll = mParent.computeVerticalScrollOffset();
-
+    private float computeScrollExtent(float scrollExtent, float maxScroll) {
         if (scrollExtent <= 0) {
             if (!mParent.canScrollVertically(1) && !mParent.canScrollVertically(-1)) {
-                return;
+                return -1f;
             } else {
-                scrollExtent = 0;
+                return 0f;
             }
         } else if (maxScroll <= scrollExtent) {
-            return;
+            return -1f;
         }
+        return scrollExtent;
+    }
 
+    private void resizeGradually(float maxScroll, float newScroll) {
         // Make changes to the VerticalScrollRange happen gradually
         if (Math.abs(maxScroll - mPreviousMaxScroll) > RESIZING_THRESHOLD_PX
                 && mPreviousMaxScroll != 0) {
@@ -106,49 +125,79 @@
                 || Math.abs(mCurrentScrollDiff) > RESIZING_THRESHOLD_PX) {
             mMaxScrollDiff *= RESIZING_RATE;
             mCurrentScrollDiff *= RESIZING_RATE;
-
-            maxScroll -= mMaxScrollDiff;
-            newScroll -= mCurrentScrollDiff;
         } else {
             mMaxScrollDiff = 0;
             mCurrentScrollDiff = 0;
         }
+    }
 
-        float currentScroll = Math.max(0, newScroll);
-        float linearThumbLength = scrollExtent;
-        float thumbWidth = dpToPx(THUMB_WIDTH_DP);
-        mThumbPaint.setStrokeWidth(thumbWidth);
-        mTrackPaint.setStrokeWidth(thumbWidth);
-
-        setThumbColor(applyAlpha(DEFAULT_THUMB_COLOR, alpha));
-        setTrackColor(applyAlpha(DEFAULT_TRACK_COLOR, alpha));
-
-        // Normalize the sweep angle for the scroll bar.
-        float sweepAngle = (linearThumbLength / maxScroll) * SCROLLBAR_ANGLE_RANGE;
-        sweepAngle = clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE);
-        // Normalize the start angle so that it falls on the track.
-        float startAngle = (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle))
-                / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2f;
-        startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2f,
-                SCROLLBAR_ANGLE_RANGE / 2f - sweepAngle);
-
-        // Draw the track and the thumb.
-        float inset = thumbWidth / 2 + mMaskThickness;
-        mRect.set(
-                bounds.left + inset,
-                bounds.top + inset,
-                bounds.right - inset,
-                bounds.bottom - inset);
-
-        if (drawToLeft) {
-            canvas.drawArc(mRect, 180 + SCROLLBAR_ANGLE_RANGE / 2f, -SCROLLBAR_ANGLE_RANGE, false,
-                    mTrackPaint);
-            canvas.drawArc(mRect, 180 - startAngle, -sweepAngle, false, mThumbPaint);
-        } else {
-            canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2f, SCROLLBAR_ANGLE_RANGE, false,
-                    mTrackPaint);
-            canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint);
+    public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds, boolean drawToLeft) {
+        if (alpha == 0) {
+            return;
         }
+        // Get information about the current scroll state of the parent view.
+        float maxScroll = mParent.computeVerticalScrollRange();
+        float scrollExtent = mParent.computeVerticalScrollExtent();
+        float newScroll = mParent.computeVerticalScrollOffset();
+
+        scrollExtent = computeScrollExtent(scrollExtent, maxScroll);
+        if (scrollExtent < 0f) {
+            return;
+        }
+
+        // Make changes to the VerticalScrollRange happen gradually
+        resizeGradually(maxScroll, newScroll);
+        maxScroll -= mMaxScrollDiff;
+        newScroll -= mCurrentScrollDiff;
+
+        applyThumbColor(alpha);
+
+        float sweepAngle = computeSweepAngle(scrollExtent, maxScroll);
+        float startAngle =
+                computeStartAngle(Math.max(0, newScroll), sweepAngle, maxScroll, scrollExtent);
+
+        updateBounds(bounds);
+
+        mDrawToLeft = drawToLeft;
+        drawRoundScrollbars(canvas, startAngle, sweepAngle, alpha);
+    }
+
+    private void drawRoundScrollbars(
+            Canvas canvas, float startAngle, float sweepAngle, float alpha) {
+        if (mUseRefactoredRoundScrollbar) {
+            draw(canvas, startAngle, sweepAngle, alpha);
+        } else {
+            applyTrackColor(alpha);
+            drawArc(canvas, -SCROLLBAR_ANGLE_RANGE / 2f, SCROLLBAR_ANGLE_RANGE, mTrackPaint);
+            drawArc(canvas, startAngle, sweepAngle, mThumbPaint);
+        }
+    }
+
+    /** Returns true if horizontal bounds are updated */
+    private void updateBounds(Rect bounds) {
+        mRect.set(
+                bounds.left + mInset,
+                bounds.top + mInset,
+                bounds.right - mInset,
+                bounds.bottom - mInset);
+        mThumbStrokeWidthAsDegrees =
+                getVertexAngle((mRect.right - mRect.left) / 2f, mThumbPaint.getStrokeWidth() / 2f);
+    }
+
+    private float computeSweepAngle(float scrollExtent, float maxScroll) {
+        // Normalize the sweep angle for the scroll bar.
+        float sweepAngle = (scrollExtent / maxScroll) * SCROLLBAR_ANGLE_RANGE;
+        return clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE);
+    }
+
+    private float computeStartAngle(
+            float currentScroll, float sweepAngle, float maxScroll, float scrollExtent) {
+        // Normalize the start angle so that it falls on the track.
+        float startAngle =
+                (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle)) / (maxScroll - scrollExtent)
+                        - SCROLLBAR_ANGLE_RANGE / 2f;
+        return clamp(
+                startAngle, -SCROLLBAR_ANGLE_RANGE / 2f, SCROLLBAR_ANGLE_RANGE / 2f - sweepAngle);
     }
 
     void getRoundVerticalScrollBarBounds(Rect bounds) {
@@ -164,10 +213,8 @@
     private static float clamp(float val, float min, float max) {
         if (val < min) {
             return min;
-        } else if (val > max) {
-            return max;
         } else {
-            return val;
+            return Math.min(val, max);
         }
     }
 
@@ -176,15 +223,17 @@
         return Color.argb(alphaByte, Color.red(color), Color.green(color), Color.blue(color));
     }
 
-    private void setThumbColor(int thumbColor) {
-        if (mThumbPaint.getColor() != thumbColor) {
-            mThumbPaint.setColor(thumbColor);
+    private void applyThumbColor(float alpha) {
+        int color = applyAlpha(DEFAULT_THUMB_COLOR, alpha);
+        if (mThumbPaint.getColor() != color) {
+            mThumbPaint.setColor(color);
         }
     }
 
-    private void setTrackColor(int trackColor) {
-        if (mTrackPaint.getColor() != trackColor) {
-            mTrackPaint.setColor(trackColor);
+    private void applyTrackColor(float alpha) {
+        int color = applyAlpha(DEFAULT_TRACK_COLOR, alpha);
+        if (mTrackPaint.getColor() != color) {
+            mTrackPaint.setColor(color);
         }
     }
 
@@ -192,4 +241,88 @@
         return dp * ((float) mParent.getContext().getResources().getDisplayMetrics().densityDpi)
                 / DisplayMetrics.DENSITY_DEFAULT;
     }
+
+    private static float getVertexAngle(float edge, float base) {
+        float edgeSquare = edge * edge * 2;
+        float baseSquare = base * base;
+        float gapInRadians = acos(((edgeSquare - baseSquare) / edgeSquare));
+        return (float) Math.toDegrees(gapInRadians);
+    }
+
+    private static float getKiteEdge(float knownEdge, float angleBetweenKnownEdgesInDegrees) {
+        return (float) (2 * knownEdge * sin(Math.toRadians(angleBetweenKnownEdgesInDegrees / 2)));
+    }
+
+    private void draw(Canvas canvas, float thumbStartAngle, float thumbSweepAngle, float alpha) {
+        // Draws the top arc
+        drawTrack(
+                canvas,
+                // The highest point of the top track on a vertical scale. Here the thumb width is
+                // reduced to account for the arc formed by ROUND stroke style
+                -SCROLLBAR_ANGLE_RANGE / 2f - mThumbStrokeWidthAsDegrees,
+                // The lowest point of the top track on a vertical scale. Here the thumb width is
+                // reduced twice to (a) account for the arc formed by ROUND stroke style (b) gap
+                // between thumb and top track
+                thumbStartAngle - mThumbStrokeWidthAsDegrees * 2,
+                alpha);
+        // Draws the thumb
+        drawArc(canvas, thumbStartAngle, thumbSweepAngle, mThumbPaint);
+        // Draws the bottom arc
+        drawTrack(
+                canvas,
+                // The highest point of the bottom track on a vertical scale. Here the thumb width
+                // is added twice to (a) account for the arc formed by ROUND stroke style (b) gap
+                // between thumb and bottom track
+                (thumbStartAngle + thumbSweepAngle) + mThumbStrokeWidthAsDegrees * 2,
+                // The lowest point of the top track on a vertical scale. Here the thumb width is
+                // added to account for the arc formed by ROUND stroke style
+                SCROLLBAR_ANGLE_RANGE / 2f + mThumbStrokeWidthAsDegrees,
+                alpha);
+    }
+
+    private void drawTrack(Canvas canvas, float beginAngle, float endAngle, float alpha) {
+        // Angular distance between end and begin
+        float angleBetweenEndAndBegin = endAngle - beginAngle;
+        // The sweep angle for the track is the angular distance between end and begin less the
+        // thumb width twice to account for top and bottom arc formed by the ROUND stroke style
+        float sweepAngle = angleBetweenEndAndBegin - 2 * mThumbStrokeWidthAsDegrees;
+
+        float startAngle = -1f;
+        float strokeWidth = -1f;
+        if (sweepAngle > 0f) {
+            // The angle is greater than 0 which means a normal arc should be drawn with stroke
+            // width same as the thumb. The ROUND stroke style will cover the top/bottom arc of the
+            // track
+            startAngle = beginAngle + mThumbStrokeWidthAsDegrees;
+            strokeWidth = mThumbPaint.getStrokeWidth();
+        } else if (Math.abs(sweepAngle) < 2 * mThumbStrokeWidthAsDegrees) {
+            // The sweep angle is less than 0 but is still relevant in creating a circle for the
+            // top/bottom track. The start angle is adjusted to account for being the mid point of
+            // begin / end angle.
+            startAngle = beginAngle + angleBetweenEndAndBegin / 2;
+            // The radius of this circle forms a kite with the radius of the arc drawn for the rect
+            // with the given angular difference between the arc radius which is used to compute the
+            // new stroke width.
+            strokeWidth = getKiteEdge(((mRect.right - mRect.left) / 2), angleBetweenEndAndBegin);
+            // The opacity is decreased proportionally, if the stroke width of the track is 50% or
+            // less that that of the thumb
+            alpha = alpha * Math.min(1f, 2 * strokeWidth / mThumbPaint.getStrokeWidth());
+            // As we desire a circle to be drawn, the sweep angle is set to a minimal value
+            sweepAngle = Float.MIN_NORMAL;
+        } else {
+            return;
+        }
+
+        applyTrackColor(alpha);
+        mTrackPaint.setStrokeWidth(strokeWidth);
+        drawArc(canvas, startAngle, sweepAngle, mTrackPaint);
+    }
+
+    private void drawArc(Canvas canvas, float startAngle, float sweepAngle, Paint paint) {
+        if (mDrawToLeft) {
+            canvas.drawArc(mRect, /* startAngle= */ 180 - startAngle, -sweepAngle, false, paint);
+        } else {
+            canvas.drawArc(mRect, startAngle, sweepAngle, /* useCenter= */ false, paint);
+        }
+    }
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 19e244a..e6de478 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1810,6 +1810,7 @@
         public DisplayMode[] supportedDisplayModes;
         public int activeDisplayModeId;
         public float renderFrameRate;
+        public boolean hasArrSupport;
 
         public int[] supportedColorModes;
         public int activeColorMode;
@@ -1827,6 +1828,7 @@
                     + "supportedDisplayModes=" + Arrays.toString(supportedDisplayModes)
                     + ", activeDisplayModeId=" + activeDisplayModeId
                     + ", renderFrameRate=" + renderFrameRate
+                    + ", hasArrSupport=" + hasArrSupport
                     + ", supportedColorModes=" + Arrays.toString(supportedColorModes)
                     + ", activeColorMode=" + activeColorMode
                     + ", hdrCapabilities=" + hdrCapabilities
@@ -1846,13 +1848,14 @@
                 && Arrays.equals(supportedColorModes, that.supportedColorModes)
                 && activeColorMode == that.activeColorMode
                 && Objects.equals(hdrCapabilities, that.hdrCapabilities)
-                && preferredBootDisplayMode == that.preferredBootDisplayMode;
+                && preferredBootDisplayMode == that.preferredBootDisplayMode
+                && hasArrSupport == that.hasArrSupport;
         }
 
         @Override
         public int hashCode() {
             return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId,
-                    renderFrameRate, activeColorMode, hdrCapabilities);
+                    renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport);
         }
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f7745d1..82235d2 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
@@ -191,7 +192,6 @@
     boolean mDrawFinished = false;
 
     final Rect mScreenRect = new Rect();
-    private final SurfaceSession mSurfaceSession = new SurfaceSession();
     private final boolean mLimitedHdrEnabled = Flags.limitedHdr();
 
     SurfaceControl mSurfaceControl;
@@ -770,6 +770,36 @@
     }
 
     /**
+     * Controls the composition order of the SurfaceView. A non-negative composition order
+     * value indicates that the SurfaceView is composited on top of the parent window, while
+     * a negative composition order indicates that the SurfaceView is behind the parent
+     * window. A SurfaceView with a higher value appears above its peers with lower values.
+     * For SurfaceViews with the same composition order value, their relative order is
+     * undefined.
+     *
+     * @param compositionOrder the composition order of the surface view.
+     */
+    @FlaggedApi(FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER)
+    public void setCompositionOrder(int compositionOrder) {
+        mRequestedSubLayer = compositionOrder;
+        if (mSubLayer != mRequestedSubLayer) {
+            updateSurface();
+        }
+    }
+
+    /**
+     * Returns the composition order of the SurfaceView.
+     *
+     * @return composition order of the SurfaceView.
+     *
+     * @see #setCompositionOrder(int)
+     */
+    @FlaggedApi(FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER)
+    public int getCompositionOrder() {
+        return mRequestedSubLayer;
+    }
+
+    /**
      * Control whether the surface view's surface is placed on top of another
      * regular surface view in the window (but still behind the window itself).
      * This is typically used to place overlays on top of an underlying media
@@ -1257,7 +1287,8 @@
                 final Transaction surfaceUpdateTransaction = new Transaction();
                 if (creating) {
                     updateOpaqueFlag();
-                    final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
+                    final String name = Integer.toHexString(System.identityHashCode(this))
+                            + " SurfaceView[" + viewRoot.getTitle().toString() + "]";
                     createBlastSurfaceControls(viewRoot, name, surfaceUpdateTransaction);
                 } else if (mSurfaceControl == null) {
                     return;
@@ -1517,7 +1548,7 @@
     private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name,
             Transaction surfaceUpdateTransaction) {
         if (mSurfaceControl == null) {
-            mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+            mSurfaceControl = new SurfaceControl.Builder()
                     .setName(name)
                     .setLocalOwnerView(this)
                     .setParent(viewRoot.updateAndGetBoundsLayer(surfaceUpdateTransaction))
@@ -1527,7 +1558,7 @@
         }
 
         if (mBlastSurfaceControl == null) {
-            mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+            mBlastSurfaceControl = new SurfaceControl.Builder()
                     .setName(name + "(BLAST)")
                     .setLocalOwnerView(this)
                     .setParent(mSurfaceControl)
@@ -1545,7 +1576,7 @@
         }
 
         if (mBackgroundControl == null) {
-            mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
+            mBackgroundControl = new SurfaceControl.Builder()
                     .setName("Background for " + name)
                     .setLocalOwnerView(this)
                     .setOpaque(true)
@@ -1702,9 +1733,7 @@
                             mRTLastReportedPosition.top /*positionTop*/,
                             postScaleX, postScaleY);
 
-                    mRTLastSetCrop.set((int) (clipLeft / postScaleX), (int) (clipTop / postScaleY),
-                            (int) Math.ceil(clipRight / postScaleX),
-                            (int) Math.ceil(clipBottom / postScaleY));
+                    mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
                     if (DEBUG_POSITION) {
                         Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] "
                                         + "from scale %f, %f", mRTLastSetCrop.left,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1b62cfd..d9092ee 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -29217,8 +29217,7 @@
                     + " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y);
         }
 
-        final SurfaceSession session = new SurfaceSession();
-        final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
+        final SurfaceControl surfaceControl = new SurfaceControl.Builder()
                 .setName("drag surface")
                 .setParent(root.getSurfaceControl())
                 .setBufferSize(shadowSize.x, shadowSize.y)
@@ -29284,7 +29283,6 @@
             if (token == null) {
                 surface.destroy();
             }
-            session.kill();
             surfaceControl.release();
         }
     }
diff --git a/core/java/android/view/ViewProtoLogGroups.java b/core/java/android/view/ViewProtoLogGroups.java
new file mode 100644
index 0000000..099f761
--- /dev/null
+++ b/core/java/android/view/ViewProtoLogGroups.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.view.inputmethod.Flags;
+
+import com.android.internal.protolog.ProtoLogGroup;
+
+import java.util.UUID;
+
+/**
+ * Defines logging groups for ProtoLog.
+ *
+ * This file is used by the ProtoLogTool to generate optimized logging code. All of its dependencies
+ * must be included in services.core.wm.protologgroups build target.
+ *
+ * @hide
+ */
+final class ViewProtoLogGroups {
+    final static ProtoLogGroup IME_INSETS_CONTROLLER = new ProtoLogGroup(
+        "IME_INSETS_CONTROLLER", "InsetsController", Flags.refactorInsetsController());
+
+    final static ProtoLogGroup[] ALL_GROUPS = {
+        IME_INSETS_CONTROLLER
+    };
+}
+
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0ca442d..0efded2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -118,6 +118,7 @@
 import static android.view.flags.Flags.sensitiveContentAppProtection;
 import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix;
 import static android.view.flags.Flags.toolkitFrameRateFunctionEnablingReadOnly;
+import static android.view.flags.Flags.toolkitFrameRateTouchBoost25q1;
 import static android.view.flags.Flags.toolkitFrameRateTypingReadOnly;
 import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly;
 import static android.view.flags.Flags.toolkitFrameRateViewEnablingReadOnly;
@@ -125,7 +126,7 @@
 import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
-import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
+import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
@@ -279,11 +280,13 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneFallbackEventHandler;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.SurfaceCallbackHelper;
 import com.android.modules.expresslog.Counter;
+import com.android.os.coregraphics.HwuiStatsLog;
 
 import libcore.io.IoUtils;
 
@@ -838,7 +841,6 @@
      * surfaces can ensure they do not draw into the surface inset region set by the parent window.
      */
     private SurfaceControl mBoundsLayer;
-    private final SurfaceSession mSurfaceSession = new SurfaceSession();
     private final Transaction mTransaction = new Transaction();
     private final Transaction mFrameRateTransaction = new Transaction();
 
@@ -1228,6 +1230,8 @@
 
     // The latest input event from the gesture that was used to resolve the pointer icon.
     private MotionEvent mPointerIconEvent = null;
+    private @ActivityInfo.ColorMode int mCurrentColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+    private long mColorModeLastSetMillis = -1;
 
     public ViewRootImpl(Context context, Display display) {
         this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
@@ -1279,6 +1283,8 @@
         mIsStylusPointerIconEnabled =
                 InputSettings.isStylusPointerIconEnabled(mContext);
 
+        initializeProtoLogInProcess();
+
         String processorOverrideName = context.getResources().getString(
                                     R.string.config_inputEventCompatProcessorOverrideClassName);
         if (processorOverrideName.isEmpty()) {
@@ -2646,6 +2652,7 @@
                 mFirstFramePresentedTimeNs = -1;
             }
         }
+        logColorMode(mCurrentColorMode, true);
     }
 
 
@@ -2704,7 +2711,7 @@
      */
     public SurfaceControl updateAndGetBoundsLayer(Transaction t) {
         if (mBoundsLayer == null) {
-            mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession)
+            mBoundsLayer = new SurfaceControl.Builder()
                     .setContainerLayer()
                     .setName("Bounds for - " + getTitle().toString())
                     .setParent(getSurfaceControl())
@@ -5945,7 +5952,34 @@
             // If no intersection, set bounds to empty.
             bounds.setEmpty();
         }
-        return !bounds.isEmpty();
+
+        if (bounds.isEmpty()) {
+            return false;
+        }
+
+        if (android.view.accessibility.Flags.focusRectMinSize()) {
+            adjustAccessibilityFocusedRectBoundsIfNeeded(bounds);
+        }
+
+        return true;
+    }
+
+    /**
+     * Adjusts accessibility focused rect bounds so that they are not invisible.
+     *
+     * <p>Focus bounds smaller than double the stroke width are very hard to see (or invisible).
+     * Expand the focus bounds if necessary to at least double the stroke width.
+     * @param bounds The bounds to adjust
+     */
+    @VisibleForTesting
+    public void adjustAccessibilityFocusedRectBoundsIfNeeded(Rect bounds) {
+        final int minRectLength = mAccessibilityManager.getAccessibilityFocusStrokeWidth() * 2;
+        if (bounds.width() < minRectLength || bounds.height() < minRectLength) {
+            final float missingWidth = Math.max(0, minRectLength - bounds.width());
+            final float missingHeight = Math.max(0, minRectLength - bounds.height());
+            bounds.inset(-1 * (int) Math.ceil(missingWidth / 2),
+                    -1 * (int) Math.ceil(missingHeight / 2));
+        }
     }
 
     private Drawable getAccessibilityFocusedDrawable() {
@@ -6341,6 +6375,7 @@
         if (mAttachInfo.mThreadedRenderer == null) {
             return;
         }
+
         boolean isHdr = colorMode == ActivityInfo.COLOR_MODE_HDR
                 || colorMode == ActivityInfo.COLOR_MODE_HDR10;
         if (isHdr && !mDisplay.isHdrSdrRatioAvailable()) {
@@ -6353,6 +6388,9 @@
                 && !getConfiguration().isScreenWideColorGamut()) {
             colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
         }
+
+        logColorMode(colorMode, false);
+
         float automaticRatio = mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
         if (desiredRatio == 0 || desiredRatio > automaticRatio) {
             desiredRatio = automaticRatio;
@@ -6963,9 +7001,7 @@
                     handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
                     break;
                 case MSG_PAUSED_FOR_SYNC_TIMEOUT:
-                    Log.e(mTag, "Timedout waiting to unpause for sync");
-                    mNumPausedForSync = 0;
-                    scheduleTraversals();
+                    resumeAfterSyncTimeout();
                     break;
                 case MSG_CHECK_INVALIDATION_IDLE: {
                     long delta;
@@ -10005,6 +10041,7 @@
 
             mAttachInfo.mThreadedRenderer = null;
             mAttachInfo.mHardwareAccelerated = false;
+            logColorMode(mCurrentColorMode, true);
         }
     }
 
@@ -12768,6 +12805,15 @@
         activeSurfaceSyncGroup.addTransaction(t);
     }
 
+    /**
+     * Resume rendering after being paused for sync due to a timeout.
+     */
+    private void resumeAfterSyncTimeout() {
+        Log.e(mTag, "Timedout waiting to unpause for sync mNumPausedForSync=" + mNumPausedForSync);
+        mNumPausedForSync = 0;
+        scheduleTraversals();
+    }
+
     @Override
     public SurfaceSyncGroup getOrCreateSurfaceSyncGroup() {
         boolean newSyncGroup = false;
@@ -12795,6 +12841,16 @@
                 }
             });
             newSyncGroup = true;
+
+            // If the sync group is marked ready by a timeout, check if rendering is paused and
+            // if it is, resume rendering and trigger a traversal.
+            mActiveSurfaceSyncGroup.addSyncCompleteCallback(mExecutor, () -> {
+                if (mActiveSurfaceSyncGroup != null
+                        && mActiveSurfaceSyncGroup.isComplete() && mNumPausedForSync > 0) {
+                    mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+                    resumeAfterSyncTimeout();
+                }
+            });
         }
 
         Trace.instant(Trace.TRACE_TAG_VIEW,
@@ -12809,12 +12865,20 @@
             }
         }
 
-        mNumPausedForSync++;
-        mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
-        mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT,
-                1000 * Build.HW_TIMEOUT_MULTIPLIER);
+        // The sync group can be marked ready by a timeout. This makes incrementing
+        // mNumPausedForSync racy. Here we check if the sync group is complete and
+        // if it is then we don't pause for syncing.
+        if (!mActiveSurfaceSyncGroup.isComplete()) {
+            mNumPausedForSync++;
+            mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT);
+            mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT,
+                    1000 * Build.HW_TIMEOUT_MULTIPLIER);
+        } else {
+            Log.d(mTag, "Active sync group is already completed "
+                    + mActiveSurfaceSyncGroup.getName());
+        }
         return mActiveSurfaceSyncGroup;
-    };
+    }
 
     private final Executor mSimpleExecutor = Runnable::run;
 
@@ -13045,6 +13109,11 @@
         boolean desiredAction = motionEventAction != MotionEvent.ACTION_OUTSIDE;
         boolean undesiredType = windowType == TYPE_INPUT_METHOD
                 && sToolkitFrameRateTypingReadOnlyFlagValue;
+
+        // don't suppress touch boost for TYPE_INPUT_METHOD in ViewRootImpl
+        if (toolkitFrameRateTouchBoost25q1()) {
+            return desiredAction && shouldEnableDvrr() && getFrameRateBoostOnTouchEnabled();
+        }
         // use toolkitSetFrameRate flag to gate the change
         return desiredAction && !undesiredType && shouldEnableDvrr()
                 && getFrameRateBoostOnTouchEnabled();
@@ -13346,4 +13415,36 @@
             mInfrequentUpdateCount = 0;
         }
     }
+
+    private void logColorMode(@ActivityInfo.ColorMode int colorMode, boolean windowStopped) {
+        if (mColorModeLastSetMillis == -1 && windowStopped) {
+            Log.d(TAG, "Skipping stats log for color mode");
+            return;
+        }
+
+        long currentTimeMillis = System.currentTimeMillis();
+
+        if (windowStopped) {
+            HwuiStatsLog.write(HwuiStatsLog.HARDWARE_RENDERER_EVENT, Process.myUid(),
+                    currentTimeMillis - mColorModeLastSetMillis, mCurrentColorMode);
+            mColorModeLastSetMillis = -1;
+        } else {
+            if (mColorModeLastSetMillis > 0) {
+                HwuiStatsLog.write(HwuiStatsLog.HARDWARE_RENDERER_EVENT, Process.myUid(),
+                        currentTimeMillis - mColorModeLastSetMillis, mCurrentColorMode);
+            }
+            mColorModeLastSetMillis = currentTimeMillis;
+        }
+
+        mCurrentColorMode = colorMode;
+    }
+
+    private static boolean sProtoLogInitialized = false;
+
+    private void initializeProtoLogInProcess() {
+        if (!sProtoLogInitialized) {
+            ProtoLog.init(ViewProtoLogGroups.ALL_GROUPS);
+            sProtoLogInitialized = true;
+        }
+    }
 }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 5129461..65e9930 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -86,7 +86,6 @@
     final HashMap<IBinder, ResizeCompleteCallback> mResizeCompletionForWindow =
         new HashMap<IBinder, ResizeCompleteCallback>();
 
-    private final SurfaceSession mSurfaceSession = new SurfaceSession();
     protected final SurfaceControl mRootSurface;
     private final Configuration mConfiguration;
     private final IWindowSession mRealWm;
@@ -184,13 +183,13 @@
             InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
             float[] outSizeCompatScale) {
-        final SurfaceControl leash = new SurfaceControl.Builder(mSurfaceSession)
+        final SurfaceControl leash = new SurfaceControl.Builder()
                 .setName(attrs.getTitle().toString() + "Leash")
                 .setCallsite("WindowlessWindowManager.addToDisplay")
                 .setParent(getParentSurface(window, attrs))
                 .build();
 
-        final SurfaceControl sc = new SurfaceControl.Builder(mSurfaceSession)
+        final SurfaceControl sc = new SurfaceControl.Builder()
                 .setFormat(attrs.format)
                 .setBLASTLayer()
                 .setName(attrs.getTitle().toString())
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index a43906f..dfac244 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -784,6 +785,19 @@
      */
     public static final int CONTENT_CHANGE_TYPE_ENABLED = 1 << 12;
 
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * The source node changed its checked state, which is returned by
+     * {@link AccessibilityNodeInfo#getChecked()}.
+     * The view changing its checked state should call
+     * {@link AccessibilityNodeInfo#setChecked(int)} and then send this event.
+     *
+     * @see AccessibilityNodeInfo#getChecked()
+     * @see AccessibilityNodeInfo#setChecked(int)
+     */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    public static final int CONTENT_CHANGE_TYPE_CHECKED = 1 << 13;
+
     // Speech state change types.
 
     /** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index fe6aafb..60ccb77 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -860,6 +860,37 @@
     public static final String EXTRA_DATA_REQUESTED_KEY =
             "android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
 
+    // Tri-state checked states.
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CHECKED_STATE" }, value = {
+            CHECKED_STATE_FALSE,
+            CHECKED_STATE_TRUE,
+            CHECKED_STATE_PARTIAL
+    })
+    public @interface CheckedState {}
+
+    /**
+     * This node is not checked.
+     */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    public static final int CHECKED_STATE_FALSE = 0;
+
+    /**
+     * This node is checked.
+     */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    public static final int CHECKED_STATE_TRUE = 1;
+
+    /**
+     * This node is partially checked. For example,
+     * when a checkbox owns a number of sub-options and they have
+     * different states, then the main checkbox is in a partially-checked state.
+     */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    public static final int CHECKED_STATE_PARTIAL = 2;
+
     // Boolean attributes.
 
     private static final int BOOLEAN_PROPERTY_CHECKABLE = 1 /* << 0 */;
@@ -1038,6 +1069,10 @@
     private IBinder mLeashedParent;
     private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
 
+    // TODO(b/369951517) Initialize mChecked explicitly with
+    // the CHECKED_FALSE state when flagging is removed.
+    private int mChecked;
+
     /**
      * Creates a new {@link AccessibilityNodeInfo}.
      */
@@ -2253,10 +2288,7 @@
     /**
      * Gets the node bounds in window coordinates.
      * <p>
-     * When magnification is enabled, the bounds in window are scaled up by magnification scale
-     * and the positions are also adjusted according to the offset of magnification viewport.
-     * For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100),
-     * when the magnification scale is 2 and offsets for X and Y are both 200.
+     *   The node bounds returned are not scaled by magnification.
      * <p/>
      *
      * @param outBounds The output node bounds.
@@ -2319,28 +2351,100 @@
     }
 
     /**
-     * Gets whether this node is checked.
+     * Gets whether this node is checked. This is only meaningful
+     * when {@link #isCheckable()} returns {@code true}.
+     *
+     * @deprecated Use {@link #getChecked()} instead.
      *
      * @return True if the node is checked.
      */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    @Deprecated
     public boolean isChecked() {
         return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED);
     }
 
     /**
-     * Sets whether this node is checked.
+     * Sets whether this node is checked. This is only meaningful
+     * when {@link #isCheckable()} returns {@code true}.
      * <p>
      *   <strong>Note:</strong> Cannot be called from an
      *   {@link android.accessibilityservice.AccessibilityService}.
      *   This class is made immutable before being delivered to an AccessibilityService.
      * </p>
      *
+     * @deprecated Use {@link #setChecked(int)} instead.
+     *
      * @param checked True if the node is checked.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
      */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    @Deprecated
     public void setChecked(boolean checked) {
         setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked);
+        if (Flags.triStateChecked()) {
+            mChecked = checked ? CHECKED_STATE_TRUE : CHECKED_STATE_FALSE;
+        }
+    }
+
+    /**
+     * Gets the checked state of this node. This is only meaningful
+     * when {@link #isCheckable()} returns {@code true}.
+     *
+     * @see #setCheckable(boolean)
+     * @see #isCheckable()
+     * @see #setChecked(int)
+     *
+     * @return The checked state, one of:
+     *          <ul>
+     *          <li>{@link #CHECKED_STATE_FALSE}
+     *          <li>{@link #CHECKED_STATE_TRUE}
+     *          <li>{@link #CHECKED_STATE_PARTIAL}
+     *          </ul>
+     */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    public @CheckedState int getChecked() {
+        return mChecked;
+    }
+
+    /**
+     * Sets the checked state of this node. This is only meaningful
+     * when {@link #isCheckable()} returns {@code true}.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @see #setCheckable(boolean)
+     * @see #isCheckable()
+     * @see #getChecked()
+     *
+     * @param checked The checked state. One of
+     *          <ul>
+     *          <li>{@link #CHECKED_STATE_FALSE}
+     *          <li>{@link #CHECKED_STATE_TRUE}
+     *          <li>{@link #CHECKED_STATE_PARTIAL}
+     *          </ul>
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     * @throws IllegalArgumentException if checked is not one of {@link #CHECKED_STATE_FALSE},
+     *          {@link #CHECKED_STATE_TRUE}, or {@link #CHECKED_STATE_PARTIAL}.
+     */
+    @FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
+    public void setChecked(@CheckedState int checked) {
+        enforceNotSealed();
+        switch (checked) {
+            case CHECKED_STATE_FALSE:
+            case CHECKED_STATE_TRUE:
+            case CHECKED_STATE_PARTIAL:
+                mChecked = checked;
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown checked argument: " + checked);
+        }
+        setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked == CHECKED_STATE_TRUE);
     }
 
     /**
@@ -4515,6 +4619,10 @@
         if (mLeashedParentNodeId != DEFAULT.mLeashedParentNodeId) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
+        fieldIndex++;
+        if (mChecked != DEFAULT.mChecked) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
         int totalFields = fieldIndex;
         parcel.writeLong(nonDefaultFields);
 
@@ -4683,6 +4791,9 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             parcel.writeLong(mLeashedParentNodeId);
         }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeInt(mChecked);
+        }
 
         if (DEBUG) {
             fieldIndex--;
@@ -4771,6 +4882,7 @@
         mLeashedChild = other.mLeashedChild;
         mLeashedParent = other.mLeashedParent;
         mLeashedParentNodeId = other.mLeashedParentNodeId;
+        mChecked = other.mChecked;
     }
 
     private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -4960,6 +5072,9 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             mLeashedParentNodeId = parcel.readLong();
         }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mChecked = parcel.readInt();
+        }
 
         mSealed = sealed;
     }
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index b9e9750..820a1fb 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -11,6 +11,13 @@
 }
 
 flag {
+    name: "a11y_is_required_api"
+    namespace: "accessibility"
+    description: "Adds an API to indicate whether a form field (or similar element) is required."
+    bug: "362784403"
+}
+
+flag {
     name: "a11y_overlay_callbacks"
     is_exported: true
     namespace: "accessibility"
@@ -89,6 +96,16 @@
 
 flag {
     namespace: "accessibility"
+    name: "focus_rect_min_size"
+    description: "Ensures the a11y focus rect is big enough to be drawn as visible"
+    bug: "368667566"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    namespace: "accessibility"
     name: "force_invert_color"
     description: "Enable force force-dark for smart inversion and dark theme everywhere"
     bug: "282821643"
@@ -210,3 +227,20 @@
     description: "Feature flag for declaring system pinch zoom opt-out apis"
     bug: "315089687"
 }
+
+flag {
+    name: "tri_state_checked"
+    namespace: "accessibility"
+    description: "Feature flag for adding tri-state checked api"
+    bug: "333784774"
+}
+
+flag {
+    name: "warning_use_default_dialog_type"
+    namespace: "accessibility"
+    description: "Uses the default type for the A11yService warning dialog, instead of SYSTEM_ALERT_DIALOG"
+    bug: "336719951"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+ }
diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index 7b04927..c31df73 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -119,4 +119,12 @@
     description: "Feature flag to enable the fix for applyLegacyAnimation for VRR V QPR2"
     bug: "335874198"
     is_fixed_read_only: true
+}
+
+flag {
+    name: "toolkit_frame_rate_touch_boost_25q1"
+    namespace: "toolkit"
+    description: "Feature flag to not suppress touch boost for specific windowTypes in VRR V QPR2"
+    bug: "335874198"
+    is_exported: true
 }
\ No newline at end of file
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index f570a9a..bb61ae4 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -97,4 +97,31 @@
     description: "Disable Draw Wakelock starting U."
     bug: "331698645"
     is_fixed_read_only: true
+}
+
+flag {
+  name: "calculate_bounds_in_parent_from_bounds_in_screen"
+  namespace: "accessibility"
+  description: "Calculate bounds in parent of each node in ViewStructure from its bounds set relative to screen and its parent's"
+  bug: "366131857"
+  is_fixed_read_only: true
+  metadata {
+      purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+    name: "surface_view_set_composition_order"
+    namespace: "window_surfaces"
+    description: "Add a SurfaceView composition order control API."
+    bug: "341021569"
+    is_fixed_read_only: true
+}
+
+flag {
+    name: "use_refactored_round_scrollbar"
+    namespace: "wear_frameworks"
+    description: "Use refactored round scrollbar."
+    bug: "333417898"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1e5c6d8..47fc437 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2352,6 +2352,13 @@
      * {@link #RESULT_HIDDEN}.
      * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
      * this does not return result of the request. For result use {@param resultReceiver} instead.
+     *
+     * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
+     * Input Method is actually shown or hidden. If result is needed, use
+     * {@link android.view.WindowInsetsController#show} instead and set a
+     * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
+     * the visibility of IME. If result is not needed, use {@link #showSoftInput(View, int)}
+     * instead.
      */
     public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
         return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2399,6 +2406,14 @@
                                 & WindowInsets.Type.ime()) == 0) {
                     ImeTracker.forLogging().onProgress(statsToken,
                             ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
+                    if (resultReceiver != null) {
+                        final boolean imeReqVisible =
+                                (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
+                                        & WindowInsets.Type.ime()) != 0;
+                        resultReceiver.send(
+                                imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+                                        : InputMethodManager.RESULT_SHOWN, null);
+                    }
                     // TODO(b/322992891) handle case of SHOW_IMPLICIT
                     viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
                             false /* fromIme */, statsToken);
@@ -2531,6 +2546,13 @@
      * {@link #RESULT_HIDDEN}.
      * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
      * this does not return result of the request. For result use {@param resultReceiver} instead.
+     *
+     * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
+     * Input Method is actually shown or hidden. If result is needed, use
+     * {@link android.view.WindowInsetsController#hide} instead and set a
+     * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
+     * the visibility of IME. If result is not needed, use
+     * {@link #hideSoftInputFromView(View, int)} instead.
      */
     public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
             ResultReceiver resultReceiver) {
@@ -2569,6 +2591,14 @@
                 // TODO(b/322992891) handle case of HIDE_IMPLICIT_ONLY
                 final var viewRootImpl = servedView.getViewRootImpl();
                 if (viewRootImpl != null) {
+                    if (resultReceiver != null) {
+                        final boolean imeReqVisible =
+                                (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
+                                        & WindowInsets.Type.ime()) != 0;
+                        resultReceiver.send(
+                                !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN
+                                        : InputMethodManager.RESULT_HIDDEN, null);
+                    }
                     viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime());
                 }
                 return true;
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index bae8aff..aa4927e 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -73,6 +73,17 @@
 }
 
 flag {
+    name: "consistent_get_current_input_method_info"
+    namespace: "input_method"
+    description: "Use BindingController as the source of truth in getCurrentInputMethodInfo"
+    bug: "355034523"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "ime_switcher_revamp"
     is_exported: true
     namespace: "input_method"
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index b7ee0b8..877fa74 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -16,6 +16,8 @@
 
 package android.webkit;
 
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Intent;
@@ -25,6 +27,9 @@
 import android.os.Message;
 import android.view.View;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class WebChromeClient {
 
     /**
@@ -547,18 +552,41 @@
      * Parameters used in the {@link #onShowFileChooser} method.
      */
     public static abstract class FileChooserParams {
+        /** @hide */
+        @IntDef(prefix = { "MODE_" }, value = {
+            MODE_OPEN,
+            MODE_OPEN_MULTIPLE,
+            MODE_OPEN_FOLDER,
+            MODE_SAVE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Mode {}
+
         /** Open single file. Requires that the file exists before allowing the user to pick it. */
         public static final int MODE_OPEN = 0;
         /** Like Open but allows multiple files to be selected. */
         public static final int MODE_OPEN_MULTIPLE = 1;
-        /** Like Open but allows a folder to be selected. The implementation should enumerate
-            all files selected by this operation.
-            This feature is not supported at the moment.
-            @hide */
+        /** Like Open but allows a folder to be selected. */
+        @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS)
         public static final int MODE_OPEN_FOLDER = 2;
         /**  Allows picking a nonexistent file and saving it. */
         public static final int MODE_SAVE = 3;
 
+        /** @hide */
+        @IntDef(prefix = { "PERMISSION_MODE_" }, value = {
+            PERMISSION_MODE_READ,
+            PERMISSION_MODE_READ_WRITE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface PermissionMode {}
+
+        /** File or directory should be opened for reading only. */
+        @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS)
+        public static final int PERMISSION_MODE_READ = 0;
+        /** File or directory should be opened for read and write. */
+        @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS)
+        public static final int PERMISSION_MODE_READ_WRITE = 1;
+
         /**
          * Parse the result returned by the file picker activity. This method should be used with
          * {@link #createIntent}. Refer to {@link #createIntent} for how to use it.
@@ -585,6 +613,7 @@
         /**
          * Returns file chooser mode.
          */
+        @Mode
         public abstract int getMode();
 
         /**
@@ -616,6 +645,21 @@
         public abstract String getFilenameHint();
 
         /**
+         * Returns permission mode {@link #PERMISSION_MODE_READ} or
+         * {@link #PERMISSION_MODE_READ_WRITE} which indicates the intended mode for opening a file
+         * or directory.
+         *
+         * This can be used to determine whether an Intent such as
+         * {@link android.content.Intent#ACTION_OPEN_DOCUMENT} should be used rather than
+         * {@link android.content.Intent#ACTION_GET_CONTENT} to choose files.
+         */
+        @FlaggedApi(Flags.FLAG_FILE_SYSTEM_ACCESS)
+        @PermissionMode
+        public int getPermissionMode() {
+            return PERMISSION_MODE_READ;
+        }
+
+        /**
          * Creates an intent that would start a file picker for file selection.
          * The Intent supports choosing files from simple file sources available
          * on the device. Some advanced sources (for example, live media capture)
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 7366b9a..ab5969b 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -16,10 +16,12 @@
 
 package android.webkit;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -151,6 +153,24 @@
     public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L;
 
     /**
+     * Enable User-Agent Reduction for webview.
+     * The OS, CPU, and Build information in the default User-Agent will be
+     * reduced to the static "Linux; Android 10; K" string.
+     * Minor/build/patch version information in the default User-Agent is
+     * reduced to "0.0.0". The rest of the default User-Agent remains unchanged.
+     *
+     * See https://developers.google.com/privacy-sandbox/protections/user-agent
+     * for details related to User-Agent Reduction.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @FlaggedApi(android.webkit.Flags.FLAG_USER_AGENT_REDUCTION)
+    @SystemApi
+    public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L;
+
+    /**
      * Default cache usage mode. If the navigation type doesn't impose any
      * specific behavior, use cached resources when they are available
      * and not expired, otherwise load resources from the network.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ffe8c80..b666399 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -17,6 +17,7 @@
 package android.webkit;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1495,7 +1496,13 @@
      * @param context Application Context.
      * @param callback will be called on the UI thread with {@code true} if initialization is
      * successful, {@code false} otherwise.
+     * @deprecated In WebView version 122.0.6174.0 and later, this initialization is done
+     * automatically, so there is no need to call this API. If called, this API will invoke
+     * the {@code callback} immediately with {@code true}, given that Safe Browsing
+     * is enabled and supported on the device.
      */
+    @Deprecated
+    @FlaggedApi(android.webkit.Flags.FLAG_DEPRECATE_START_SAFE_BROWSING)
     public static void startSafeBrowsing(@NonNull Context context,
             @Nullable ValueCallback<Boolean> callback) {
         getFactory().getStatics().initSafeBrowsing(context, callback);
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index b21a490..c9e94d2 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -11,6 +11,14 @@
 }
 
 flag {
+    name: "deprecate_start_safe_browsing"
+    is_exported: true
+    namespace: "webview"
+    description: "Deprecating startSafeBrowsing API because it is a NOOP"
+    bug: "372193372"
+}
+
+flag {
     name: "mainline_apis"
     is_exported: true
     namespace: "webview"
@@ -18,3 +26,19 @@
     bug: "310653407"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "user_agent_reduction"
+    is_exported: true
+    namespace: "webview"
+    description: "New feature reduce user-agent for webview"
+    bug: "371034303"
+}
+
+flag {
+    name: "file_system_access"
+    is_exported: true
+    namespace: "webview"
+    description: "New APIs required by File System Access"
+    bug: "40101963"
+}
diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java
index 9b396ae..fc09f88 100644
--- a/core/java/android/widget/RemoteCollectionItemsAdapter.java
+++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java
@@ -40,13 +40,15 @@
     private RemoteCollectionItems mItems;
     private InteractionHandler mInteractionHandler;
     private ColorResources mColorResources;
+    private boolean mOnLightBackground;
 
     private SparseIntArray mLayoutIdToViewType;
 
     RemoteCollectionItemsAdapter(
             @NonNull RemoteCollectionItems items,
             @NonNull InteractionHandler interactionHandler,
-            @NonNull ColorResources colorResources) {
+            @NonNull ColorResources colorResources,
+            boolean onLightBackground) {
         // View type count can never increase after an adapter has been set on a ListView.
         // Additionally, decreasing it could inhibit view recycling if the count were to back and
         // forth between 3-2-3-2 for example. Therefore, the view type count, should be fixed for
@@ -56,6 +58,7 @@
         mItems = items;
         mInteractionHandler = interactionHandler;
         mColorResources = colorResources;
+        mOnLightBackground = onLightBackground;
 
         initLayoutIdToViewType();
     }
@@ -68,7 +71,8 @@
     void setData(
             @NonNull RemoteCollectionItems items,
             @NonNull InteractionHandler interactionHandler,
-            @NonNull ColorResources colorResources) {
+            @NonNull ColorResources colorResources,
+            boolean onLightBackground) {
         if (mViewTypeCount < items.getViewTypeCount()) {
             throw new IllegalArgumentException(
                     "RemoteCollectionItemsAdapter cannot increase view type count after creation");
@@ -77,6 +81,7 @@
         mItems = items;
         mInteractionHandler = interactionHandler;
         mColorResources = colorResources;
+        mOnLightBackground = onLightBackground;
 
         initLayoutIdToViewType();
 
@@ -184,6 +189,7 @@
                 : new AppWidgetHostView.AdapterChildHostView(parent.getContext());
         newView.setInteractionHandler(mInteractionHandler);
         newView.setColorResourcesNoReapply(mColorResources);
+        newView.setOnLightBackground(mOnLightBackground);
         newView.updateAppWidget(item);
         return newView;
     }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 06820cd..9b6311f 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -46,6 +46,7 @@
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.appwidget.AppWidgetHostView;
+import android.appwidget.flags.Flags;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -1247,6 +1248,7 @@
 
             AdapterView adapterView = (AdapterView) target;
             Adapter adapter = adapterView.getAdapter();
+            boolean onLightBackground = hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
             // We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type
             // count hasn't increased. Note that AbsListView allocates a fixed size array for view
             // recycling in setAdapter, so we must call setAdapter again if the number of view types
@@ -1254,8 +1256,12 @@
             if (adapter instanceof RemoteCollectionItemsAdapter
                     && adapter.getViewTypeCount() >= items.getViewTypeCount()) {
                 try {
-                    ((RemoteCollectionItemsAdapter) adapter).setData(
-                            items, params.handler, params.colorResources);
+                    ((RemoteCollectionItemsAdapter) adapter)
+                            .setData(
+                                    items,
+                                    params.handler,
+                                    params.colorResources,
+                                    onLightBackground);
                 } catch (Throwable throwable) {
                     // setData should never failed with the validation in the items builder, but if
                     // it does, catch and rethrow.
@@ -1265,8 +1271,9 @@
             }
 
             try {
-                adapterView.setAdapter(new RemoteCollectionItemsAdapter(items,
-                        params.handler, params.colorResources));
+                adapterView.setAdapter(
+                        new RemoteCollectionItemsAdapter(
+                                items, params.handler, params.colorResources, onLightBackground));
             } catch (Throwable throwable) {
                 // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
                 // a type error.
@@ -4119,6 +4126,71 @@
         public void visitUris(@NonNull Consumer<Uri> visitor) {
             mNestedViews.visitUris(visitor);
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            if (!Flags.remoteViewsProto()) return;
+            final long token = out.start(RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION);
+            out.write(RemoteViewsProto.ViewGroupAddAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.ViewGroupAddAction.INDEX, mIndex);
+            out.write(RemoteViewsProto.ViewGroupAddAction.STABLE_ID, mStableId);
+            long rvToken = out.start(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS);
+            mNestedViews.writePreviewToProto(context, out);
+            out.end(rvToken);
+            out.end(token);
+        }
+    }
+
+    private PendingResources<Action> createViewGroupActionAddFromProto(ProtoInputStream in)
+            throws Exception {
+        final LongSparseArray<Object> values = new LongSparseArray<>();
+
+        final long token = in.start(RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION);
+        while (in.nextField() != NO_MORE_FIELDS) {
+            switch (in.getFieldNumber()) {
+                case (int) RemoteViewsProto.ViewGroupAddAction.VIEW_ID:
+                    values.put(RemoteViewsProto.ViewGroupAddAction.VIEW_ID,
+                            in.readString(RemoteViewsProto.ViewGroupAddAction.VIEW_ID));
+                    break;
+                case (int) RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS:
+                    final long nvToken = in.start(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS);
+                    values.put(RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS,
+                            createFromProto(in));
+                    in.end(nvToken);
+                    break;
+                case (int) RemoteViewsProto.ViewGroupAddAction.INDEX:
+                    values.put(RemoteViewsProto.ViewGroupAddAction.INDEX,
+                            in.readInt(RemoteViewsProto.ViewGroupAddAction.INDEX));
+                    break;
+                case (int) RemoteViewsProto.ViewGroupAddAction.STABLE_ID:
+                    values.put(RemoteViewsProto.ViewGroupAddAction.STABLE_ID,
+                            in.readInt(RemoteViewsProto.ViewGroupAddAction.STABLE_ID));
+                    break;
+                default:
+                    Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                            + ProtoUtils.currentFieldToString(in));
+            }
+        }
+        in.end(token);
+
+        checkContainsKeys(values, new long[]{RemoteViewsProto.ViewGroupAddAction.VIEW_ID,
+                RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS});
+
+        return (context, resources, rootData, depth) -> {
+            int viewId = getAsIdentifier(resources, values,
+                    RemoteViewsProto.ViewGroupAddAction.VIEW_ID);
+            return new ViewGroupActionAdd(viewId, ((PendingResources<RemoteViews>) values.get(
+                    RemoteViewsProto.ViewGroupAddAction.NESTED_VIEWS)).create(context, resources,
+                    rootData, depth),
+                    (int) values.get(RemoteViewsProto.ViewGroupAddAction.INDEX, 0),
+                    (int) values.get(RemoteViewsProto.ViewGroupAddAction.STABLE_ID, 0));
+        };
     }
 
     /**
@@ -4241,6 +4313,60 @@
         public int mergeBehavior() {
             return MERGE_APPEND;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION);
+            out.write(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            if (mViewIdToKeep != REMOVE_ALL_VIEWS_ID) {
+                out.write(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP,
+                        appResources.getResourceName(mViewIdToKeep));
+            }
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID:
+                        values.put(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP:
+                        values.put(RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP,
+                                in.readString(
+                                        RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID);
+                int viewIdToKeep = (values.indexOfKey(
+                        RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP) >= 0)
+                        ? getAsIdentifier(resources, values,
+                        RemoteViewsProto.ViewGroupRemoveAction.VIEW_ID_TO_KEEP)
+                        : REMOVE_ALL_VIEWS_ID;
+                return new ViewGroupActionRemove(viewId, viewIdToKeep);
+            };
+        }
     }
 
     /**
@@ -4497,6 +4623,200 @@
                 visitIconUri(mI4, visitor);
             }
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context,
+                Resources appResources) { // rebase
+            final long token = out.start(RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION);
+            out.write(RemoteViewsProto.TextViewDrawableAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE, mIsRelative);
+            if (mUseIcons) {
+                long iconsToken = out.start(RemoteViewsProto.TextViewDrawableAction.ICONS);
+                if (mI1 != null) {
+                    writeIconToProto(out, appResources, mI1,
+                            RemoteViewsProto.TextViewDrawableAction.Icons.ONE);
+                }
+                if (mI2 != null) {
+                    writeIconToProto(out, appResources, mI2,
+                            RemoteViewsProto.TextViewDrawableAction.Icons.TWO);
+                }
+                if (mI3 != null) {
+                    writeIconToProto(out, appResources, mI3,
+                            RemoteViewsProto.TextViewDrawableAction.Icons.THREE);
+                }
+                if (mI4 != null) {
+                    writeIconToProto(out, appResources, mI4,
+                            RemoteViewsProto.TextViewDrawableAction.Icons.FOUR);
+                }
+                out.end(iconsToken);
+            } else {
+                long resourcesToken = out.start(RemoteViewsProto.TextViewDrawableAction.RESOURCES);
+                if (mD1 != 0) {
+                    out.write(RemoteViewsProto.TextViewDrawableAction.Resources.ONE,
+                            appResources.getResourceName(mD1));
+                }
+                if (mD2 != 0) {
+                    out.write(RemoteViewsProto.TextViewDrawableAction.Resources.TWO,
+                            appResources.getResourceName(mD2));
+                }
+                if (mD3 != 0) {
+                    out.write(RemoteViewsProto.TextViewDrawableAction.Resources.THREE,
+                            appResources.getResourceName(mD3));
+                }
+                if (mD4 != 0) {
+                    out.write(RemoteViewsProto.TextViewDrawableAction.Resources.FOUR,
+                            appResources.getResourceName(mD4));
+                }
+                out.end(resourcesToken);
+            }
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            values.put(RemoteViewsProto.TextViewDrawableAction.ICONS,
+                    new SparseArray<PendingResources<Icon>>());
+            values.put(RemoteViewsProto.TextViewDrawableAction.RESOURCES,
+                    new SparseArray<String>());
+            final long token = in.start(RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.TextViewDrawableAction.VIEW_ID:
+                        values.put(RemoteViewsProto.TextViewDrawableAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.TextViewDrawableAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE:
+                        values.put(RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE,
+                                in.readBoolean(
+                                        RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE));
+                        break;
+                    case (int) RemoteViewsProto.TextViewDrawableAction.RESOURCES:
+                        final long resourcesToken = in.start(
+                                RemoteViewsProto.TextViewDrawableAction.RESOURCES);
+                        while (in.nextField() != NO_MORE_FIELDS) {
+                            switch (in.getFieldNumber()) {
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Resources.ONE:
+                                    ((SparseArray<String>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
+                                            1, in.readString(
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction.Resources.ONE));
+                                    break;
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Resources.TWO:
+                                    ((SparseArray<String>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
+                                            2, in.readString(
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction.Resources.TWO));
+                                    break;
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Resources.THREE:
+                                    ((SparseArray<String>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
+                                            3, in.readString(
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction
+                                                            .Resources.THREE));
+                                    break;
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Resources.FOUR:
+                                    ((SparseArray<String>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.RESOURCES)).put(
+                                            4, in.readString(
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction
+                                                            .Resources.FOUR));
+                                    break;
+                                default:
+                                    Log.w(LOG_TAG,
+                                            "Unhandled field while reading RemoteViews proto!\n"
+                                                    + ProtoUtils.currentFieldToString(in));
+                            }
+                        }
+                        in.end(resourcesToken);
+                        break;
+                    case (int) RemoteViewsProto.TextViewDrawableAction.ICONS:
+                        final long iconsToken = in.start(
+                                RemoteViewsProto.TextViewDrawableAction.ICONS);
+                        while (in.nextField() != NO_MORE_FIELDS) {
+                            switch (in.getFieldNumber()) {
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Icons.ONE:
+                                    ((SparseArray<PendingResources<Icon>>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.ICONS)).put(1,
+                                            createIconFromProto(in,
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction.Icons.ONE));
+                                    break;
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Icons.TWO:
+                                    ((SparseArray<PendingResources<Icon>>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.ICONS)).put(2,
+                                            createIconFromProto(in,
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction.Icons.TWO));
+                                    break;
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Icons.THREE:
+                                    ((SparseArray<PendingResources<Icon>>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.ICONS)).put(3,
+                                            createIconFromProto(in,
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction.Icons.THREE));
+                                    break;
+                                case (int) RemoteViewsProto.TextViewDrawableAction.Icons.FOUR:
+                                    ((SparseArray<PendingResources<Icon>>) values.get(
+                                            RemoteViewsProto.TextViewDrawableAction.ICONS)).put(4,
+                                            createIconFromProto(in,
+                                                    RemoteViewsProto
+                                                            .TextViewDrawableAction.Icons.FOUR));
+                                    break;
+                                default:
+                                    Log.w(LOG_TAG,
+                                            "Unhandled field while reading RemoteViews proto!\n"
+                                                    + ProtoUtils.currentFieldToString(in));
+                            }
+                        }
+                        in.end(iconsToken);
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.TextViewDrawableAction.VIEW_ID});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.TextViewDrawableAction.VIEW_ID);
+                SparseArray<PendingResources<Icon>> icons =
+                        (SparseArray<PendingResources<Icon>>) values.get(
+                                RemoteViewsProto.TextViewDrawableAction.ICONS);
+                SparseArray<String> resArray = (SparseArray<String>) values.get(
+                        RemoteViewsProto.TextViewDrawableAction.RESOURCES);
+                boolean isRelative = (boolean) values.get(
+                        RemoteViewsProto.TextViewDrawableAction.IS_RELATIVE, false);
+                if (icons.size() > 0) {
+                    return new TextViewDrawableAction(viewId, isRelative,
+                            icons.get(1).create(context, resources, rootData, depth),
+                            icons.get(2).create(context, resources, rootData, depth),
+                            icons.get(3).create(context, resources, rootData, depth),
+                            icons.get(4).create(context, resources, rootData, depth));
+                } else {
+                    int first = resArray.contains(1) ? getAsIdentifier(resources, resArray, 1) : 0;
+                    int second = resArray.contains(2) ? getAsIdentifier(resources, resArray, 2) : 0;
+                    int third = resArray.contains(3) ? getAsIdentifier(resources, resArray, 3) : 0;
+                    int fourth = resArray.contains(4) ? getAsIdentifier(resources, resArray, 4) : 0;
+                    return new TextViewDrawableAction(viewId, isRelative, first, second, third,
+                            fourth);
+                }
+            };
+        }
     }
 
     /**
@@ -4535,6 +4855,58 @@
         public int getActionTag() {
             return TEXT_VIEW_SIZE_ACTION_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION);
+            out.write(RemoteViewsProto.TextViewSizeAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.TextViewSizeAction.UNITS, mUnits);
+            out.write(RemoteViewsProto.TextViewSizeAction.SIZE, mSize);
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.TextViewSizeAction.VIEW_ID:
+                        values.put(RemoteViewsProto.TextViewSizeAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.TextViewSizeAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.TextViewSizeAction.UNITS:
+                        values.put(RemoteViewsProto.TextViewSizeAction.UNITS,
+                                in.readInt(RemoteViewsProto.TextViewSizeAction.UNITS));
+                        break;
+                    case (int) RemoteViewsProto.TextViewSizeAction.SIZE:
+                        values.put(RemoteViewsProto.TextViewSizeAction.SIZE,
+                                in.readFloat(RemoteViewsProto.TextViewSizeAction.SIZE));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.TextViewSizeAction.VIEW_ID});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.TextViewSizeAction.VIEW_ID);
+                return new TextViewSizeAction(viewId,
+                        (int) values.get(RemoteViewsProto.TextViewSizeAction.UNITS, 0),
+                        (float) values.get(RemoteViewsProto.TextViewSizeAction.SIZE, 0));
+            };
+        }
     }
 
     /**
@@ -4579,6 +4951,70 @@
         public int getActionTag() {
             return VIEW_PADDING_ACTION_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(RemoteViewsProto.Action.VIEW_PADDING_ACTION);
+            out.write(RemoteViewsProto.ViewPaddingAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.ViewPaddingAction.LEFT, mLeft);
+            out.write(RemoteViewsProto.ViewPaddingAction.RIGHT, mRight);
+            out.write(RemoteViewsProto.ViewPaddingAction.TOP, mTop);
+            out.write(RemoteViewsProto.ViewPaddingAction.BOTTOM, mBottom);
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(RemoteViewsProto.Action.VIEW_PADDING_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.ViewPaddingAction.VIEW_ID:
+                        values.put(RemoteViewsProto.ViewPaddingAction.VIEW_ID,
+                                in.readString(RemoteViewsProto.ViewPaddingAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.ViewPaddingAction.LEFT:
+                        values.put(RemoteViewsProto.ViewPaddingAction.LEFT,
+                                in.readInt(RemoteViewsProto.ViewPaddingAction.LEFT));
+                        break;
+                    case (int) RemoteViewsProto.ViewPaddingAction.RIGHT:
+                        values.put(RemoteViewsProto.ViewPaddingAction.RIGHT,
+                                in.readInt(RemoteViewsProto.ViewPaddingAction.RIGHT));
+                        break;
+                    case (int) RemoteViewsProto.ViewPaddingAction.TOP:
+                        values.put(RemoteViewsProto.ViewPaddingAction.TOP,
+                                in.readInt(RemoteViewsProto.ViewPaddingAction.TOP));
+                        break;
+                    case (int) RemoteViewsProto.ViewPaddingAction.BOTTOM:
+                        values.put(RemoteViewsProto.ViewPaddingAction.BOTTOM,
+                                in.readInt(RemoteViewsProto.ViewPaddingAction.BOTTOM));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values, new long[]{RemoteViewsProto.ViewPaddingAction.VIEW_ID});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.ViewPaddingAction.VIEW_ID);
+                return new ViewPaddingAction(viewId,
+                        (int) values.get(RemoteViewsProto.ViewPaddingAction.LEFT, 0),
+                        (int) values.get(RemoteViewsProto.ViewPaddingAction.TOP, 0),
+                        (int) values.get(RemoteViewsProto.ViewPaddingAction.RIGHT, 0),
+                        (int) values.get(RemoteViewsProto.ViewPaddingAction.BOTTOM, 0));
+            };
+        }
     }
 
     /**
@@ -5241,6 +5677,69 @@
         public int getActionTag() {
             return SET_VIEW_OUTLINE_RADIUS_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return true;
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            final long token = out.start(
+                    RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION);
+            out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID,
+                    appResources.getResourceName(mViewId));
+            out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE, mValueType);
+            out.write(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE, mValue);
+            out.end(token);
+        }
+
+        public static PendingResources<Action> createFromProto(ProtoInputStream in)
+                throws Exception {
+            final LongSparseArray<Object> values = new LongSparseArray<>();
+
+            final long token = in.start(
+                    RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION);
+            while (in.nextField() != NO_MORE_FIELDS) {
+                switch (in.getFieldNumber()) {
+                    case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID:
+                        values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID,
+                                in.readString(
+                                        RemoteViewsProto
+                                                .SetViewOutlinePreferredRadiusAction.VIEW_ID));
+                        break;
+                    case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE:
+                        values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE,
+                                in.readInt(
+                                        RemoteViewsProto
+                                                .SetViewOutlinePreferredRadiusAction.VALUE_TYPE));
+                        break;
+                    case (int) RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE:
+                        values.put(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE,
+                                in.readInt(
+                                        RemoteViewsProto
+                                                .SetViewOutlinePreferredRadiusAction.VALUE));
+                        break;
+                    default:
+                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                                + ProtoUtils.currentFieldToString(in));
+                }
+            }
+            in.end(token);
+
+            checkContainsKeys(values,
+                    new long[]{RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID,
+                            RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE});
+
+            return (context, resources, rootData, depth) -> {
+                int viewId = getAsIdentifier(resources, values,
+                        RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VIEW_ID);
+                return new SetViewOutlinePreferredRadiusAction(viewId,
+                        (int) values.get(RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE,
+                                0), (int) values.get(
+                        RemoteViewsProto.SetViewOutlinePreferredRadiusAction.VALUE_TYPE));
+            };
+        }
     }
 
     /**
@@ -5324,6 +5823,46 @@
         public int getActionTag() {
             return SET_DRAW_INSTRUCTION_TAG;
         }
+
+        @Override
+        public boolean canWriteToProto() {
+            return drawDataParcel();
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream out, Context context, Resources appResources) {
+            if (!drawDataParcel()) return;
+            final long token = out.start(RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION);
+            if (mInstructions != null) {
+                for (byte[] bytes : mInstructions.mInstructions) {
+                    out.write(RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS, bytes);
+                }
+            }
+            out.end(token);
+        }
+    }
+
+    @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
+    private PendingResources<Action> createSetDrawInstructionActionFromProto(ProtoInputStream in)
+            throws Exception {
+        List<byte[]> instructions = new ArrayList<byte[]>();
+
+        final long token = in.start(RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION);
+        while (in.nextField() != NO_MORE_FIELDS) {
+            switch (in.getFieldNumber()) {
+                case (int) RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS:
+                    instructions.add(
+                            in.readBytes(RemoteViewsProto.SetDrawInstructionAction.INSTRUCTIONS));
+                    break;
+                default:
+                    Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+                            + ProtoUtils.currentFieldToString(in));
+            }
+        }
+        in.end(token);
+
+        return (context, resources, rootData, depth) -> new SetDrawInstructionAction(
+                new DrawInstructions.Builder(instructions).build());
     }
 
     /**
@@ -9604,12 +10143,20 @@
             }
             if (ref.mMode == MODE_NORMAL) {
                 rv.setIdealSize(ref.mIdealSize);
+                boolean hasDrawInstructionAction = false;
                 for (PendingResources<Action> pendingAction : ref.mActions) {
                     Action action = pendingAction.create(appContext, appResources, rootData, depth);
                     if (action != null) {
+                        if (action instanceof SetDrawInstructionAction) {
+                            hasDrawInstructionAction = true;
+                        }
                         rv.addAction(action);
                     }
                 }
+                if (rv.mHasDrawInstructions && !hasDrawInstructionAction) {
+                    throw new InvalidProtoException(
+                            "RemoteViews proto is missing DrawInstructions");
+                }
                 return rv;
             } else if (ref.mMode == MODE_HAS_SIZED_REMOTEVIEWS) {
                 List<RemoteViews> sizedViews = new ArrayList<>();
@@ -9685,6 +10232,23 @@
                 return rv.createSetRemoteCollectionItemListAdapterActionFromProto(in);
             case (int) RemoteViewsProto.Action.SET_RIPPLE_DRAWABLE_COLOR_ACTION:
                 return SetRippleDrawableColor.createFromProto(in);
+            case (int) RemoteViewsProto.Action.SET_VIEW_OUTLINE_PREFERRED_RADIUS_ACTION:
+                return SetViewOutlinePreferredRadiusAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.TEXT_VIEW_DRAWABLE_ACTION:
+                return TextViewDrawableAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.TEXT_VIEW_SIZE_ACTION:
+                return TextViewSizeAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.VIEW_GROUP_ADD_ACTION:
+                return rv.createViewGroupActionAddFromProto(in);
+            case (int) RemoteViewsProto.Action.VIEW_GROUP_REMOVE_ACTION:
+                return ViewGroupActionRemove.createFromProto(in);
+            case (int) RemoteViewsProto.Action.VIEW_PADDING_ACTION:
+                return ViewPaddingAction.createFromProto(in);
+            case (int) RemoteViewsProto.Action.SET_DRAW_INSTRUCTION_ACTION:
+                if (!drawDataParcel()) {
+                    return null;
+                }
+                return rv.createSetDrawInstructionActionFromProto(in);
             default:
                 throw new RuntimeException("Unhandled field while reading Action proto!\n"
                         + ProtoUtils.currentFieldToString(in));
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index d3733b7..23e572f 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -16,8 +16,13 @@
 
 package android.window;
 
+import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API;
+import static com.android.window.flags.Flags.predictiveBackTimestampApi;
+
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
+import android.util.TimeUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -44,18 +49,25 @@
     private final float mTouchX;
     private final float mTouchY;
     private final float mProgress;
+    private final long mFrameTime;
 
     @SwipeEdge
     private final int mSwipeEdge;
 
     /** @hide */
     public static BackEvent fromBackMotionEvent(BackMotionEvent backMotionEvent) {
-        return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(),
-                backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge());
+        if (predictiveBackTimestampApi()) {
+            return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(),
+                    backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge(),
+                    backMotionEvent.getFrameTime());
+        } else {
+            return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(),
+                    backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge());
+        }
     }
 
     /**
-     * Creates a new {@link BackEvent} instance.
+     * Creates a new {@link BackEvent} instance with the current uptime as frame time.
      *
      * @param touchX Absolute X location of the touch point of this event.
      * @param touchY Absolute Y location of the touch point of this event.
@@ -67,6 +79,26 @@
         mTouchY = touchY;
         mProgress = progress;
         mSwipeEdge = swipeEdge;
+        mFrameTime = System.nanoTime() / TimeUtils.NANOS_PER_MS;
+    }
+
+    /**
+     * Creates a new {@link BackEvent} instance.
+     *
+     * @param touchX Absolute X location of the touch point of this event.
+     * @param touchY Absolute Y location of the touch point of this event.
+     * @param progress Value between 0 and 1 on how far along the back gesture is.
+     * @param swipeEdge Indicates which edge the swipe starts from.
+     * @param frameTime frame time of the back event.
+     */
+    @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API)
+    public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge,
+            long frameTime) {
+        mTouchX = touchX;
+        mTouchY = touchY;
+        mProgress = progress;
+        mSwipeEdge = swipeEdge;
+        mFrameTime = frameTime;
     }
 
     /**
@@ -115,13 +147,38 @@
         return mSwipeEdge;
     }
 
+    /**
+     * Returns the frameTime of the BackEvent in milliseconds. Useful for calculating velocity.
+     */
+    @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API)
+    public long getFrameTime() {
+        return mFrameTime;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof BackEvent)) {
+            return false;
+        }
+        final BackEvent that = (BackEvent) other;
+        return mTouchX == that.mTouchX
+                && mTouchY == that.mTouchY
+                && mProgress == that.mProgress
+                && mSwipeEdge == that.mSwipeEdge
+                && mFrameTime == that.mFrameTime;
+    }
+
     @Override
     public String toString() {
         return "BackEvent{"
                 + "mTouchX=" + mTouchX
                 + ", mTouchY=" + mTouchY
                 + ", mProgress=" + mProgress
-                + ", mSwipeEdge" + mSwipeEdge
+                + ", mSwipeEdge=" + mSwipeEdge
+                + ", mFrameTime=" + mFrameTime + "ms"
                 + "}";
     }
 }
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index 8ac68ab..a8ec4ee 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -33,6 +33,7 @@
 public final class BackMotionEvent implements Parcelable {
     private final float mTouchX;
     private final float mTouchY;
+    private final long mFrameTime;
     private final float mProgress;
     private final boolean mTriggerBack;
 
@@ -48,6 +49,7 @@
      *
      * @param touchX Absolute X location of the touch point of this event.
      * @param touchY Absolute Y location of the touch point of this event.
+     * @param frameTime Event time of the corresponding touch event.
      * @param progress Value between 0 and 1 on how far along the back gesture is.
      * @param triggerBack Indicates whether the back arrow is in the triggered state or not
      * @param swipeEdge Indicates which edge the swipe starts from.
@@ -57,12 +59,14 @@
     public BackMotionEvent(
             float touchX,
             float touchY,
+            long frameTime,
             float progress,
             boolean triggerBack,
             @BackEvent.SwipeEdge int swipeEdge,
             @Nullable RemoteAnimationTarget departingAnimationTarget) {
         mTouchX = touchX;
         mTouchY = touchY;
+        mFrameTime = frameTime;
         mProgress = progress;
         mTriggerBack = triggerBack;
         mSwipeEdge = swipeEdge;
@@ -76,6 +80,7 @@
         mTriggerBack = in.readBoolean();
         mSwipeEdge = in.readInt();
         mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
+        mFrameTime = in.readLong();
     }
 
     @NonNull
@@ -104,6 +109,7 @@
         dest.writeBoolean(mTriggerBack);
         dest.writeInt(mSwipeEdge);
         dest.writeTypedObject(mDepartingAnimationTarget, flags);
+        dest.writeLong(mFrameTime);
     }
 
     /**
@@ -148,6 +154,13 @@
     }
 
     /**
+     * Returns the frame time of the BackMotionEvent in milliseconds.
+     */
+    public long getFrameTime() {
+        return mFrameTime;
+    }
+
+    /**
      * Returns the {@link RemoteAnimationTarget} of the top departing application window,
      * or {@code null} if the top window should not be moved for the current type of back
      * destination.
@@ -162,10 +175,11 @@
         return "BackMotionEvent{"
                 + "mTouchX=" + mTouchX
                 + ", mTouchY=" + mTouchY
+                + ", mFrameTime=" + mFrameTime + "ms"
                 + ", mProgress=" + mProgress
                 + ", mTriggerBack=" + mTriggerBack
-                + ", mSwipeEdge" + mSwipeEdge
-                + ", mDepartingAnimationTarget" + mDepartingAnimationTarget
+                + ", mSwipeEdge=" + mSwipeEdge
+                + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
                 + "}";
     }
 }
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index 465e11a..a5be58b 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -17,6 +17,7 @@
 package android.window;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.window.flags.Flags.predictiveBackTimestampApi;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -66,7 +67,8 @@
                 reset();
             };
     private final DynamicAnimation.OnAnimationUpdateListener mOnBackInvokedFlingUpdateListener =
-            (animation, progress, velocity) -> updateProgressValue(progress, velocity);
+            (animation, progress, velocity) ->
+                    updateProgressValue(progress, velocity, animation.getLastFrameTime());
 
 
     private void setProgress(float progress) {
@@ -92,7 +94,9 @@
 
     @Override
     public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
-        if (mBackInvokedFinishRunnable == null) updateProgressValue(value, velocity);
+        if (mBackInvokedFinishRunnable == null) {
+            updateProgressValue(value, velocity, animation.getLastFrameTime());
+        }
     }
 
 
@@ -137,7 +141,9 @@
         mLastBackEvent = event;
         mCallback = callback;
         mBackAnimationInProgress = true;
-        updateProgressValue(0, 0);
+        updateProgressValue(/* progress */ 0, /* velocity */ 0,
+                /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS);
+        onBackProgressed(event);
     }
 
     /**
@@ -146,7 +152,8 @@
     public void reset() {
         if (mBackCancelledFinishRunnable != null) {
             // Ensure that last progress value that apps see is 0
-            updateProgressValue(0, 0);
+            updateProgressValue(/* progress */ 0, /* velocity */ 0,
+                    /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS);
             invokeBackCancelledRunnable();
         } else if (mBackInvokedFinishRunnable != null) {
             invokeBackInvokedRunnable();
@@ -236,14 +243,20 @@
         return mVelocity / SCALE_FACTOR;
     }
 
-    private void updateProgressValue(float progress, float velocity) {
+    private void updateProgressValue(float progress, float velocity, long frameTime) {
         mVelocity = velocity;
         if (mLastBackEvent == null || mCallback == null || !mBackAnimationInProgress) {
             return;
         }
-        mCallback.onProgressUpdate(
-                new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(),
-                        progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge()));
+        BackEvent backEvent;
+        if (predictiveBackTimestampApi()) {
+            backEvent = new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(),
+                    progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge(), frameTime);
+        } else {
+            backEvent = new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(),
+                    progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge());
+        }
+        mCallback.onProgressUpdate(backEvent);
     }
 
     private void invokeBackCancelledRunnable() {
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index 290c494..39a3025 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -151,6 +151,7 @@
         return new BackMotionEvent(
                 /* touchX = */ mInitTouchX,
                 /* touchY = */ mInitTouchY,
+                /* frameTime = */ 0,
                 /* progress = */ 0,
                 /* triggerBack = */ mTriggerBack,
                 /* swipeEdge = */ mSwipeEdge,
@@ -235,6 +236,7 @@
         return new BackMotionEvent(
                 /* touchX = */ mLatestTouchX,
                 /* touchY = */ mLatestTouchY,
+                /* frameTime = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ mTriggerBack,
                 /* swipeEdge = */ mSwipeEdge,
diff --git a/core/java/android/window/flags/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
similarity index 94%
rename from core/java/android/window/flags/DesktopModeFlags.java
rename to core/java/android/window/DesktopModeFlags.java
index 47af50da..05dc910 100644
--- a/core/java/android/window/flags/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.window.flags;
+package android.window;
 
 import android.annotation.Nullable;
 import android.app.ActivityThread;
@@ -64,8 +64,12 @@
     ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
     ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(
             Flags::enableDesktopWindowingTaskbarRunningApps, true),
+    // TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up
     ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
-    ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false);
+    ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
+    ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
+    ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
+            Flags::enableWindowingTransitionHandlersObservers, false);
 
     private static final String TAG = "DesktopModeFlagsUtil";
     // Function called to obtain aconfig flag value.
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index 66c35e2..8db1f95 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -17,6 +17,7 @@
 package android.window;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.window.flags.Flags.predictiveBackTimestampApi;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -235,8 +236,12 @@
         @Override
         public void onBackStarted(@NonNull BackEvent backEvent) {
             try {
+                long frameTime = 0;
+                if (predictiveBackTimestampApi()) {
+                    frameTime = backEvent.getFrameTime();
+                }
                 mIOnBackInvokedCallback.onBackStarted(
-                        new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(),
+                        new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
                                 backEvent.getProgress(), false, backEvent.getSwipeEdge(),
                                 null));
             } catch (RemoteException e) {
@@ -247,8 +252,12 @@
         @Override
         public void onBackProgressed(@NonNull BackEvent backEvent) {
             try {
+                long frameTime = 0;
+                if (predictiveBackTimestampApi()) {
+                    frameTime = backEvent.getFrameTime();
+                }
                 mIOnBackInvokedCallback.onBackProgressed(
-                        new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(),
+                        new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
                                 backEvent.getProgress(), false, backEvent.getSwipeEdge(),
                                 null));
             } catch (RemoteException e) {
diff --git a/core/java/android/window/OWNERS b/core/java/android/window/OWNERS
index 2c61df9..77c99b9 100644
--- a/core/java/android/window/OWNERS
+++ b/core/java/android/window/OWNERS
@@ -1,3 +1,5 @@
 set noparent
 
 include /services/core/java/com/android/server/wm/OWNERS
+
+per-file DesktopModeFlags.java = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 5d14698..a68bdc0 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -839,6 +839,16 @@
     }
 
     /**
+     * Returns true if the SurfaceSyncGroup has completed its sync.
+     * @hide
+     */
+    public boolean isComplete() {
+        synchronized (mLock) {
+            return mFinished;
+        }
+    }
+
+    /**
      * A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
      * implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync
      * knows when the frame is ready to add to the sync.
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 20d1b3b..a37bef8 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -83,13 +83,16 @@
     public static final int REFERENCE_CACHE = 1 << 1;
     /** This snapshot object is being persistent. */
     public static final int REFERENCE_PERSIST = 1 << 2;
+    /** This snapshot object is being used for content suggestion. */
+    public static final int REFERENCE_CONTENT_SUGGESTION = 1 << 3;
     @IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
             REFERENCE_BROADCAST,
             REFERENCE_CACHE,
-            REFERENCE_PERSIST
+            REFERENCE_PERSIST,
+            REFERENCE_CONTENT_SUGGESTION
     })
     @Retention(RetentionPolicy.SOURCE)
-    @interface ReferenceFlags {}
+    public @interface ReferenceFlags {}
 
     public TaskSnapshot(long id, long captureTime,
             @NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 8e495ec..34abf31 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -525,6 +525,22 @@
     }
 
     /**
+     * Disables or enables activities to be started in adjacent tasks (see
+     * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT}) for the specified root of any child tasks.  This
+     * differs from {@link #setLaunchAdjacentFlagRoot(WindowContainerToken)} which controls the
+     * preferred launch-adjacent target and allows for selectively setting which root tasks can
+     * support launch-adjacent.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction setDisableLaunchAdjacent(
+            @NonNull WindowContainerToken container, boolean disabled) {
+        mHierarchyOps.add(HierarchyOp.createForSetDisableLaunchAdjacent(container.asBinder(),
+                disabled));
+        return this;
+    }
+
+    /**
      * Starts a task by id. The task is expected to already exist (eg. as a recent task).
      * @param taskId Id of task to start.
      * @param options bundle containing ActivityOptions for the task's top activity.
@@ -1488,6 +1504,7 @@
         public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20;
         public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21;
         public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22;
+        public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23;
 
         // The following key(s) are for use with mLaunchOptions:
         // When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1556,6 +1573,8 @@
 
         private @InsetsType int mExcludeInsetsTypes;
 
+        private boolean mLaunchAdjacentDisabled;
+
         public static HierarchyOp createForReparent(
                 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1644,6 +1663,15 @@
                     .build();
         }
 
+        /** Create a hierarchy op for disabling launch adjacent. */
+        public static HierarchyOp createForSetDisableLaunchAdjacent(IBinder container,
+                boolean disabled) {
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT)
+                    .setContainer(container)
+                    .setLaunchAdjacentDisabled(disabled)
+                    .build();
+        }
+
         /** create a hierarchy op for deleting a task **/
         public static HierarchyOp createForRemoveTask(@NonNull IBinder container) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK)
@@ -1695,6 +1723,7 @@
             mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch;
             mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents;
             mExcludeInsetsTypes = copy.mExcludeInsetsTypes;
+            mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled;
         }
 
         protected HierarchyOp(Parcel in) {
@@ -1719,6 +1748,7 @@
             mReparentLeafTaskIfRelaunch = in.readBoolean();
             mIsTrimmableFromRecents = in.readBoolean();
             mExcludeInsetsTypes = in.readInt();
+            mLaunchAdjacentDisabled = in.readBoolean();
         }
 
         public int getType() {
@@ -1814,13 +1844,11 @@
         }
 
         /** Denotes whether the parents should also be included in the op. */
-        @NonNull
         public boolean includingParents() {
             return mIncludingParents;
         }
 
-        /** Set the task to be trimmable */
-        @NonNull
+        /** Denotes whether the task can be trimmable from recents */
         public boolean isTrimmableFromRecents() {
             return mIsTrimmableFromRecents;
         }
@@ -1829,6 +1857,11 @@
             return mExcludeInsetsTypes;
         }
 
+        /** Denotes whether launch-adjacent flag is respected from this task or its children */
+        public boolean isLaunchAdjacentDisabled() {
+            return mLaunchAdjacentDisabled;
+        }
+
         /** Gets a string representation of a hierarchy-op type. */
         public static String hopToString(int type) {
             switch (type) {
@@ -1839,6 +1872,8 @@
                 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot";
                 case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask";
                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot";
+                case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
+                    return "SetDisableLaunchAdjacent";
                 case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent";
                 case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut";
                 case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider";
@@ -1891,6 +1926,10 @@
                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
                     sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop);
                     break;
+                case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
+                    sb.append("container=").append(mContainer).append(" disabled=")
+                            .append(mLaunchAdjacentDisabled);
+                    break;
                 case HIERARCHY_OP_TYPE_START_SHORTCUT:
                     sb.append("options=").append(mLaunchOptions)
                             .append(" info=").append(mShortcutInfo);
@@ -1971,6 +2010,7 @@
             dest.writeBoolean(mReparentLeafTaskIfRelaunch);
             dest.writeBoolean(mIsTrimmableFromRecents);
             dest.writeInt(mExcludeInsetsTypes);
+            dest.writeBoolean(mLaunchAdjacentDisabled);
         }
 
         @Override
@@ -2047,6 +2087,8 @@
 
             private @InsetsType int mExcludeInsetsTypes;
 
+            private boolean mLaunchAdjacentDisabled;
+
             Builder(int type) {
                 mType = type;
             }
@@ -2153,6 +2195,11 @@
                 return this;
             }
 
+            Builder setLaunchAdjacentDisabled(boolean disabled) {
+                mLaunchAdjacentDisabled = disabled;
+                return this;
+            }
+
             HierarchyOp build() {
                 final HierarchyOp hierarchyOp = new HierarchyOp(mType);
                 hierarchyOp.mContainer = mContainer;
@@ -2179,6 +2226,7 @@
                 hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;
                 hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents;
                 hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes;
+                hierarchyOp.mLaunchAdjacentDisabled = mLaunchAdjacentDisabled;
 
                 return hierarchyOp;
             }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 44a374f..c9d458f 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -17,6 +17,7 @@
 package android.window;
 
 import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver;
+import static com.android.window.flags.Flags.predictiveBackTimestampApi;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -563,7 +564,8 @@
                 }
                 OnBackAnimationCallback animationCallback = getBackAnimationCallback();
                 if (animationCallback != null
-                        && !(callback instanceof ImeBackAnimationController)) {
+                        && !(callback instanceof ImeBackAnimationController)
+                        && !predictiveBackTimestampApi()) {
                     mProgressAnimator.onBackInvoked(() -> {
                         if (mIsSystemCallback) {
                             mSystemNavigationObserverCallbackRunnable.run();
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index f739622..235ba3a 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -37,6 +37,13 @@
 }
 
 flag {
+  name: "move_to_external_display_shortcut"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether the move to external display shortcut in overview is available"
+  bug: "372872848"
+}
+
+flag {
   name: "app_compat_properties_api"
   is_exported: true
   namespace: "large_screen_experiences_app_compat"
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index b22aa22..0bcc9c1 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -228,7 +228,7 @@
     name: "enable_desktop_windowing_app_handle_education"
     namespace: "lse_desktop_experience"
     description: "Enables desktop windowing app handle education"
-    bug: "348208342"
+    bug: "316006079"
 }
 
 flag {
@@ -239,6 +239,13 @@
 }
 
 flag {
+    name: "enable_desktop_windowing_enter_transitions"
+    namespace: "lse_desktop_experience"
+    description: "Enables enter desktop windowing transition & motion polish changes"
+    bug: "369763947"
+}
+
+flag {
     name: "enable_desktop_windowing_exit_transitions"
     namespace: "lse_desktop_experience"
     description: "Enables exit desktop windowing transition & motion polish changes"
@@ -301,6 +308,13 @@
 }
 
 flag {
+    name: "enable_restore_to_previous_size_from_desktop_immersive"
+    namespace: "lse_desktop_experience"
+    description: "Restores the window bounds to their previous size when exiting desktop immersive"
+    bug: "372318163"
+}
+
+flag {
     name: "enable_display_focus_in_shell_transitions"
     namespace: "lse_desktop_experience"
     description: "Creates a shell transition when display focus switches."
@@ -319,4 +333,18 @@
     namespace: "lse_desktop_experience"
     description: "Enables custom transitions for alt-tab app launches in Desktop Mode."
     bug: "370735595"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "enable_move_to_next_display_shortcut"
+    namespace: "lse_desktop_experience"
+    description: "Add new keyboard shortcut of moving a task into next display"
+    bug: "364154795"
+}
+
+flag {
+    name: "enable_drag_to_maximize"
+    namespace: "lse_desktop_experience"
+    description: "Enables a switch to change the concequence of dragging a window to the top edge."
+    bug: "372614715"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index c9b93c9..0d235ff 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -70,17 +70,6 @@
 }
 
 flag {
-  name: "common_surface_animator"
-  namespace: "windowing_frontend"
-  description: "A reusable surface animator for default transition"
-  bug: "326331384"
-  is_fixed_read_only: true
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "reduce_keyguard_transitions"
   namespace: "windowing_frontend"
   description: "Avoid setting keyguard transitions ready unless there are no other changes"
@@ -173,6 +162,16 @@
 }
 
 flag {
+  name: "filter_irrelevant_input_device_change"
+  namespace: "windowing_frontend"
+  description: "Recompute display configuration only for necessary input device changes"
+  bug: "368461853"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "respect_non_top_visible_fixed_orientation"
   namespace: "windowing_frontend"
   description: "If top activity is not opaque, respect the fixed orientation of activity behind it"
@@ -315,3 +314,11 @@
     is_fixed_read_only: true
     bug: "362938401"
 }
+
+flag {
+    name: "predictive_back_timestamp_api"
+    namespace: "systemui"
+    description: "expose timestamp in BackEvent (API extension)"
+    is_fixed_read_only: true
+    bug: "362938401"
+}
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index f0ea7a8..b7012b6 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -108,3 +108,10 @@
     description: "Makes WindowLayoutInfo accessible without racing in the Activity#onCreate()"
     bug: "337820752"
 }
+
+flag {
+    namespace: "windowing_sdk"
+    name: "better_support_non_match_parent_activity"
+    description: "Relax the assumption of non-match parent activity"
+    bug: "356277166"
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java
index 0f8ced2..3557633 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityServiceWarning.java
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.accessibility.Flags;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -58,13 +59,15 @@
             @NonNull View.OnClickListener uninstallListener) {
         final AlertDialog ad = new AlertDialog.Builder(context)
                 .setView(createAccessibilityServiceWarningDialogContentView(
-                                context, info, allowListener, denyListener, uninstallListener))
+                        context, info, allowListener, denyListener, uninstallListener))
                 .setCancelable(true)
                 .create();
         Window window = ad.getWindow();
         WindowManager.LayoutParams params = window.getAttributes();
         params.privateFlags |= SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+        if (!Flags.warningUseDefaultDialogType()) {
+            params.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+        }
         window.setAttributes(params);
         return ad;
     }
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 18c8eb4..de7ad34 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -1195,7 +1195,12 @@
 
         @Nullable
         protected Drawable loadIconFromResource(Resources res, int resId) {
-            return res.getDrawableForDensity(resId, mIconDpi);
+            try {
+                return res.getDrawableForDensity(resId, mIconDpi);
+            } catch (Resources.NotFoundException e) {
+                Log.e(TAG, "Resource not found", e);
+                return null;
+            }
         }
 
     }
diff --git a/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java b/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java
index d4fe7c8d..7a4c1a0 100644
--- a/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java
+++ b/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java
@@ -762,6 +762,10 @@
         return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance();
     }
 
+    public long getLastFrameTime() {
+        return mLastFrameTime;
+    }
+
     /****************Sub class animations**************/
     /**
      * Returns the acceleration at the given value with the given velocity.
diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
index 39aadfb..8faaf95 100644
--- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
+++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
@@ -53,19 +53,18 @@
  * @hide
  */
 public class AconfigFlags {
+    private static final boolean DEBUG = false;
     private static final String LOG_TAG = "AconfigFlags";
-
-    public enum Permission {
-        READ_WRITE,
-        READ_ONLY
-    }
+    private static final String OVERRIDE_PREFIX = "device_config_overrides/";
+    private static final String STAGED_PREFIX = "staged/";
 
     private final ArrayMap<String, Boolean> mFlagValues = new ArrayMap<>();
-    private final ArrayMap<String, Permission> mFlagPermissions = new ArrayMap<>();
 
     public AconfigFlags() {
         if (!Flags.manifestFlagging()) {
-            Slog.v(LOG_TAG, "Feature disabled, skipped all loading");
+            if (DEBUG) {
+                Slog.v(LOG_TAG, "Feature disabled, skipped all loading");
+            }
             return;
         }
         final var defaultFlagProtoFiles =
@@ -130,19 +129,17 @@
                     if (!"false".equalsIgnoreCase(value) && !"true".equalsIgnoreCase(value)) {
                         continue;
                     }
-                    final var overridePrefix = "device_config_overrides/";
-                    final var stagedPrefix = "staged/";
                     String separator = "/";
                     String prefix = "default";
                     int priority = 0;
-                    if (name.startsWith(overridePrefix)) {
-                        prefix = overridePrefix;
-                        name = name.substring(overridePrefix.length());
+                    if (name.startsWith(OVERRIDE_PREFIX)) {
+                        prefix = OVERRIDE_PREFIX;
+                        name = name.substring(OVERRIDE_PREFIX.length());
                         separator = ":";
                         priority = 20;
-                    } else if (name.startsWith(stagedPrefix)) {
-                        prefix = stagedPrefix;
-                        name = name.substring(stagedPrefix.length());
+                    } else if (name.startsWith(STAGED_PREFIX)) {
+                        prefix = STAGED_PREFIX;
+                        name = name.substring(STAGED_PREFIX.length());
                         separator = "*";
                         priority = 10;
                     }
@@ -155,12 +152,19 @@
                     if (!mFlagValues.containsKey(flagPackageAndName)) {
                         continue;
                     }
-                    Slog.d(LOG_TAG, "Found " + prefix
-                            + " Aconfig flag value for " + flagPackageAndName + " = " + value);
+                    if (DEBUG) {
+                        Slog.d(LOG_TAG, "Found " + prefix
+                                + " Aconfig flag value in settings for " + flagPackageAndName
+                                + " = " + value);
+                    }
                     final Integer currentPriority = flagPriority.get(flagPackageAndName);
                     if (currentPriority != null && currentPriority >= priority) {
-                        Slog.i(LOG_TAG, "Skipping " + prefix + " flag " + flagPackageAndName
-                                + " because of the existing one with priority " + currentPriority);
+                        if (DEBUG) {
+                            Slog.d(LOG_TAG, "Skipping " + prefix + " flag "
+                                    + flagPackageAndName
+                                    + " in settings because of existing one with priority "
+                                    + currentPriority);
+                        }
                         continue;
                     }
                     flagPriority.put(flagPackageAndName, priority);
@@ -185,15 +189,7 @@
         for (parsed_flag flag : parsedFlags.parsedFlag) {
             String flagPackageAndName = flag.package_ + "." + flag.name;
             boolean flagValue = (flag.state == Aconfig.ENABLED);
-            Slog.v(LOG_TAG, "Read Aconfig default flag value "
-                    + flagPackageAndName + " = " + flagValue);
             mFlagValues.put(flagPackageAndName, flagValue);
-
-            Permission permission = flag.permission == Aconfig.READ_ONLY
-                    ? Permission.READ_ONLY
-                    : Permission.READ_WRITE;
-
-            mFlagPermissions.put(flagPackageAndName, permission);
         }
     }
 
@@ -203,24 +199,15 @@
      * @return the current value of the given Aconfig flag, or null if there is no such flag
      */
     @Nullable
-    public Boolean getFlagValue(@NonNull String flagPackageAndName) {
+    private Boolean getFlagValue(@NonNull String flagPackageAndName) {
         Boolean value = mFlagValues.get(flagPackageAndName);
-        Slog.d(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value);
+        if (DEBUG) {
+            Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value);
+        }
         return value;
     }
 
     /**
-     * Get the flag permission, or null if the flag doesn't exist.
-     * @param flagPackageAndName Full flag name formatted as 'package.flag'
-     * @return the current permission of the given Aconfig flag, or null if there is no such flag
-     */
-    @Nullable
-    public Permission getFlagPermission(@NonNull String flagPackageAndName) {
-        Permission permission = mFlagPermissions.get(flagPackageAndName);
-        return permission;
-    }
-
-    /**
      * Check if the element in {@code parser} should be skipped because of the feature flag.
      * @param parser XML parser object currently parsing an element
      * @return true if the element is disabled because of its feature flag
@@ -247,7 +234,7 @@
         }
         // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
         if (flagValue == negated) {
-            Slog.v(LOG_TAG, "Skipping element " + parser.getName()
+            Slog.i(LOG_TAG, "Skipping element " + parser.getName()
                     + " behind feature flag " + featureFlag + " = " + flagValue);
             return true;
         }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 6faea17..bd746d5 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -38,8 +38,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.flags.Flags.customizableWindowHeaders;
-import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
-import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
+import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
+import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
 
 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
 
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index f3dc896..a037cb4 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -218,6 +218,22 @@
         // NOTE: Registering that datasource is an async operation, so there may be no data traced
         // for some messages logged right after the construction of this class.
         mDataSource.register(params);
+
+        if (viewerConfigInputStreamProvider == null && viewerConfigFilePath != null) {
+            viewerConfigInputStreamProvider = new ViewerConfigInputStreamProvider() {
+                @NonNull
+                @Override
+                public ProtoInputStream getInputStream() {
+                    try {
+                        return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+                    } catch (FileNotFoundException e) {
+                        throw new RuntimeException(
+                                "Failed to load viewer config file " + viewerConfigFilePath, e);
+                    }
+                }
+            };
+        }
+
         this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
         this.mViewerConfigReader = viewerConfigReader;
         this.mCacheUpdater = cacheUpdater;
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 0a80e00..571fe0b 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -39,7 +39,7 @@
      * or the viewer config is not loaded into memory.
      */
     @Nullable
-    public synchronized String getViewerString(long messageHash) {
+    public String getViewerString(long messageHash) {
         return mLogMessageMap.get(messageHash);
     }
 
@@ -96,6 +96,7 @@
                 logger.log("Unloading viewer config hash " + hash);
                 mLogMessageMap.remove(hash);
             }
+            mGroupHashes.remove(group);
         }
     }
 
@@ -179,6 +180,6 @@
             }
         }
 
-        throw new RuntimeException("Group " + group + "not found in viewer config");
+        throw new RuntimeException("Group " + group + " not found in viewer config");
     }
 }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index f177e14..81b885a 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -78,8 +78,9 @@
     void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
     void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList);
     void onMediaQualityStatusChanged(in MediaQualityStatus mediaQualityStatus);
-    void onCallBackModeStarted(int type);
-    void onCallBackModeStopped(int type, int reason);
+    void onCallbackModeStarted(int type, long durationMillis, int subId);
+    void onCallbackModeRestarted(int type, long durationMillis, int subId);
+    void onCallbackModeStopped(int type, int reason, int subId);
     void onSimultaneousCallingStateChanged(in int[] subIds);
     void onCarrierRoamingNtnModeChanged(in boolean active);
     void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible);
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index e500a37a..f836cf2 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -118,7 +118,8 @@
     void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg);
     void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId);
 
-    void notifyCallbackModeStarted(int phoneId, int subId, int type);
+    void notifyCallbackModeStarted(int phoneId, int subId, int type, long durationMillis);
+    void notifyCallbackModeRestarted(int phoneId, int subId, int type, long durationMillis);
     void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason);
     void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active);
     void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible);
diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java
index 0c5c853..d34bca6 100644
--- a/core/java/com/android/internal/util/MemInfoReader.java
+++ b/core/java/com/android/internal/util/MemInfoReader.java
@@ -88,6 +88,13 @@
     }
 
     /**
+     * Amount of RAM that used by shared memory (shmem) and tmpfs
+     */
+    public long getShmemSizeKb() {
+        return mInfos[Debug.MEMINFO_SHMEM];
+    }
+
+    /**
      * Amount of RAM that the kernel is being used for caches, not counting caches
      * that are mapped in to processes.
      */
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
new file mode 100644
index 0000000..d3b1f97
--- /dev/null
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification.ProgressStyle;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.RemotableViewMethod;
+import android.widget.ProgressBar;
+import android.widget.RemoteViews;
+
+import androidx.annotation.ColorInt;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.internal.widget.NotificationProgressDrawable.Part;
+import com.android.internal.widget.NotificationProgressDrawable.Point;
+import com.android.internal.widget.NotificationProgressDrawable.Segment;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * NotificationProgressBar extends the capabilities of ProgressBar by adding functionalities to
+ * represent Notification ProgressStyle progress, such as for ridesharing and navigation.
+ */
+@RemoteViews.RemoteView
+public class NotificationProgressBar extends ProgressBar {
+    private static final String TAG = "NotificationProgressBar";
+
+    private NotificationProgressModel mProgressModel;
+
+    @Nullable
+    private List<Part> mProgressDrawableParts = null;
+
+    @Nullable
+    private Drawable mProgressTrackerDrawable = null;
+
+    public NotificationProgressBar(Context context) {
+        this(context, null);
+    }
+
+    public NotificationProgressBar(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.progressBarStyle);
+    }
+
+    public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Setter for the notification progress model.
+     *
+     * @see NotificationProgressModel#fromBundle
+     * @see #setProgressModelAsync
+     */
+    @RemotableViewMethod(asyncImpl = "setProgressModelAsync")
+    public void setProgressModel(@Nullable Bundle bundle) {
+        Preconditions.checkArgument(bundle != null,
+                "Bundle shouldn't be null");
+
+        mProgressModel = NotificationProgressModel.fromBundle(bundle);
+
+        if (mProgressModel.isIndeterminate()) {
+            final int indeterminateColor = mProgressModel.getIndeterminateColor();
+            setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
+        } else {
+            mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
+                    mProgressModel.getPoints(),
+                    mProgressModel.getProgress(), mProgressModel.isStyledByProgress());
+
+            try {
+                final NotificationProgressDrawable drawable = getNotificationProgressDrawable();
+                drawable.setParts(mProgressDrawableParts);
+            } catch (IllegalStateException ex) {
+                Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex);
+            }
+        }
+    }
+
+    @NonNull
+    private NotificationProgressDrawable getNotificationProgressDrawable() {
+        final Drawable d = getProgressDrawable();
+        if (d == null) {
+            throw new IllegalStateException("getProgressDrawable() returns null");
+        }
+        if (!(d instanceof LayerDrawable)) {
+            throw new IllegalStateException("getProgressDrawable() doesn't return a LayerDrawable");
+        }
+
+        final Drawable layer = ((LayerDrawable) d).findDrawableByLayerId(R.id.background);
+        if (!(layer instanceof NotificationProgressDrawable)) {
+            throw new IllegalStateException(
+                    "Couldn't get NotificationProgressDrawable, retrieved drawable is: " + (
+                            layer != null ? layer.toString() : null));
+        }
+
+        return (NotificationProgressDrawable) layer;
+    }
+
+    /**
+     * Setter for the progress tracker icon.
+     *
+     * @see #setProgressTrackerIconAsync
+     */
+    @RemotableViewMethod(asyncImpl = "setProgressTrackerIconAsync")
+    public void setProgressTrackerIcon(@Nullable Icon icon) {
+    }
+
+    /**
+     * Async version of {@link #setProgressTrackerIcon}
+     */
+    public Runnable setProgressTrackerIconAsync(@Nullable Icon icon) {
+        final Drawable progressTrackerDrawable;
+        if (icon != null) {
+            progressTrackerDrawable = icon.loadDrawable(getContext());
+        } else {
+            progressTrackerDrawable = null;
+        }
+        return () -> {
+            setProgressTrackerDrawable(progressTrackerDrawable);
+        };
+    }
+
+    private void setProgressTrackerDrawable(@Nullable  Drawable drawable) {
+        mProgressTrackerDrawable = drawable;
+    }
+
+    /**
+     * Processes the ProgressStyle data and convert to list of {@code
+     * NotificationProgressDrawable.Part}.
+     */
+    @VisibleForTesting
+    public static List<Part> processAndConvertToDrawableParts(
+            List<ProgressStyle.Segment> segments,
+            List<ProgressStyle.Point> points,
+            int progress,
+            boolean isStyledByProgress
+    ) {
+        if (segments.isEmpty()) {
+            throw new IllegalArgumentException("List of segments shouldn't be empty");
+        }
+
+        for (ProgressStyle.Segment segment : segments) {
+            final int length = segment.getLength();
+            if (length <= 0) {
+                throw new IllegalArgumentException("Invalid segment length : " + length);
+            }
+        }
+
+        final int progressMax = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum();
+
+        if (progress < 0 || progress > progressMax) {
+            throw new IllegalArgumentException("Invalid progress : " + progress);
+        }
+        for (ProgressStyle.Point point : points) {
+            final int pos = point.getPosition();
+            if (pos <= 0 || pos >= progressMax) {
+                throw new IllegalArgumentException("Invalid Point position : " + pos);
+            }
+        }
+
+        final Map<Integer, ProgressStyle.Segment> startToSegmentMap = generateStartToSegmentMap(
+                segments);
+        final Map<Integer, ProgressStyle.Point> positionToPointMap = generatePositionToPointMap(
+                points);
+        final SortedSet<Integer> sortedPos = generateSortedPositionSet(startToSegmentMap,
+                positionToPointMap, progress, isStyledByProgress);
+
+        final Map<Integer, ProgressStyle.Segment> startToSplitSegmentMap =
+                splitSegmentsByPointsAndProgress(
+                        startToSegmentMap, sortedPos, progressMax);
+
+        return convertToDrawableParts(startToSplitSegmentMap, positionToPointMap, sortedPos,
+                progress, progressMax,
+                isStyledByProgress);
+    }
+
+
+    // Any segment with a point on it gets split by the point.
+    // If isStyledByProgress is true, also split the segment with the progress value in its range.
+    private static Map<Integer, ProgressStyle.Segment> splitSegmentsByPointsAndProgress(
+            Map<Integer, ProgressStyle.Segment> startToSegmentMap,
+            SortedSet<Integer> sortedPos,
+            int progressMax) {
+        int prevSegStart = 0;
+        for (Integer pos : sortedPos) {
+            if (pos == 0 || pos == progressMax) continue;
+            if (startToSegmentMap.containsKey(pos)) {
+                prevSegStart = pos;
+                continue;
+            }
+
+            final ProgressStyle.Segment prevSeg = startToSegmentMap.get(prevSegStart);
+            final ProgressStyle.Segment leftSeg = new ProgressStyle.Segment(
+                    pos - prevSegStart).setColor(
+                    prevSeg.getColor());
+            final ProgressStyle.Segment rightSeg = new ProgressStyle.Segment(
+                    prevSegStart + prevSeg.getLength() - pos).setColor(prevSeg.getColor());
+
+            startToSegmentMap.put(prevSegStart, leftSeg);
+            startToSegmentMap.put(pos, rightSeg);
+
+            prevSegStart = pos;
+        }
+
+        return startToSegmentMap;
+    }
+
+    private static List<Part> convertToDrawableParts(
+            Map<Integer, ProgressStyle.Segment> startToSegmentMap,
+            Map<Integer, ProgressStyle.Point> positionToPointMap,
+            SortedSet<Integer> sortedPos,
+            int progress,
+            int progressMax,
+            boolean isStyledByProgress
+    ) {
+        List<Part> parts = new ArrayList<>();
+        boolean styleRemainingParts = false;
+        for (Integer pos : sortedPos) {
+            if (positionToPointMap.containsKey(pos)) {
+                final ProgressStyle.Point point = positionToPointMap.get(pos);
+                final int color = maybeGetFadedColor(point.getColor(), styleRemainingParts);
+                parts.add(new Point(null, color, styleRemainingParts));
+            }
+            // We want the Point at the current progress to be filled (not faded), but a Segment
+            // starting at this progress to be faded.
+            if (isStyledByProgress && !styleRemainingParts && pos == progress) {
+                styleRemainingParts = true;
+            }
+            if (startToSegmentMap.containsKey(pos)) {
+                final ProgressStyle.Segment seg = startToSegmentMap.get(pos);
+                final int color = maybeGetFadedColor(seg.getColor(), styleRemainingParts);
+                parts.add(new Segment(
+                        (float) seg.getLength() / progressMax, color, styleRemainingParts));
+            }
+        }
+
+        return parts;
+    }
+
+    @ColorInt
+    private static int maybeGetFadedColor(@ColorInt int color, boolean fade) {
+        if (!fade) return color;
+
+        return NotificationProgressDrawable.getFadedColor(color);
+    }
+
+    private static Map<Integer, ProgressStyle.Segment> generateStartToSegmentMap(
+            List<ProgressStyle.Segment> segments) {
+        final Map<Integer, ProgressStyle.Segment> startToSegmentMap = new HashMap<>();
+
+        int currentStart = 0;  // Initial start position is 0
+
+        for (ProgressStyle.Segment segment : segments) {
+            // Use the current start position as the key, and the segment as the value
+            startToSegmentMap.put(currentStart, segment);
+
+            // Update the start position for the next segment
+            currentStart += segment.getLength();
+        }
+
+        return startToSegmentMap;
+    }
+
+    private static Map<Integer, ProgressStyle.Point> generatePositionToPointMap(
+            List<ProgressStyle.Point> points) {
+        final Map<Integer, ProgressStyle.Point> positionToPointMap = new HashMap<>();
+
+        for (ProgressStyle.Point point : points) {
+            positionToPointMap.put(point.getPosition(), point);
+        }
+
+        return positionToPointMap;
+    }
+
+    private static SortedSet<Integer> generateSortedPositionSet(
+            Map<Integer, ProgressStyle.Segment> startToSegmentMap,
+            Map<Integer, ProgressStyle.Point> positionToPointMap, int progress,
+            boolean isStyledByProgress) {
+        final SortedSet<Integer> sortedPos = new TreeSet<>(startToSegmentMap.keySet());
+        sortedPos.addAll(positionToPointMap.keySet());
+        if (isStyledByProgress) {
+            sortedPos.add(progress);
+        }
+
+        return sortedPos;
+    }
+}
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 4d88546..2be7273 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -45,6 +45,8 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 
 /**
  * This is used by NotificationProgressBar for displaying a custom background. It composes of
@@ -126,7 +128,7 @@
      * @see #setStroke(int, int, float, float)
      */
     public void setStrokeDefaultColor(@ColorInt int color) {
-        mState.mStrokeColor = color;
+        mState.setStrokeColor(color);
     }
 
     /**
@@ -138,7 +140,7 @@
      * @see #mutate()
      */
     public void setPointRectDefaultColor(@ColorInt int color) {
-        mState.mPointRectColor = color;
+        mState.setPointRectColor(color);
     }
 
     private void setStrokeInternal(int width, float dashWidth, float dashGap) {
@@ -155,11 +157,18 @@
     }
 
     /**
-     *
+     * Set the segments and points that constitute the drawable.
+     */
+    public void setParts(List<Part> parts) {
+        mParts.clear();
+        mParts.addAll(parts);
+    }
+
+    /**
+     * Set the segments and points that constitute the drawable.
      */
     public void setParts(@NonNull Part... parts) {
-        mParts.clear();
-        mParts.addAll(Arrays.asList(parts));
+        setParts(Arrays.asList(parts));
     }
 
     @Override
@@ -194,7 +203,7 @@
                 mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
                         : mState.mStrokeColor);
                 mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
-                        : mState.mStrokeColor);
+                        : mState.mFadedStrokeColor);
 
                 // Leave space for the rounded line cap which extends beyond start/end.
                 final float capWidth = mStrokePaint.getStrokeWidth() / 2F;
@@ -220,7 +229,8 @@
                     mPointRectF.inset(inset, inset);
 
                     mFillPaint.setColor(point.mColor != Color.TRANSPARENT ? point.mColor
-                            : mState.mPointRectColor);
+                            : (point.mFaded ? mState.mFadedPointRectColor
+                                    : mState.mPointRectColor));
 
                     canvas.drawRoundRect(mPointRectF, cornerRadius, cornerRadius, mFillPaint);
                 }
@@ -377,7 +387,7 @@
         if (state.mThemeAttrsPoints != null) {
             final TypedArray a = t.resolveAttributes(
                     state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints);
-            updateSegmentsFromTypedArray(a);
+            updatePointsFromTypedArray(a);
             a.recycle();
         }
     }
@@ -424,8 +434,9 @@
         state.mPointRectCornerRadius = a.getDimension(
                 R.styleable.NotificationProgressDrawablePoints_cornerRadius,
                 state.mPointRectCornerRadius);
-        state.mPointRectColor = a.getColor(R.styleable.NotificationProgressDrawablePoints_color,
+        final int color = a.getColor(R.styleable.NotificationProgressDrawablePoints_color,
                 state.mPointRectColor);
+        setPointRectDefaultColor(color);
     }
 
     static int resolveDensity(@Nullable Resources r, int parentDensity) {
@@ -478,7 +489,6 @@
      * {@link Point} with zero length.
      */
     public interface Part {
-
     }
 
     /**
@@ -521,6 +531,24 @@
             return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", dashed="
                     + this.mDashed + ')';
         }
+
+        // Needed for unit tests
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) return true;
+
+            if (other == null || getClass() != other.getClass()) return false;
+
+            Segment that = (Segment) other;
+            if (Float.compare(this.mFraction, that.mFraction) != 0) return false;
+            if (this.mColor != that.mColor) return false;
+            return this.mDashed == that.mDashed;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFraction, mColor, mDashed);
+        }
     }
 
     /**
@@ -532,14 +560,21 @@
         @Nullable
         private final Drawable mIcon;
         @ColorInt private final int mColor;
+        private final boolean mFaded;
 
         public Point(@Nullable Drawable icon) {
-            this(icon, Color.TRANSPARENT);
+            this(icon, Color.TRANSPARENT, false);
         }
 
         public Point(@Nullable Drawable icon, @ColorInt int color) {
+            this(icon, color, false);
+
+        }
+
+        public Point(@Nullable Drawable icon, @ColorInt int color, boolean faded) {
             mIcon = icon;
             mColor = color;
+            mFaded = faded;
         }
 
         @Nullable
@@ -547,9 +582,37 @@
             return this.mIcon;
         }
 
+        public int getColor() {
+            return this.mColor;
+        }
+
+        public boolean getFaded() {
+            return this.mFaded;
+        }
+
         @Override
         public String toString() {
-            return "Point(icon=" + this.mIcon + ", color=" + this.mColor + ')';
+            return "Point(icon=" + this.mIcon + ", color=" + this.mColor + ", faded=" + this.mFaded
+                    + ")";
+        }
+
+        // Needed for unit tests.
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) return true;
+
+            if (other == null || getClass() != other.getClass()) return false;
+
+            Point that = (Point) other;
+
+            if (!Objects.equals(this.mIcon, that.mIcon)) return false;
+            if (this.mColor != that.mColor) return false;
+            return this.mFaded == that.mFaded;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mIcon, mColor, mFaded);
         }
     }
 
@@ -576,12 +639,14 @@
         float mSegPointGap = 0.0f;
         int mStrokeWidth = 0;
         int mStrokeColor;
+        int mFadedStrokeColor;
         float mStrokeDashWidth = 0.0f;
         float mStrokeDashGap = 0.0f;
         float mPointRadius;
         float mPointRectInset;
         float mPointRectCornerRadius;
         int mPointRectColor;
+        int mFadedPointRectColor;
 
         int[] mThemeAttrs;
         int[] mThemeAttrsSegments;
@@ -594,14 +659,18 @@
 
         State(@NonNull State orig, @Nullable Resources res) {
             mChangingConfigurations = orig.mChangingConfigurations;
-            mStrokeColor = orig.mStrokeColor;
+            mSegSegGap = orig.mSegSegGap;
+            mSegPointGap = orig.mSegPointGap;
             mStrokeWidth = orig.mStrokeWidth;
+            mStrokeColor = orig.mStrokeColor;
+            mFadedStrokeColor = orig.mFadedStrokeColor;
             mStrokeDashWidth = orig.mStrokeDashWidth;
             mStrokeDashGap = orig.mStrokeDashGap;
             mPointRadius = orig.mPointRadius;
             mPointRectInset = orig.mPointRectInset;
             mPointRectCornerRadius = orig.mPointRectCornerRadius;
             mPointRectColor = orig.mPointRectColor;
+            mFadedPointRectColor = orig.mFadedPointRectColor;
 
             mThemeAttrs = orig.mThemeAttrs;
             mThemeAttrsSegments = orig.mThemeAttrsSegments;
@@ -683,10 +752,30 @@
 
         public void setStroke(int width, int color, float dashWidth, float dashGap) {
             mStrokeWidth = width;
-            mStrokeColor = color;
             mStrokeDashWidth = dashWidth;
             mStrokeDashGap = dashGap;
+
+            setStrokeColor(color);
         }
+
+        public void setStrokeColor(int color) {
+            mStrokeColor = color;
+            mFadedStrokeColor = getFadedColor(color);
+        }
+
+        public void setPointRectColor(int color) {
+            mPointRectColor = color;
+            mFadedPointRectColor = getFadedColor(color);
+        }
+    }
+
+    /**
+     * Get a color with an opacity that's 50% of the input color.
+     */
+    @ColorInt
+    static int getFadedColor(@ColorInt int color) {
+        return Color.argb(Color.alpha(color) / 2, Color.red(color), Color.green(color),
+                Color.blue(color));
     }
 
     @Override
@@ -712,6 +801,7 @@
         final State state = mState;
 
         mStrokePaint.setStrokeWidth(state.mStrokeWidth);
+        mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth);
 
         if (state.mStrokeDashWidth != 0.0f) {
             final DashPathEffect e = new DashPathEffect(
diff --git a/core/java/com/android/internal/widget/NotificationProgressModel.java b/core/java/com/android/internal/widget/NotificationProgressModel.java
new file mode 100644
index 0000000..e51ea99
--- /dev/null
+++ b/core/java/com/android/internal/widget/NotificationProgressModel.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+
+import android.annotation.ColorInt;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.Flags;
+import android.app.Notification;
+import android.app.Notification.ProgressStyle.Point;
+import android.app.Notification.ProgressStyle.Segment;
+import android.graphics.Color;
+import android.os.Bundle;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Data model for {@link NotificationProgressBar}.
+ *
+ * This class holds the necessary data to render the notification progressbar.
+ * It is used to bind the progress style progress data to {@link NotificationProgressBar}.
+ *
+ * @hide
+ * @see NotificationProgressModel#toBundle
+ * @see NotificationProgressModel#fromBundle
+ */
+@FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+public final class NotificationProgressModel {
+    private static final int INVALID_INDETERMINATE_COLOR = Color.TRANSPARENT;
+    private static final String KEY_SEGMENTS = "segments";
+    private static final String KEY_POINTS = "points";
+    private static final String KEY_PROGRESS = "progress";
+    private static final String KEY_IS_STYLED_BY_PROGRESS = "isStyledByProgress";
+    private static final String KEY_INDETERMINATE_COLOR = "indeterminateColor";
+    private final List<Segment> mSegments;
+    private final List<Point> mPoints;
+    private final int mProgress;
+    private final boolean mIsStyledByProgress;
+    @ColorInt
+    private final int mIndeterminateColor;
+
+    public NotificationProgressModel(
+            @NonNull List<Segment> segments,
+            @NonNull List<Point> points,
+            int progress,
+            boolean isStyledByProgress
+    ) {
+        Preconditions.checkArgument(progress >= 0);
+        Preconditions.checkArgument(!segments.isEmpty());
+        mSegments = segments;
+        mPoints = points;
+        mProgress = progress;
+        mIsStyledByProgress = isStyledByProgress;
+        mIndeterminateColor = INVALID_INDETERMINATE_COLOR;
+    }
+
+    public NotificationProgressModel(
+            @ColorInt int indeterminateColor
+    ) {
+        Preconditions.checkArgument(indeterminateColor != INVALID_INDETERMINATE_COLOR);
+        mSegments = Collections.emptyList();
+        mPoints = Collections.emptyList();
+        mProgress = 0;
+        mIsStyledByProgress = false;
+        mIndeterminateColor = indeterminateColor;
+    }
+
+    public List<Segment> getSegments() {
+        return mSegments;
+    }
+
+    public List<Point> getPoints() {
+        return mPoints;
+    }
+
+    public int getProgress() {
+        return mProgress;
+    }
+
+    public boolean isStyledByProgress() {
+        return mIsStyledByProgress;
+    }
+
+    @ColorInt
+    public int getIndeterminateColor() {
+        return mIndeterminateColor;
+    }
+
+    public boolean isIndeterminate() {
+        return mIndeterminateColor != INVALID_INDETERMINATE_COLOR;
+    }
+
+    /**
+     * Returns a {@link Bundle} representation of this {@link NotificationProgressModel}.
+     */
+    @NonNull
+    public Bundle toBundle() {
+        final Bundle bundle = new Bundle();
+        if (mIndeterminateColor != INVALID_INDETERMINATE_COLOR) {
+            bundle.putInt(KEY_INDETERMINATE_COLOR, mIndeterminateColor);
+        } else {
+            bundle.putParcelableList(KEY_SEGMENTS,
+                    Notification.ProgressStyle.getProgressSegmentsAsBundleList(mSegments));
+            bundle.putParcelableList(KEY_POINTS,
+                    Notification.ProgressStyle.getProgressPointsAsBundleList(mPoints));
+            bundle.putInt(KEY_PROGRESS, mProgress);
+            bundle.putBoolean(KEY_IS_STYLED_BY_PROGRESS, mIsStyledByProgress);
+        }
+        return bundle;
+    }
+
+    /**
+     * Creates a {@link NotificationProgressModel} from a {@link Bundle}.
+     */
+    @NonNull
+    public static NotificationProgressModel fromBundle(@NonNull Bundle bundle) {
+        final int indeterminateColor = bundle.getInt(KEY_INDETERMINATE_COLOR,
+                INVALID_INDETERMINATE_COLOR);
+        if (indeterminateColor != INVALID_INDETERMINATE_COLOR) {
+            return new NotificationProgressModel(indeterminateColor);
+        } else {
+            final List<Segment> segments =
+                    Notification.ProgressStyle.getProgressSegmentsFromBundleList(
+                            bundle.getParcelableArrayList(KEY_SEGMENTS, Bundle.class));
+            final List<Point> points =
+                    Notification.ProgressStyle.getProgressPointsFromBundleList(
+                            bundle.getParcelableArrayList(KEY_POINTS, Bundle.class));
+            final int progress = bundle.getInt(KEY_PROGRESS);
+            final boolean isStyledByProgress = bundle.getBoolean(KEY_IS_STYLED_BY_PROGRESS);
+            return new NotificationProgressModel(segments, points, progress, isStyledByProgress);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "NotificationProgressModel{"
+                + "mSegments=" + mSegments
+                + ", mPoints=" + mPoints
+                + ", mProgress=" + mProgress
+                + ", mIsStyledByProgress=" + mIsStyledByProgress
+                + ", mIndeterminateColor=" + mIndeterminateColor + "}";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        final NotificationProgressModel that = (NotificationProgressModel) o;
+        return mProgress == that.mProgress
+                && mIsStyledByProgress == that.mIsStyledByProgress
+                && mIndeterminateColor == that.mIndeterminateColor
+                && Objects.equals(mSegments, that.mSegments)
+                && Objects.equals(mPoints, that.mPoints);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSegments,
+                mPoints,
+                mProgress,
+                mIsStyledByProgress,
+                mIndeterminateColor);
+    }
+}
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 98e6e85..adcc0f6 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -39,17 +39,24 @@
 
 /**
  * An image view that holds the icon displayed at the start of a notification row.
+ * This can generally either display the "small icon" of a notification set via
+ * {@link this#setImageIcon(Icon)}, or an app icon controlled and fetched by the provider set
+ * through {@link this#setIconProvider(NotificationIconProvider)}.
  */
 @RemoteViews.RemoteView
 public class NotificationRowIconView extends CachingIconView {
+    private NotificationIconProvider mIconProvider;
+
     private boolean mApplyCircularCrop = false;
     private boolean mShouldShowAppIcon = false;
+    private Drawable mAppIcon = null;
 
-    // Padding and background set on the view prior to being changed by setShouldShowAppIcon(true),
-    // to be restored if shouldShowAppIcon becomes false again.
+    // Padding, background and colors set on the view prior to being overridden when showing the app
+    // icon, to be restored if we're showing the small icon again.
     private Rect mOriginalPadding = null;
     private Drawable mOriginalBackground = null;
-
+    private int mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID;
+    private int mOriginalIconColor = ColoredIconHelper.COLOR_INVALID;
 
     public NotificationRowIconView(Context context) {
         super(context);
@@ -81,6 +88,71 @@
         super.onFinishInflate();
     }
 
+    /**
+     * Sets the icon provider for this view. This is used to determine whether we should show the
+     * app icon instead of the small icon, and to fetch the app icon if needed.
+     */
+    public void setIconProvider(NotificationIconProvider iconProvider) {
+        mIconProvider = iconProvider;
+    }
+
+    private Drawable loadAppIcon() {
+        if (mIconProvider != null && mIconProvider.shouldShowAppIcon()) {
+            return mIconProvider.getAppIcon();
+        }
+        return null;
+    }
+
+    @RemotableViewMethod(asyncImpl = "setImageIconAsync")
+    @Override
+    public void setImageIcon(Icon icon) {
+        if (Flags.notificationsRedesignAppIcons()) {
+            if (mAppIcon != null) {
+                // We already know that we should be using the app icon, and we already loaded it.
+                // We assume that cannot change throughout the lifetime of a notification, so
+                // there's nothing to do here.
+                return;
+            }
+            mAppIcon = loadAppIcon();
+            if (mAppIcon != null) {
+                setImageDrawable(mAppIcon);
+                adjustViewForAppIcon();
+            } else {
+                super.setImageIcon(icon);
+                restoreViewForSmallIcon();
+            }
+            return;
+        }
+        super.setImageIcon(icon);
+    }
+
+    @RemotableViewMethod
+    @Override
+    public Runnable setImageIconAsync(Icon icon) {
+        if (Flags.notificationsRedesignAppIcons()) {
+            if (mAppIcon != null) {
+                // We already know that we should be using the app icon, and we already loaded it.
+                // We assume that cannot change throughout the lifetime of a notification, so
+                // there's nothing to do here.
+                return () -> {
+                };
+            }
+            mAppIcon = loadAppIcon();
+            if (mAppIcon != null) {
+                return () -> {
+                    setImageDrawable(mAppIcon);
+                    adjustViewForAppIcon();
+                };
+            } else {
+                return () -> {
+                    super.setImageIcon(icon);
+                    restoreViewForSmallIcon();
+                };
+            }
+        }
+        return super.setImageIconAsync(icon);
+    }
+
     /** Whether the icon represents the app icon (instead of the small icon). */
     @RemotableViewMethod
     public void setShouldShowAppIcon(boolean shouldShowAppIcon) {
@@ -91,35 +163,122 @@
 
             mShouldShowAppIcon = shouldShowAppIcon;
             if (mShouldShowAppIcon) {
-                if (mOriginalPadding == null && mOriginalBackground == null) {
-                    mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(),
-                            getPaddingRight(), getPaddingBottom());
-                    mOriginalBackground = getBackground();
-                }
-
-                setPadding(0, 0, 0, 0);
-
-                // Make the background white in case the icon itself doesn't have one.
-                ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE,
-                        PorterDuff.Mode.SRC_ATOP);
-
-                if (mOriginalBackground == null) {
-                    setBackground(getContext().getDrawable(R.drawable.notification_icon_circle));
-                }
-                getBackground().mutate().setColorFilter(colorFilter);
+                adjustViewForAppIcon();
             } else {
                 // Restore original padding and background if needed
-                if (mOriginalPadding != null) {
-                    setPadding(mOriginalPadding.left, mOriginalPadding.top, mOriginalPadding.right,
-                            mOriginalPadding.bottom);
-                    mOriginalPadding = null;
-                }
-                setBackground(mOriginalBackground);
-                mOriginalBackground = null;
+                restoreViewForSmallIcon();
             }
         }
     }
 
+    /**
+     * Override padding and background from the view to display the app icon.
+     */
+    private void adjustViewForAppIcon() {
+        removePadding();
+
+        if (Flags.notificationsUseAppIconInRow()) {
+            addWhiteBackground();
+        } else {
+            // No need to set the background for notification redesign, since the icon
+            // factory already does that for us.
+            removeBackground();
+        }
+    }
+
+    /**
+     * Restore padding and background overridden by {@link this#adjustViewForAppIcon}.
+     * Does nothing if they were not overridden.
+     */
+    private void restoreViewForSmallIcon() {
+        restorePadding();
+        restoreBackground();
+        restoreColors();
+    }
+
+    private void removePadding() {
+        if (mOriginalPadding == null) {
+            mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(),
+                    getPaddingRight(), getPaddingBottom());
+        }
+        setPadding(0, 0, 0, 0);
+    }
+
+    private void restorePadding() {
+        if (mOriginalPadding != null) {
+            setPadding(mOriginalPadding.left, mOriginalPadding.top,
+                    mOriginalPadding.right,
+                    mOriginalPadding.bottom);
+            mOriginalPadding = null;
+        }
+    }
+
+    private void removeBackground() {
+        if (mOriginalBackground == null) {
+            mOriginalBackground = getBackground();
+        }
+
+        setBackground(null);
+    }
+
+    private void addWhiteBackground() {
+        if (mOriginalBackground == null) {
+            mOriginalBackground = getBackground();
+        }
+
+        // Make the background white in case the icon itself doesn't have one.
+        ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE,
+                PorterDuff.Mode.SRC_ATOP);
+
+        if (mOriginalBackground == null) {
+            setBackground(getContext().getDrawable(R.drawable.notification_icon_circle));
+        }
+        getBackground().mutate().setColorFilter(colorFilter);
+    }
+
+    private void restoreBackground() {
+        // NOTE: This will not work if the original background was null, but that's better than
+        //  accidentally clearing the background. We expect that there's generally going to be one
+        //  anyway unless we manually clear it.
+        if (mOriginalBackground != null) {
+            setBackground(mOriginalBackground);
+            mOriginalBackground = null;
+        }
+    }
+
+    private void restoreColors() {
+        if (mOriginalBackgroundColor != ColoredIconHelper.COLOR_INVALID) {
+            super.setBackgroundColor(mOriginalBackgroundColor);
+            mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID;
+        }
+        if (mOriginalIconColor != ColoredIconHelper.COLOR_INVALID) {
+            super.setOriginalIconColor(mOriginalIconColor);
+            mOriginalIconColor = ColoredIconHelper.COLOR_INVALID;
+        }
+    }
+
+    @RemotableViewMethod
+    @Override
+    public void setBackgroundColor(int color) {
+        // Ignore color overrides if we're showing the app icon.
+        if (mAppIcon == null) {
+            super.setBackgroundColor(color);
+        } else {
+            mOriginalBackgroundColor = color;
+        }
+    }
+
+    @RemotableViewMethod
+    @Override
+    public void setOriginalIconColor(int color) {
+        // Ignore color overrides if we're showing the app icon.
+        if (mAppIcon == null) {
+            super.setOriginalIconColor(color);
+        } else {
+            mOriginalIconColor = color;
+        }
+    }
+
     @Nullable
     @Override
     Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
@@ -197,4 +356,17 @@
 
         return bitmap;
     }
+
+    /**
+     * A provider that allows this view to verify whether it should use the app icon instead of the
+     * icon provided to it via setImageIcon, as well as actually fetching the app icon. It should
+     * primarily be called on the background thread.
+     */
+    public interface NotificationIconProvider {
+        /** Whether this notification should use the app icon instead of the small icon. */
+        boolean shouldShowAppIcon();
+
+        /** Get the app icon for this notification. */
+        Drawable getAppIcon();
+    }
 }
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index e65b4b6..46855c6 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -16,14 +16,19 @@
 
 package com.android.internal.widget;
 
+import static java.lang.Float.NaN;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
 import android.graphics.Path;
+import android.graphics.PorterDuff;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.hardware.input.InputManager;
@@ -40,6 +45,7 @@
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.RoundedCorner;
+import android.view.Surface;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -65,18 +71,21 @@
     private static final PointerState EMPTY_POINTER_STATE = new PointerState();
 
     public static class PointerState {
-        // Trace of previous points.
-        private float[] mTraceX = new float[32];
-        private float[] mTraceY = new float[32];
-        private boolean[] mTraceCurrent = new boolean[32];
-        private int mTraceCount;
+        private float mCurrentX = NaN;
+        private float mCurrentY = NaN;
+        private float mPreviousX = NaN;
+        private float mPreviousY = NaN;
+        private float mFirstX = NaN;
+        private float mFirstY = NaN;
+        private boolean mPreviousPointIsHistorical;
+        private boolean mCurrentPointIsHistorical;
 
         // True if the pointer is down.
         @UnsupportedAppUsage
         private boolean mCurDown;
 
         // Most recent coordinates.
-        private PointerCoords mCoords = new PointerCoords();
+        private final PointerCoords mCoords = new PointerCoords();
         private int mToolType;
 
         // Most recent velocity.
@@ -96,31 +105,20 @@
         public PointerState() {
         }
 
-        public void clearTrace() {
-            mTraceCount = 0;
-        }
-
-        public void addTrace(float x, float y, boolean current) {
-            int traceCapacity = mTraceX.length;
-            if (mTraceCount == traceCapacity) {
-                traceCapacity *= 2;
-                float[] newTraceX = new float[traceCapacity];
-                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
-                mTraceX = newTraceX;
-
-                float[] newTraceY = new float[traceCapacity];
-                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
-                mTraceY = newTraceY;
-
-                boolean[] newTraceCurrent = new boolean[traceCapacity];
-                System.arraycopy(mTraceCurrent, 0, newTraceCurrent, 0, mTraceCount);
-                mTraceCurrent= newTraceCurrent;
+        void addTrace(float x, float y, boolean isHistorical) {
+            if (Float.isNaN(mFirstX)) {
+                mFirstX = x;
+            }
+            if (Float.isNaN(mFirstY)) {
+                mFirstY = y;
             }
 
-            mTraceX[mTraceCount] = x;
-            mTraceY[mTraceCount] = y;
-            mTraceCurrent[mTraceCount] = current;
-            mTraceCount += 1;
+            mPreviousX = mCurrentX;
+            mPreviousY = mCurrentY;
+            mCurrentX = x;
+            mCurrentY = y;
+            mPreviousPointIsHistorical = mCurrentPointIsHistorical;
+            mCurrentPointIsHistorical = isHistorical;
         }
     }
 
@@ -149,6 +147,13 @@
     private final SparseArray<PointerState> mPointers = new SparseArray<PointerState>();
     private final PointerCoords mTempCoords = new PointerCoords();
 
+    // Draw the trace of all pointers in the current gesture in a separate layer
+    // that is not cleared on every frame so that we don't have to re-draw the
+    // entire trace on each frame. The trace bitmap is in the coordinate space of the unrotated
+    // display.
+    private Bitmap mTraceBitmap;
+    private final Canvas mTraceCanvas;
+
     private final Region mSystemGestureExclusion = new Region();
     private final Region mSystemGestureExclusionRejected = new Region();
     private final Path mSystemGestureExclusionPath = new Path();
@@ -197,6 +202,9 @@
         mPathPaint.setARGB(255, 0, 96, 255);
         mPathPaint.setStyle(Paint.Style.STROKE);
 
+        mTraceCanvas = new Canvas();
+        configureTraceBitmap();
+
         configureDensityDependentFactors();
 
         mSystemGestureExclusionPaint = new Paint();
@@ -256,7 +264,7 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         mTextPaint.getFontMetricsInt(mTextMetrics);
-        mHeaderBottom = mHeaderPaddingTop-mTextMetrics.ascent+mTextMetrics.descent+2;
+        mHeaderBottom = mHeaderPaddingTop - mTextMetrics.ascent + mTextMetrics.descent + 2;
         if (false) {
             Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
                     + " descent=" + mTextMetrics.descent
@@ -268,7 +276,8 @@
 
     // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
     // angles less than or greater than 0 radians rotate the major axis left or right.
-    private RectF mReusableOvalRect = new RectF();
+    private final RectF mReusableOvalRect = new RectF();
+
     private void drawOval(Canvas canvas, float x, float y, float major, float minor,
             float angle, Paint paint) {
         canvas.save(Canvas.MATRIX_SAVE_FLAG);
@@ -285,6 +294,12 @@
     protected void onDraw(Canvas canvas) {
         final int NP = mPointers.size();
 
+        // Pointer trace.
+        canvas.save();
+        rotateCanvasToUnrotatedDisplay(canvas);
+        canvas.drawBitmap(mTraceBitmap, 0, 0, null);
+        canvas.restore();
+
         if (!mSystemGestureExclusion.isEmpty()) {
             mSystemGestureExclusionPath.reset();
             mSystemGestureExclusion.getBoundaryPath(mSystemGestureExclusionPath);
@@ -300,35 +315,14 @@
         // Labels
         drawLabels(canvas);
 
-        // Pointer trace.
+        // Current pointer states.
+        canvas.save();
+        rotateCanvasToUnrotatedDisplay(canvas);
         for (int p = 0; p < NP; p++) {
             final PointerState ps = mPointers.valueAt(p);
+            float lastX = ps.mCurrentX, lastY = ps.mCurrentY;
 
-            // Draw path.
-            final int N = ps.mTraceCount;
-            float lastX = 0, lastY = 0;
-            boolean haveLast = false;
-            boolean drawn = false;
-            mPaint.setARGB(255, 128, 255, 255);
-            for (int i=0; i < N; i++) {
-                float x = ps.mTraceX[i];
-                float y = ps.mTraceY[i];
-                if (Float.isNaN(x) || Float.isNaN(y)) {
-                    haveLast = false;
-                    continue;
-                }
-                if (haveLast) {
-                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
-                    final Paint paint = ps.mTraceCurrent[i - 1] ? mCurrentPointPaint : mPaint;
-                    canvas.drawPoint(lastX, lastY, paint);
-                    drawn = true;
-                }
-                lastX = x;
-                lastY = y;
-                haveLast = true;
-            }
-
-            if (drawn) {
+            if (!Float.isNaN(lastX) && !Float.isNaN(lastY)) {
                 // Draw velocity vector.
                 mPaint.setARGB(255, 255, 64, 128);
                 float xVel = ps.mXVelocity * (1000 / 60);
@@ -353,7 +347,7 @@
                         Math.max(getHeight(), getWidth()), mTargetPaint);
 
                 // Draw current point.
-                int pressureLevel = (int)(ps.mCoords.pressure * 255);
+                int pressureLevel = (int) (ps.mCoords.pressure * 255);
                 mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
                 canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
 
@@ -406,6 +400,7 @@
                 }
             }
         }
+        canvas.restore();
     }
 
     private void drawLabels(Canvas canvas) {
@@ -424,8 +419,7 @@
                 .append(" / ").append(mMaxNumPointers)
                 .toString(), 1, base, mTextPaint);
 
-        final int count = ps.mTraceCount;
-        if ((mCurDown && ps.mCurDown) || count == 0) {
+        if ((mCurDown && ps.mCurDown) || Float.isNaN(ps.mCurrentX)) {
             canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
                     mTextBackgroundPaint);
             canvas.drawText(mText.clear()
@@ -437,8 +431,8 @@
                     .append("Y: ").append(ps.mCoords.y, 1)
                     .toString(), 1 + itemW * 2, base, mTextPaint);
         } else {
-            float dx = ps.mTraceX[count - 1] - ps.mTraceX[0];
-            float dy = ps.mTraceY[count - 1] - ps.mTraceY[0];
+            float dx = ps.mCurrentX - ps.mFirstX;
+            float dy = ps.mCurrentY - ps.mFirstY;
             canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
                     Math.abs(dx) < mVC.getScaledTouchSlop()
                             ? mTextBackgroundPaint : mTextLevelPaint);
@@ -565,9 +559,9 @@
                 .append(" TouchMinor=").append(coords.touchMinor, 3)
                 .append(" ToolMajor=").append(coords.toolMajor, 3)
                 .append(" ToolMinor=").append(coords.toolMinor, 3)
-                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
+                .append(" Orientation=").append((float) (coords.orientation * 180 / Math.PI), 1)
                 .append("deg")
-                .append(" Tilt=").append((float)(
+                .append(" Tilt=").append((float) (
                         coords.getAxisValue(MotionEvent.AXIS_TILT) * 180 / Math.PI), 1)
                 .append("deg")
                 .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
@@ -586,6 +580,11 @@
 
     @Override
     public void onPointerEvent(MotionEvent event) {
+        // PointerLocationView stores and draws events in the unrotated display space, so undo the
+        // event's rotation to bring it back to the unrotated display space.
+        event.transform(MotionEvent.createRotateMatrix(inverseRotation(event.getSurfaceRotation()),
+                mTraceBitmap.getWidth(), mTraceBitmap.getHeight()));
+
         final int action = event.getAction();
 
         if (action == MotionEvent.ACTION_DOWN
@@ -598,6 +597,7 @@
                 mCurNumPointers = 0;
                 mMaxNumPointers = 0;
                 mVelocity.clear();
+                mTraceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                 if (mAltVelocity != null) {
                     mAltVelocity.clear();
                 }
@@ -646,7 +646,8 @@
                     logCoords("Pointer", action, i, coords, id, event);
                 }
                 if (ps != null) {
-                    ps.addTrace(coords.x, coords.y, false);
+                    ps.addTrace(coords.x, coords.y, /*isHistorical*/ true);
+                    updateDrawTrace(ps);
                 }
             }
         }
@@ -659,7 +660,8 @@
                 logCoords("Pointer", action, i, coords, id, event);
             }
             if (ps != null) {
-                ps.addTrace(coords.x, coords.y, true);
+                ps.addTrace(coords.x, coords.y, /*isHistorical*/ false);
+                updateDrawTrace(ps);
                 ps.mXVelocity = mVelocity.getXVelocity(id);
                 ps.mYVelocity = mVelocity.getYVelocity(id);
                 if (mAltVelocity != null) {
@@ -702,13 +704,27 @@
                 if (mActivePointerId == id) {
                     mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
                 }
-                ps.addTrace(Float.NaN, Float.NaN, false);
+                ps.addTrace(Float.NaN, Float.NaN, true);
             }
         }
 
         invalidate();
     }
 
+    private void updateDrawTrace(PointerState ps) {
+        mPaint.setARGB(255, 128, 255, 255);
+        float x = ps.mCurrentX;
+        float y = ps.mCurrentY;
+        float lastX = ps.mPreviousX;
+        float lastY = ps.mPreviousY;
+        if (Float.isNaN(x) || Float.isNaN(y) || Float.isNaN(lastX) || Float.isNaN(lastY)) {
+            return;
+        }
+        mTraceCanvas.drawLine(lastX, lastY, x, y, mPathPaint);
+        Paint paint = ps.mPreviousPointIsHistorical ? mPaint : mCurrentPointPaint;
+        mTraceCanvas.drawPoint(lastX, lastY, paint);
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         onPointerEvent(event);
@@ -767,7 +783,7 @@
                 return true;
             default:
                 return KeyEvent.isGamepadButton(keyCode)
-                    || KeyEvent.isModifierKey(keyCode);
+                        || KeyEvent.isModifierKey(keyCode);
         }
     }
 
@@ -887,7 +903,7 @@
         public FasterStringBuilder append(int value, int zeroPadWidth) {
             final boolean negative = value < 0;
             if (negative) {
-                value = - value;
+                value = -value;
                 if (value < 0) {
                     append("-2147483648");
                     return this;
@@ -971,32 +987,34 @@
         }
     }
 
-    private ISystemGestureExclusionListener mSystemGestureExclusionListener =
+    private final ISystemGestureExclusionListener mSystemGestureExclusionListener =
             new ISystemGestureExclusionListener.Stub() {
-        @Override
-        public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion,
-                Region systemGestureExclusionUnrestricted) {
-            Region exclusion = Region.obtain(systemGestureExclusion);
-            Region rejected = Region.obtain();
-            if (systemGestureExclusionUnrestricted != null) {
-                rejected.set(systemGestureExclusionUnrestricted);
-                rejected.op(exclusion, Region.Op.DIFFERENCE);
-            }
-            Handler handler = getHandler();
-            if (handler != null) {
-                handler.post(() -> {
-                    mSystemGestureExclusion.set(exclusion);
-                    mSystemGestureExclusionRejected.set(rejected);
-                    exclusion.recycle();
-                    invalidate();
-                });
-            }
-        }
-    };
+                @Override
+                public void onSystemGestureExclusionChanged(int displayId,
+                        Region systemGestureExclusion,
+                        Region systemGestureExclusionUnrestricted) {
+                    Region exclusion = Region.obtain(systemGestureExclusion);
+                    Region rejected = Region.obtain();
+                    if (systemGestureExclusionUnrestricted != null) {
+                        rejected.set(systemGestureExclusionUnrestricted);
+                        rejected.op(exclusion, Region.Op.DIFFERENCE);
+                    }
+                    Handler handler = getHandler();
+                    if (handler != null) {
+                        handler.post(() -> {
+                            mSystemGestureExclusion.set(exclusion);
+                            mSystemGestureExclusionRejected.set(rejected);
+                            exclusion.recycle();
+                            invalidate();
+                        });
+                    }
+                }
+            };
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        configureTraceBitmap();
         configureDensityDependentFactors();
     }
 
@@ -1008,4 +1026,58 @@
         mCurrentPointPaint.setStrokeWidth(1 * mDensity);
         mPathPaint.setStrokeWidth(1 * mDensity);
     }
+
+    private void configureTraceBitmap() {
+        final var display = mContext.getDisplay();
+        final boolean rotated = display.getRotation() == Surface.ROTATION_90
+                || display.getRotation() == Surface.ROTATION_270;
+        int unrotatedWidth = rotated ? display.getHeight() : display.getWidth();
+        int unrotatedHeight = rotated ? display.getWidth() : display.getHeight();
+
+        if (mTraceBitmap != null && mTraceBitmap.getWidth() == unrotatedWidth
+                && mTraceBitmap.getHeight() == unrotatedHeight) {
+            return;
+        }
+        if (unrotatedWidth <= 0 || unrotatedHeight <= 0) {
+            Slog.w(TAG, "Ignoring configuration: invalid display size: " + unrotatedWidth + "x"
+                    + unrotatedHeight);
+            // Initialize the bitmap to an arbitrary size. It should be reconfigured with a valid
+            // size in the future.
+            unrotatedWidth = 100;
+            unrotatedHeight = 100;
+        }
+        mTraceBitmap = Bitmap.createBitmap(unrotatedWidth, unrotatedHeight,
+                Bitmap.Config.ARGB_8888);
+        mTraceCanvas.setBitmap(mTraceBitmap);
+    }
+
+    private static int inverseRotation(@Surface.Rotation int rotation) {
+        return switch(rotation) {
+            case Surface.ROTATION_0 -> Surface.ROTATION_0;
+            case Surface.ROTATION_90 -> Surface.ROTATION_270;
+            case Surface.ROTATION_180 -> Surface.ROTATION_180;
+            case Surface.ROTATION_270 -> Surface.ROTATION_90;
+            default -> {
+                Slog.e(TAG, "Received unexpected surface rotation: " + rotation);
+                yield Surface.ROTATION_0;
+            }
+        };
+    }
+
+    private void rotateCanvasToUnrotatedDisplay(Canvas c) {
+        switch (inverseRotation(mContext.getDisplay().getRotation())) {
+            case Surface.ROTATION_90 -> {
+                c.rotate(90);
+                c.translate(0, -mTraceBitmap.getHeight());
+            }
+            case Surface.ROTATION_180 -> {
+                c.rotate(180);
+                c.translate(-mTraceBitmap.getWidth(), -mTraceBitmap.getHeight());
+            }
+            case Surface.ROTATION_270 -> {
+                c.rotate(270);
+                c.translate(-mTraceBitmap.getWidth(), 0);
+            }
+        }
+    }
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2bc3265..816ace2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -73,12 +73,6 @@
 
     srcs: [
         "android_animation_PropertyValuesHolder.cpp",
-        "android_database_CursorWindow.cpp",
-        "android_database_SQLiteCommon.cpp",
-        "android_database_SQLiteConnection.cpp",
-        "android_database_SQLiteGlobal.cpp",
-        "android_database_SQLiteDebug.cpp",
-        "android_database_SQLiteRawStatement.cpp",
         "android_os_SystemClock.cpp",
         "android_os_SystemProperties.cpp",
         "android_os_Trace.cpp",
@@ -163,6 +157,12 @@
                 "android_opengl_GLES31.cpp",
                 "android_opengl_GLES31Ext.cpp",
                 "android_opengl_GLES32.cpp",
+                "android_database_CursorWindow.cpp",
+                "android_database_SQLiteCommon.cpp",
+                "android_database_SQLiteConnection.cpp",
+                "android_database_SQLiteGlobal.cpp",
+                "android_database_SQLiteDebug.cpp",
+                "android_database_SQLiteRawStatement.cpp",
                 "android_graphics_GraphicBuffer.cpp",
                 "android_graphics_SurfaceTexture.cpp",
                 "android_view_CompositionSamplingListener.cpp",
@@ -428,7 +428,6 @@
                 "libnativehelper_jvm",
                 "libpiex",
                 "libpng",
-                "libsqlite",
                 "libtiff_directory",
                 "libui-types",
                 "libutils",
@@ -444,6 +443,12 @@
         host_linux: {
             srcs: [
                 "android_content_res_ApkAssets.cpp",
+                "android_database_CursorWindow.cpp",
+                "android_database_SQLiteCommon.cpp",
+                "android_database_SQLiteConnection.cpp",
+                "android_database_SQLiteGlobal.cpp",
+                "android_database_SQLiteDebug.cpp",
+                "android_database_SQLiteRawStatement.cpp",
                 "android_hardware_input_InputApplicationHandle.cpp",
                 "android_os_MessageQueue.cpp",
                 "android_os_Parcel.cpp",
@@ -459,6 +464,7 @@
             ],
             static_libs: [
                 "libbinderthreadstateutils",
+                "libsqlite",
                 "libgui_window_info_static",
             ],
             shared_libs: [
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 18c3146..c0e9215 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -38,9 +38,7 @@
 #define LOG_NDEBUG 1
 
 #include <androidfw/CursorWindow.h>
-#ifdef __linux__
 #include "android_os_Parcel.h"
-#endif
 #include "android_util_Binder.h"
 #include "android_database_SQLiteCommon.h"
 
@@ -113,7 +111,6 @@
     return 0;
 }
 
-#ifdef __linux__
 static jlong nativeCreateFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
 
@@ -131,7 +128,6 @@
             window->getNumRows(), window->getNumColumns(), window);
     return reinterpret_cast<jlong>(window);
 }
-#endif
 
 static void nativeDispose(JNIEnv* env, jclass clazz, jlong windowPtr) {
     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
@@ -146,7 +142,6 @@
     return env->NewStringUTF(window->name().c_str());
 }
 
-#ifdef __linux__
 static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr,
         jobject parcelObj) {
     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
@@ -159,7 +154,6 @@
         jniThrowRuntimeException(env, msg.c_str());
     }
 }
-#endif
 
 static void nativeClear(JNIEnv * env, jclass clazz, jlong windowPtr) {
     CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
@@ -526,35 +520,55 @@
     return true;
 }
 
-static const JNINativeMethod sMethods[] = {
-        /* name, signature, funcPtr */
-        {"nativeCreate", "(Ljava/lang/String;I)J", (void*)nativeCreate},
-        {"nativeDispose", "(J)V", (void*)nativeDispose},
-#ifdef __linux__
-        {"nativeCreateFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeCreateFromParcel},
-        {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
-#endif
-        {"nativeGetName", "(J)Ljava/lang/String;", (void*)nativeGetName},
-        {"nativeGetBlob", "(JII)[B", (void*)nativeGetBlob},
-        {"nativeGetString", "(JII)Ljava/lang/String;", (void*)nativeGetString},
-        {"nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V",
-         (void*)nativeCopyStringToBuffer},
-        {"nativePutBlob", "(J[BII)Z", (void*)nativePutBlob},
-        {"nativePutString", "(JLjava/lang/String;II)Z", (void*)nativePutString},
+static const JNINativeMethod sMethods[] =
+{
+    /* name, signature, funcPtr */
+    { "nativeCreate", "(Ljava/lang/String;I)J",
+            (void*)nativeCreate },
+    { "nativeCreateFromParcel", "(Landroid/os/Parcel;)J",
+            (void*)nativeCreateFromParcel },
+    { "nativeDispose", "(J)V",
+            (void*)nativeDispose },
+    { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
+            (void*)nativeWriteToParcel },
 
-        // ------- @FastNative below here ----------------------
-        {"nativeClear", "(J)V", (void*)nativeClear},
-        {"nativeGetNumRows", "(J)I", (void*)nativeGetNumRows},
-        {"nativeSetNumColumns", "(JI)Z", (void*)nativeSetNumColumns},
-        {"nativeAllocRow", "(J)Z", (void*)nativeAllocRow},
-        {"nativeFreeLastRow", "(J)V", (void*)nativeFreeLastRow},
-        {"nativeGetType", "(JII)I", (void*)nativeGetType},
-        {"nativeGetLong", "(JII)J", (void*)nativeGetLong},
-        {"nativeGetDouble", "(JII)D", (void*)nativeGetDouble},
+    { "nativeGetName", "(J)Ljava/lang/String;",
+            (void*)nativeGetName },
+    { "nativeGetBlob", "(JII)[B",
+            (void*)nativeGetBlob },
+    { "nativeGetString", "(JII)Ljava/lang/String;",
+            (void*)nativeGetString },
+    { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V",
+            (void*)nativeCopyStringToBuffer },
+    { "nativePutBlob", "(J[BII)Z",
+            (void*)nativePutBlob },
+    { "nativePutString", "(JLjava/lang/String;II)Z",
+            (void*)nativePutString },
 
-        {"nativePutLong", "(JJII)Z", (void*)nativePutLong},
-        {"nativePutDouble", "(JDII)Z", (void*)nativePutDouble},
-        {"nativePutNull", "(JII)Z", (void*)nativePutNull},
+    // ------- @FastNative below here ----------------------
+    { "nativeClear", "(J)V",
+            (void*)nativeClear },
+    { "nativeGetNumRows", "(J)I",
+            (void*)nativeGetNumRows },
+    { "nativeSetNumColumns", "(JI)Z",
+            (void*)nativeSetNumColumns },
+    { "nativeAllocRow", "(J)Z",
+            (void*)nativeAllocRow },
+    { "nativeFreeLastRow", "(J)V",
+            (void*)nativeFreeLastRow },
+    { "nativeGetType", "(JII)I",
+            (void*)nativeGetType },
+    { "nativeGetLong", "(JII)J",
+            (void*)nativeGetLong },
+    { "nativeGetDouble", "(JII)D",
+            (void*)nativeGetDouble },
+
+    { "nativePutLong", "(JJII)Z",
+            (void*)nativePutLong },
+    { "nativePutDouble", "(JDII)Z",
+            (void*)nativePutDouble },
+    { "nativePutNull", "(JII)Z",
+            (void*)nativePutNull },
 };
 
 int register_android_database_CursorWindow(JNIEnv* env)
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 041fed7..a098737 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -336,7 +336,7 @@
 static jbyteArray CameraMetadata_readValues(JNIEnv *env, jclass thiz, jint tag, jlong ptr) {
     ALOGV("%s (tag = %d)", __FUNCTION__, tag);
 
-    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, ptr);
+    const CameraMetadata *metadata = CameraMetadata_getPointerThrow(env, ptr);
     if (metadata == NULL) return NULL;
 
     const camera_metadata_t *metaBuffer = metadata->getAndLock();
@@ -349,16 +349,15 @@
     }
     size_t tagSize = Helpers::getTypeSize(tagType);
 
-    camera_metadata_entry entry = metadata->find(tag);
+    camera_metadata_ro_entry entry = metadata->find(tag);
     if (entry.count == 0) {
-         if (!metadata->exists(tag)) {
-             ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
-             return NULL;
-         } else {
-             // OK: we will return a 0-sized array.
-             ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__,
-                   tag);
-         }
+        if (!metadata->exists(tag)) {
+            ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
+            return NULL;
+        } else {
+            // OK: we will return a 0-sized array.
+            ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__, tag);
+        }
     }
 
     jsize byteCount = entry.count * tagSize;
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 9bccf5a..8eaa7aa 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -747,16 +747,12 @@
                                           indexMax));
 }
 
-static jint
-android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
-                                               jobject thiz,
-                                               jint stream,
-                                               jint index,
-                                               jint device)
-{
+static jint android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream,
+                                                           jint index, jboolean muted,
+                                                           jint device) {
     return check_AudioSystem_Command(
             AudioSystem::setStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), index,
-                                              static_cast<audio_devices_t>(device)));
+                                              muted, static_cast<audio_devices_t>(device)));
 }
 
 static jint
@@ -773,13 +769,9 @@
     return index;
 }
 
-static jint
-android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env,
-                                                      jobject thiz,
-                                                      jobject jaa,
-                                                      jint index,
-                                                      jint device)
-{
+static jint android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, jobject thiz,
+                                                                  jobject jaa, jint index,
+                                                                  jboolean muted, jint device) {
     // read the AudioAttributes values
     JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
     jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
@@ -787,7 +779,7 @@
         return jStatus;
     }
     return check_AudioSystem_Command(
-            AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index,
+            AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, muted,
                                                      static_cast<audio_devices_t>(device)));
 }
 
@@ -3448,182 +3440,179 @@
 #define MAKE_AUDIO_SYSTEM_METHOD(x) \
     MAKE_JNI_NATIVE_METHOD_AUTOSIG(#x, android_media_AudioSystem_##x)
 
-static const JNINativeMethod gMethods[] =
-        {MAKE_AUDIO_SYSTEM_METHOD(setParameters),
-         MAKE_AUDIO_SYSTEM_METHOD(getParameters),
-         MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone),
-         MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted),
-         MAKE_AUDIO_SYSTEM_METHOD(isStreamActive),
-         MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely),
-         MAKE_AUDIO_SYSTEM_METHOD(isSourceActive),
-         MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId),
-         MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId),
-         MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId),
-         MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I",
-                                android_media_AudioSystem_setDeviceConnectionState),
-         MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState),
-         MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange),
-         MAKE_AUDIO_SYSTEM_METHOD(setPhoneState),
-         MAKE_AUDIO_SYSTEM_METHOD(setForceUse),
-         MAKE_AUDIO_SYSTEM_METHOD(getForceUse),
-         MAKE_AUDIO_SYSTEM_METHOD(setDeviceAbsoluteVolumeEnabled),
-         MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume),
-         MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex),
-         MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex),
-         MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes",
-                                "(Landroid/media/AudioAttributes;II)I",
-                                android_media_AudioSystem_setVolumeIndexForAttributes),
-         MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes",
-                                "(Landroid/media/AudioAttributes;I)I",
-                                android_media_AudioSystem_getVolumeIndexForAttributes),
-         MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes",
-                                "(Landroid/media/AudioAttributes;)I",
-                                android_media_AudioSystem_getMinVolumeIndexForAttributes),
-         MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes",
-                                "(Landroid/media/AudioAttributes;)I",
-                                android_media_AudioSystem_getMaxVolumeIndexForAttributes),
-         MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume),
-         MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume),
-         MAKE_AUDIO_SYSTEM_METHOD(setMasterMute),
-         MAKE_AUDIO_SYSTEM_METHOD(getMasterMute),
-         MAKE_AUDIO_SYSTEM_METHOD(setMasterMono),
-         MAKE_AUDIO_SYSTEM_METHOD(getMasterMono),
-         MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance),
-         MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance),
-         MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate),
-         MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount),
-         MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency),
-         MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice),
-         MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger),
-         MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V",
-                                android_media_AudioSystem_setAudioFlingerBinder),
-         MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I",
-                                android_media_AudioSystem_listAudioPorts),
-         MAKE_JNI_NATIVE_METHOD("getSupportedDeviceTypes", "(ILandroid/util/IntArray;)I",
-                                android_media_AudioSystem_getSupportedDeviceTypes),
-         MAKE_JNI_NATIVE_METHOD("createAudioPatch",
-                                "([Landroid/media/AudioPatch;[Landroid/media/"
-                                "AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
-                                android_media_AudioSystem_createAudioPatch),
-         MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
-                                android_media_AudioSystem_releaseAudioPatch),
-         MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I",
-                                android_media_AudioSystem_listAudioPatches),
-         MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
-                                android_media_AudioSystem_setAudioPortConfig),
-         MAKE_JNI_NATIVE_METHOD("startAudioSource",
-                                "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
-                                android_media_AudioSystem_startAudioSource),
-         MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource),
-         MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession),
-         MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
-                                android_media_AudioSystem_registerPolicyMixes),
-         MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I",
-                                android_media_AudioSystem_getRegisteredPolicyMixes),
-         MAKE_JNI_NATIVE_METHOD("updatePolicyMixes",
-                                "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/"
-                                "AudioMixingRule;)I",
-                                android_media_AudioSystem_updatePolicyMixes),
-         MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
-                                android_media_AudioSystem_setUidDeviceAffinities),
-         MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities),
-         MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback",
-                                        android_media_AudioSystem_registerDynPolicyCallback),
-         MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback",
-                                        android_media_AudioSystem_registerRecordingCallback),
-         MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback",
-                                        android_media_AudioSystem_registerRoutingCallback),
-         MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback",
-                                        android_media_AudioSystem_registerVolRangeInitReqCallback),
-         MAKE_AUDIO_SYSTEM_METHOD(systemReady),
-         MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB),
-         MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support",
-                                        android_media_AudioSystem_getOffloadSupport),
-         MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I",
-                                android_media_AudioSystem_getMicrophones),
-         MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I",
-                                android_media_AudioSystem_getSurroundFormats),
-         MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I",
-                                android_media_AudioSystem_getReportedSurroundFormats),
-         MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled),
-         MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids),
-         MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids),
-         MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids),
-         MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported),
-         MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported),
-         MAKE_JNI_NATIVE_METHOD(
-                 "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I",
-                 android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia),
-         MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages),
-         MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy),
-         MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled),
-         MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids),
-         MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported),
-         MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
-                                android_media_AudioSystem_setDevicesRoleForStrategy),
-         MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
-                                android_media_AudioSystem_removeDevicesRoleForStrategy),
-         MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy),
-         MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
-                                android_media_AudioSystem_getDevicesForRoleAndStrategy),
-         MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
-                                android_media_AudioSystem_setDevicesRoleForCapturePreset),
-         MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
-                                android_media_AudioSystem_addDevicesRoleForCapturePreset),
-         MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
-                                android_media_AudioSystem_removeDevicesRoleForCapturePreset),
-         MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset),
-         MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
-                                android_media_AudioSystem_getDevicesForRoleAndCapturePreset),
-         MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes",
-                                "(Landroid/media/AudioAttributes;[Landroid/media/"
-                                "AudioDeviceAttributes;Z)I",
-                                android_media_AudioSystem_getDevicesForAttributes),
-         MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
-                                android_media_AudioSystem_setUserIdDeviceAffinities),
-         MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities),
-         MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid),
-         MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I",
-                                android_media_AudioSystem_setVibratorInfos),
-         MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer",
-                                "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;",
-                                android_media_AudioSystem_getSpatializer),
-         MAKE_JNI_NATIVE_METHOD("canBeSpatialized",
-                                "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
-                                "[Landroid/media/AudioDeviceAttributes;)Z",
-                                android_media_AudioSystem_canBeSpatialized),
-         MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose",
-                                "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;",
-                                android_media_AudioSystem_nativeGetSoundDose),
-         MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport",
-                                "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I",
-                                android_media_AudioSystem_getDirectPlaybackSupport),
-         MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes",
-                                "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I",
-                                android_media_AudioSystem_getDirectProfilesForAttributes),
-         MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I",
-                                android_media_AudioSystem_getSupportedMixerAttributes),
-         MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes",
-                                "(Landroid/media/AudioAttributes;IILandroid/media/"
-                                "AudioMixerAttributes;)I",
-                                android_media_AudioSystem_setPreferredMixerAttributes),
-         MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes",
-                                "(Landroid/media/AudioAttributes;ILjava/util/List;)I",
-                                android_media_AudioSystem_getPreferredMixerAttributes),
-         MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes",
-                                "(Landroid/media/AudioAttributes;II)I",
-                                android_media_AudioSystem_clearPreferredMixerAttributes),
-         MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency),
-         MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
-         MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
-         MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
-                                "(Ljava/lang/String;Ljava/lang/Runnable;)J",
-                                android_media_AudioSystem_listenForSystemPropertyChange),
-         MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate",
-                                "(J)V",
-                                android_media_AudioSystem_triggerSystemPropertyUpdate),
-
-        };
+static const JNINativeMethod gMethods[] = {
+        MAKE_AUDIO_SYSTEM_METHOD(setParameters),
+        MAKE_AUDIO_SYSTEM_METHOD(getParameters),
+        MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone),
+        MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted),
+        MAKE_AUDIO_SYSTEM_METHOD(isStreamActive),
+        MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely),
+        MAKE_AUDIO_SYSTEM_METHOD(isSourceActive),
+        MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId),
+        MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId),
+        MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId),
+        MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I",
+                               android_media_AudioSystem_setDeviceConnectionState),
+        MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState),
+        MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange),
+        MAKE_AUDIO_SYSTEM_METHOD(setPhoneState),
+        MAKE_AUDIO_SYSTEM_METHOD(setForceUse),
+        MAKE_AUDIO_SYSTEM_METHOD(getForceUse),
+        MAKE_AUDIO_SYSTEM_METHOD(setDeviceAbsoluteVolumeEnabled),
+        MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume),
+        MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex),
+        MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex),
+        MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes",
+                               "(Landroid/media/AudioAttributes;IZI)I",
+                               android_media_AudioSystem_setVolumeIndexForAttributes),
+        MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I",
+                               android_media_AudioSystem_getVolumeIndexForAttributes),
+        MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes",
+                               "(Landroid/media/AudioAttributes;)I",
+                               android_media_AudioSystem_getMinVolumeIndexForAttributes),
+        MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes",
+                               "(Landroid/media/AudioAttributes;)I",
+                               android_media_AudioSystem_getMaxVolumeIndexForAttributes),
+        MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume),
+        MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume),
+        MAKE_AUDIO_SYSTEM_METHOD(setMasterMute),
+        MAKE_AUDIO_SYSTEM_METHOD(getMasterMute),
+        MAKE_AUDIO_SYSTEM_METHOD(setMasterMono),
+        MAKE_AUDIO_SYSTEM_METHOD(getMasterMono),
+        MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance),
+        MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance),
+        MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate),
+        MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount),
+        MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency),
+        MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice),
+        MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger),
+        MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V",
+                               android_media_AudioSystem_setAudioFlingerBinder),
+        MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I",
+                               android_media_AudioSystem_listAudioPorts),
+        MAKE_JNI_NATIVE_METHOD("getSupportedDeviceTypes", "(ILandroid/util/IntArray;)I",
+                               android_media_AudioSystem_getSupportedDeviceTypes),
+        MAKE_JNI_NATIVE_METHOD("createAudioPatch",
+                               "([Landroid/media/AudioPatch;[Landroid/media/"
+                               "AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
+                               android_media_AudioSystem_createAudioPatch),
+        MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
+                               android_media_AudioSystem_releaseAudioPatch),
+        MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I",
+                               android_media_AudioSystem_listAudioPatches),
+        MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
+                               android_media_AudioSystem_setAudioPortConfig),
+        MAKE_JNI_NATIVE_METHOD("startAudioSource",
+                               "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
+                               android_media_AudioSystem_startAudioSource),
+        MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource),
+        MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession),
+        MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
+                               android_media_AudioSystem_registerPolicyMixes),
+        MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I",
+                               android_media_AudioSystem_getRegisteredPolicyMixes),
+        MAKE_JNI_NATIVE_METHOD("updatePolicyMixes",
+                               "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/"
+                               "AudioMixingRule;)I",
+                               android_media_AudioSystem_updatePolicyMixes),
+        MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+                               android_media_AudioSystem_setUidDeviceAffinities),
+        MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities),
+        MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback",
+                                       android_media_AudioSystem_registerDynPolicyCallback),
+        MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback",
+                                       android_media_AudioSystem_registerRecordingCallback),
+        MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback",
+                                       android_media_AudioSystem_registerRoutingCallback),
+        MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback",
+                                       android_media_AudioSystem_registerVolRangeInitReqCallback),
+        MAKE_AUDIO_SYSTEM_METHOD(systemReady),
+        MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB),
+        MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support",
+                                       android_media_AudioSystem_getOffloadSupport),
+        MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I",
+                               android_media_AudioSystem_getMicrophones),
+        MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I",
+                               android_media_AudioSystem_getSurroundFormats),
+        MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I",
+                               android_media_AudioSystem_getReportedSurroundFormats),
+        MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled),
+        MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids),
+        MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids),
+        MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids),
+        MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported),
+        MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported),
+        MAKE_JNI_NATIVE_METHOD(
+                "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I",
+                android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia),
+        MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages),
+        MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy),
+        MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled),
+        MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids),
+        MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported),
+        MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+                               android_media_AudioSystem_setDevicesRoleForStrategy),
+        MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+                               android_media_AudioSystem_removeDevicesRoleForStrategy),
+        MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy),
+        MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
+                               android_media_AudioSystem_getDevicesForRoleAndStrategy),
+        MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+                               android_media_AudioSystem_setDevicesRoleForCapturePreset),
+        MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+                               android_media_AudioSystem_addDevicesRoleForCapturePreset),
+        MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+                               android_media_AudioSystem_removeDevicesRoleForCapturePreset),
+        MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset),
+        MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
+                               android_media_AudioSystem_getDevicesForRoleAndCapturePreset),
+        MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes",
+                               "(Landroid/media/AudioAttributes;[Landroid/media/"
+                               "AudioDeviceAttributes;Z)I",
+                               android_media_AudioSystem_getDevicesForAttributes),
+        MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+                               android_media_AudioSystem_setUserIdDeviceAffinities),
+        MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities),
+        MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid),
+        MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I",
+                               android_media_AudioSystem_setVibratorInfos),
+        MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer",
+                               "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;",
+                               android_media_AudioSystem_getSpatializer),
+        MAKE_JNI_NATIVE_METHOD("canBeSpatialized",
+                               "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
+                               "[Landroid/media/AudioDeviceAttributes;)Z",
+                               android_media_AudioSystem_canBeSpatialized),
+        MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose",
+                               "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;",
+                               android_media_AudioSystem_nativeGetSoundDose),
+        MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport",
+                               "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I",
+                               android_media_AudioSystem_getDirectPlaybackSupport),
+        MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes",
+                               "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I",
+                               android_media_AudioSystem_getDirectProfilesForAttributes),
+        MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I",
+                               android_media_AudioSystem_getSupportedMixerAttributes),
+        MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes",
+                               "(Landroid/media/AudioAttributes;IILandroid/media/"
+                               "AudioMixerAttributes;)I",
+                               android_media_AudioSystem_setPreferredMixerAttributes),
+        MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes",
+                               "(Landroid/media/AudioAttributes;ILjava/util/List;)I",
+                               android_media_AudioSystem_getPreferredMixerAttributes),
+        MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes",
+                               "(Landroid/media/AudioAttributes;II)I",
+                               android_media_AudioSystem_clearPreferredMixerAttributes),
+        MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency),
+        MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
+        MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
+        MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
+                               "(Ljava/lang/String;Ljava/lang/Runnable;)J",
+                               android_media_AudioSystem_listenForSystemPropertyChange),
+        MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate", "(J)V",
+                               android_media_AudioSystem_triggerSystemPropertyUpdate),
+};
 
 static const JNINativeMethod gEventHandlerMethods[] =
         {MAKE_JNI_NATIVE_METHOD("native_setup", "(Ljava/lang/Object;)V",
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index f5992d9..ce40e51 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -60,7 +60,7 @@
                                                                   ? layoutInfo->layoutType.c_str()
                                                                   : NULL));
 
-    std::shared_ptr<KeyCharacterMap> map = deviceInfo.getKeyCharacterMap();
+    const KeyCharacterMap* map = deviceInfo.getKeyCharacterMap();
     std::unique_ptr<KeyCharacterMap> mapCopy;
     if (map != nullptr) {
         mapCopy = std::make_unique<KeyCharacterMap>(*map);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a939d92..755704a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -115,6 +115,7 @@
     jfieldID supportedDisplayModes;
     jfieldID activeDisplayModeId;
     jfieldID renderFrameRate;
+    jfieldID hasArrSupport;
     jfieldID supportedColorModes;
     jfieldID activeColorMode;
     jfieldID hdrCapabilities;
@@ -1453,7 +1454,7 @@
     env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeDisplayModeId,
                      info.activeDisplayModeId);
     env->SetFloatField(object, gDynamicDisplayInfoClassInfo.renderFrameRate, info.renderFrameRate);
-
+    env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.hasArrSupport, info.hasArrSupport);
     jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size());
     if (colorModesArray == NULL) {
         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
@@ -2641,6 +2642,8 @@
             GetFieldIDOrDie(env, dynamicInfoClazz, "activeDisplayModeId", "I");
     gDynamicDisplayInfoClassInfo.renderFrameRate =
             GetFieldIDOrDie(env, dynamicInfoClazz, "renderFrameRate", "F");
+    gDynamicDisplayInfoClassInfo.hasArrSupport =
+            GetFieldIDOrDie(env, dynamicInfoClazz, "hasArrSupport", "Z");
     gDynamicDisplayInfoClassInfo.supportedColorModes =
             GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I");
     gDynamicDisplayInfoClassInfo.activeColorMode =
diff --git a/core/jni/android_view_WindowManagerGlobal.cpp b/core/jni/android_view_WindowManagerGlobal.cpp
index abc621d..4202de3 100644
--- a/core/jni/android_view_WindowManagerGlobal.cpp
+++ b/core/jni/android_view_WindowManagerGlobal.cpp
@@ -69,8 +69,8 @@
     JNIEnv* env = AndroidRuntime::getJNIEnv();
 
     ScopedLocalRef<jobject> clientTokenObj(env, javaObjectForIBinder(env, clientToken));
-    env->CallStaticObjectMethod(gWindowManagerGlobal.clazz, gWindowManagerGlobal.removeInputChannel,
-                                clientTokenObj.get());
+    env->CallStaticVoidMethod(gWindowManagerGlobal.clazz, gWindowManagerGlobal.removeInputChannel,
+                              clientTokenObj.get());
 }
 
 int register_android_view_WindowManagerGlobal(JNIEnv* env) {
@@ -88,4 +88,4 @@
     return NO_ERROR;
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 88b3e1c..24551a4 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -20,7 +20,9 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <jni_wrappers.h>
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/jni_macros.h>
+#include <unicode/locid.h>
 #include <unicode/putil.h>
 #include <unicode/udata.h>
 
@@ -64,8 +66,8 @@
 };
 
 int register_libcore_util_NativeAllocationRegistry(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry", gMethods,
-                                    NELEM(gMethods));
+    return android::RegisterMethodsOrDie(env, "libcore/util/NativeAllocationRegistry", gMethods,
+                                         NELEM(gMethods));
 }
 
 namespace android {
@@ -115,9 +117,10 @@
 #ifdef __linux__
         {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
         {"android.content.res.AssetManager", REG_JNI(register_android_content_AssetManager)},
+#endif
         {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
         {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
-#endif
+#ifdef __linux__
         {"android.database.CursorWindow", REG_JNI(register_android_database_CursorWindow)},
         {"android.database.sqlite.SQLiteConnection",
          REG_JNI(register_android_database_SQLiteConnection)},
@@ -125,7 +128,6 @@
         {"android.database.sqlite.SQLiteDebug", REG_JNI(register_android_database_SQLiteDebug)},
         {"android.database.sqlite.SQLiteRawStatement",
          REG_JNI(register_android_database_SQLiteRawStatement)},
-#ifdef __linux__
         {"android.os.Binder", REG_JNI(register_android_os_Binder)},
         {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
         {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
@@ -259,35 +261,68 @@
 #endif
 }
 
-// Loads the ICU data file from the location specified in the system property ro.icu.data.path
-static void loadIcuData() {
-    string icuPath = base::GetProperty("ro.icu.data.path", "");
-    if (!icuPath.empty()) {
-        // Set the location of ICU data
-        void* addr = mmapFile(icuPath.c_str());
-        UErrorCode err = U_ZERO_ERROR;
-        udata_setCommonData(addr, &err);
-        if (err != U_ZERO_ERROR) {
-            ALOGE("Unable to load ICU data\n");
-        }
-    }
-}
-
-static int register_android_core_classes(JNIEnv* env) {
+// returns result from java.lang.System.getProperty
+static string getJavaProperty(JNIEnv* env, const char* property_name,
+                              const char* defaultValue = "") {
     jclass system = FindClassOrDie(env, "java/lang/System");
     jmethodID getPropertyMethod =
             GetStaticMethodIDOrDie(env, system, "getProperty",
                                    "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
 
-    // Get the names of classes that need to register their native methods
-    auto nativesClassesJString =
-            (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
-                                                 env->NewStringUTF("core_native_classes"),
-                                                 env->NewStringUTF(""));
-    const char* nativesClassesArray = env->GetStringUTFChars(nativesClassesJString, nullptr);
-    string nativesClassesString(nativesClassesArray);
+    auto jString = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+                                                        env->NewStringUTF(property_name),
+                                                        env->NewStringUTF(defaultValue));
+    ScopedUtfChars chars(env, jString);
+    return string(chars.c_str());
+}
+
+static void loadIcuData(string icuPath) {
+    void* addr = mmapFile(icuPath.c_str());
+    UErrorCode err = U_ZERO_ERROR;
+    udata_setCommonData(addr, &err);
+    if (err != U_ZERO_ERROR) {
+        ALOGE("Unable to load ICU data\n");
+    }
+}
+
+// Loads the ICU data file from the location specified in properties.
+// First try specified in the system property ro.icu.data.path,
+// then fallback to java property icu.data.path
+static void loadIcuData() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    string icuPath = base::GetProperty("ro.icu.data.path", "");
+    if (!icuPath.empty()) {
+        loadIcuData(icuPath);
+    } else {
+        // fallback to read from java.lang.System.getProperty
+        string icuPathFromJava = getJavaProperty(env, "icu.data.path");
+        if (!icuPathFromJava.empty()) {
+            loadIcuData(icuPathFromJava);
+        }
+    }
+
+    // Check for the ICU default locale property. In Libcore, the default ICU
+    // locale is set when ICU.setDefaultLocale is called, which is called by
+    // Libcore's implemenentation of Java's Locale.setDefault. The default
+    // locale is used in cases such as when ucol_open(NULL, ...) is called, for
+    // example in SQLite's 'COLLATE UNICODE'.
+    string icuLocaleDefault = getJavaProperty(env, "icu.locale.default");
+    if (!icuLocaleDefault.empty()) {
+        UErrorCode status = U_ZERO_ERROR;
+        icu::Locale locale = icu::Locale::forLanguageTag(icuLocaleDefault.c_str(), status);
+        if (U_SUCCESS(status)) {
+            icu::Locale::setDefault(locale, status);
+        }
+        if (U_FAILURE(status)) {
+            fprintf(stderr, "Failed to set the ICU default locale to '%s' (error code %d)\n",
+                    icuLocaleDefault.c_str(), status);
+        }
+    }
+}
+
+static int register_android_core_classes(JNIEnv* env) {
+    string nativesClassesString = getJavaProperty(env, "core_native_classes");
     vector<string> classesToRegister = parseCsv(nativesClassesString);
-    env->ReleaseStringUTFChars(nativesClassesJString, nativesClassesArray);
 
     if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
         return JNI_ERR;
@@ -359,6 +394,11 @@
 
 void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+    auto method_binding_format = getJavaProperty(env, "method_binding_format");
+
+    setJniMethodFormat(method_binding_format);
+
     // Register native functions.
     if (startReg(env) < 0) {
         ALOGE("Unable to register all android native methods\n");
@@ -391,7 +431,6 @@
 
 } // namespace android
 
-#ifndef _WIN32
 using namespace android;
 
 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
@@ -400,12 +439,14 @@
         return JNI_ERR;
     }
 
-    Vector<String8> args;
-    HostRuntime runtime;
+    string useBaseHostRuntime = getJavaProperty(env, "use_base_native_hostruntime", "true");
+    if (useBaseHostRuntime == "true") {
+        Vector<String8> args;
+        HostRuntime runtime;
 
-    runtime.onVmCreated(env);
-    runtime.start("HostRuntime", args, false);
+        runtime.onVmCreated(env);
+        runtime.start("HostRuntime", args, false);
+    }
 
     return JNI_VERSION_1_6;
 }
-#endif
diff --git a/core/proto/android/app/OWNERS b/core/proto/android/app/OWNERS
index a137ea9..519bf9a 100644
--- a/core/proto/android/app/OWNERS
+++ b/core/proto/android/app/OWNERS
@@ -1,3 +1,3 @@
-per-file appstartinfo.proto = file:/services/core/java/com/android/server/am/OWNERS
+per-file appstartinfo.proto = file:/PERFORMANCE_OWNERS
 per-file location_time_zone_manager.proto = file:platform/frameworks/base:/services/core/java/com/android/server/timezonedetector/OWNERS
 per-file time_zone_detector.proto = file:platform/frameworks/base:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index e795e809..9779dc0e 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -220,6 +220,15 @@
     }
     optional Touchpad touchpad = 36;
 
+    message Mouse {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto reverse_vertical_scrolling = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto swap_primary_button = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+
+    optional Mouse mouse = 38;
+
     optional SettingProto tty_mode = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message Vibrate {
@@ -277,5 +286,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 38;
+    // Next tag = 39;
 }
diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto
index f477d32..6a987a4 100644
--- a/core/proto/android/widget/remoteviews.proto
+++ b/core/proto/android/widget/remoteviews.proto
@@ -32,6 +32,8 @@
  *
  * Do not change the tag number or type of any fields in order to maintain compatibility with
  * previous versions. If a field is deleted, use `reserved` to mark its tag number.
+ *
+ * Next tag: 17
  */
 message RemoteViewsProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -290,6 +292,7 @@
         }
     }
 
+    // Next tag: 23
     message Action {
         oneof action {
             AttributeReflectionAction attribute_reflection_action = 1;
@@ -307,6 +310,13 @@
             SetRadioGroupCheckedAction set_radio_group_checked_action = 13;
             SetRemoteCollectionItemListAdapterAction set_remote_collection_item_list_adapter_action = 14;
             SetRippleDrawableColorAction set_ripple_drawable_color_action = 15;
+            SetViewOutlinePreferredRadiusAction set_view_outline_preferred_radius_action = 16;
+            TextViewDrawableAction text_view_drawable_action = 17;
+            TextViewSizeAction text_view_size_action = 18;
+            ViewGroupAddAction view_group_add_action = 19;
+            ViewGroupRemoveAction view_group_remove_action = 20;
+            ViewPaddingAction view_padding_action = 21;
+            SetDrawInstructionAction set_draw_instruction_action = 22;
         }
     }
 
@@ -428,6 +438,65 @@
         optional string view_id = 1;
         optional android.content.res.ColorStateListProto color_state_list = 2;
     }
+
+    message SetViewOutlinePreferredRadiusAction {
+        optional string view_id = 1;
+        optional int32 value_type = 2;
+        optional int32 value = 3;
+    }
+
+    message TextViewDrawableAction {
+        optional string view_id = 1;
+        optional bool is_relative = 2;
+        oneof drawables {
+            Resources resources = 3;
+            Icons icons = 4;
+        };
+
+        message Resources {
+            optional string one = 1;
+            optional string two = 2;
+            optional string three = 3;
+            optional string four = 4;
+        }
+
+        message Icons {
+            optional Icon one = 1;
+            optional Icon two = 2;
+            optional Icon three = 3;
+            optional Icon four = 4;
+        }
+    }
+
+    message TextViewSizeAction {
+        optional string view_id = 1;
+        optional int32 units = 2;
+        optional float size = 3;
+    }
+
+    message ViewGroupAddAction {
+        optional string view_id = 1;
+        optional RemoteViewsProto nested_views = 2;
+        optional int32 index = 3;
+        optional int32 stableId = 4;
+    }
+
+    message ViewGroupRemoveAction {
+        optional string view_id = 1;
+        optional string view_id_to_keep = 2;
+    }
+
+    message ViewPaddingAction {
+        optional string view_id = 1;
+        optional int32 left = 2;
+        optional int32 right = 3;
+        optional int32 top = 4;
+        optional int32 bottom = 5;
+    }
+
+    message SetDrawInstructionAction {
+        repeated bytes instructions = 1;
+    }
 }
 
 
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 17d7bfa..aa324fc 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -167,6 +167,7 @@
         "android.os.flags-aconfig",
         "android.os.vibrator.flags-aconfig",
         "android.media.tv.flags-aconfig",
+        "android.security.flags-aconfig",
         "com.android.hardware.input.input-aconfig",
     ],
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 549f8df..6ab6476 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -155,6 +155,7 @@
 
     <protected-broadcast android:name="android.bluetooth.intent.DISCOVERABLE_TIMEOUT" />
     <protected-broadcast android:name="android.bluetooth.action.AUTO_ON_STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
@@ -240,6 +241,8 @@
     <protected-broadcast
         android:name="android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST" />
     <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING" />
+    <protected-broadcast
         android:name="android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT" />
     <protected-broadcast
         android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
@@ -266,6 +269,7 @@
     <protected-broadcast
         android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.action.HAP_CONNECTION_STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.action.HAP_DEVICE_AVAILABLE" />
     <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONF_CHANGED" />
@@ -848,6 +852,7 @@
     <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_LOADED" />
     <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_UNLOADED" />
 
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -4101,6 +4106,24 @@
                 android:protectionLevel="signature|installer" />
     <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
 
+    <!-- Allows an application to toggle the device's advanced protection mode status.
+        @FlaggedApi("android.security.aapm_api")
+        @SystemApi
+        @hide -->
+    <permission android:name="android.permission.SET_ADVANCED_PROTECTION_MODE"
+        android:protectionLevel="signature|privileged"
+        android:featureFlag="android.security.aapm_api"/>
+    <uses-permission android:name="android.permission.SET_ADVANCED_PROTECTION_MODE"
+        android:featureFlag="android.security.aapm_api"/>
+
+    <!-- Allows an application to query the device's advanced protection mode status.
+        @FlaggedApi("android.security.aapm_api") -->
+    <permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
+        android:protectionLevel="normal"
+        android:featureFlag="android.security.aapm_api"/>
+    <uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
+        android:featureFlag="android.security.aapm_api"/>
+
     <!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.-->
     <permission android:name="android.permission.PROVISION_DEMO_DEVICE"
                 android:protectionLevel="signature|setup|knownSigner"
@@ -8475,6 +8498,18 @@
         android:protectionLevel="internal"
         android:featureFlag="android.content.pm.verification_service" />
 
+    <!--
+        This permission allows the system to receive PACKAGE_CHANGED broadcasts when the component
+        state of a non-exported component has been changed.
+        <p>Not for use by third-party applications. </p>
+        <p>Protection level: internal
+        @hide
+    -->
+    <permission
+        android:name="android.permission.RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+        android:protectionLevel="internal"
+        android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 5293131..faed4d8 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -53,13 +53,14 @@
 per-file res/values/dimens_car.xml = file:/platform/packages/services/Car:/OWNERS
 
 # Device Idle
-per-file res/values/config_device_idle.xml = file:/apex/jobscheduler/OWNERS
+per-file res/values/config_device_idle.xml = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
 
 # Display Manager
 per-file res/values/config_display.xml = file:/services/core/java/com/android/server/display/OWNERS
 
 # Wear
 per-file res/*-watch/* = file:/WEAR_OWNERS
+per-file res/*-watch-v*/* = file:/WEAR_OWNERS
 
 # Performance
 per-file res/values/config.xml = file:/PERFORMANCE_OWNERS
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
similarity index 60%
copy from packages/SystemUI/res/layout/udfps_bp_view.xml
copy to core/res/res/color-watch-v36/btn_material_filled_background_color.xml
index f1c55ef..8b2afa8 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,9 +13,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/disabledAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorPrimary" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
new file mode 100644
index 0000000..cefc912
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/primaryContentAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorOnPrimary" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
new file mode 100644
index 0000000..eaf9e7d
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/disabledAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorSurfaceContainer" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
new file mode 100644
index 0000000..94e50fb
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?attr/primaryContentAlpha"
+          android:color="?attr/materialColorOnSurface" />
+    <item android:state_enabled="true"
+          android:color="?attr/materialColorOnSurface" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
new file mode 100644
index 0000000..0029de1
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/btn_material_filled_background_color"/>
+            <corners android:radius="?android:attr/buttonCornerRadius"/>
+            <size
+                android:width="@dimen/btn_material_width"
+                android:height="@dimen/btn_material_height" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
new file mode 100644
index 0000000..105f077
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+            <corners android:radius="?android:attr/buttonCornerRadius"/>
+            <size
+                android:width="@dimen/btn_material_width"
+                android:height="@dimen/btn_material_height" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_piano.xml b/core/res/res/drawable/ic_zen_mode_icon_piano.xml
new file mode 100644
index 0000000..012b939
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_piano.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L330,760L330,580L320,580Q303,580 291.5,568.5Q280,557 280,540L280,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM630,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L680,200L680,540Q680,557 668.5,568.5Q657,580 640,580L630,580L630,760ZM390,760L570,760L570,580L560,580Q543,580 531.5,568.5Q520,557 520,540L520,200L440,200L440,540Q440,557 428.5,568.5Q417,580 400,580L390,580L390,760Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
new file mode 100644
index 0000000..3a6b600
--- /dev/null
+++ b/core/res/res/drawable/notification_progress.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@id/background"
+          android:gravity="center_vertical|fill_horizontal">
+        <com.android.internal.widget.NotificationProgressDrawable
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:segSegGap="@dimen/notification_progress_segSeg_gap"
+            android:segPointGap="@dimen/notification_progress_segPoint_gap">
+            <segments
+                android:color="?attr/colorProgressBackgroundNormal"
+                android:dashGap="@dimen/notification_progress_segments_dash_gap"
+                android:dashWidth="@dimen/notification_progress_segments_dash_width"
+                android:width="@dimen/notification_progress_segments_height" />
+            <points
+                android:color="?attr/colorProgressBackgroundNormal"
+                android:radius="@dimen/notification_progress_points_radius"
+                android:cornerRadius="@dimen/notification_progress_points_corner_radius"
+                android:inset="@dimen/notification_progress_points_inset" />
+        </com.android.internal.widget.NotificationProgressDrawable>
+    </item>
+</layer-list>
diff --git a/core/res/res/layout/input_method_switch_dialog_new.xml b/core/res/res/layout/input_method_switch_dialog_new.xml
index 058fe3f..118f93b 100644
--- a/core/res/res/layout/input_method_switch_dialog_new.xml
+++ b/core/res/res/layout/input_method_switch_dialog_new.xml
@@ -39,7 +39,7 @@
                 android:id="@+id/list"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:paddingVertical="8dp"
+                android:paddingTop="8dp"
                 android:clipToPadding="false"
                 android:layoutManager="com.android.internal.widget.LinearLayoutManager"/>
 
@@ -74,8 +74,7 @@
             android:text="@string/input_method_switcher_settings_button"
             android:fontFamily="google-sans-text"
             android:textAppearance="?attr/textAppearance"
-            android:contentDescription="@string/input_method_language_settings"
-            android:visibility="gone"/>
+            android:contentDescription="@string/input_method_language_settings"/>
 
     </LinearLayout>
 
diff --git a/core/res/res/layout/input_method_switch_item_divider.xml b/core/res/res/layout/input_method_switch_item_divider.xml
new file mode 100644
index 0000000..4f8c963
--- /dev/null
+++ b/core/res/res/layout/input_method_switch_item_divider.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:layout_marginHorizontal="16dp"
+    android:layout_marginTop="8dp"
+    android:layout_marginBottom="16dp">
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/materialColorOutlineVariant"
+        android:layout_marginStart="20dp"
+        android:layout_marginEnd="24dp"
+        android:importantForAccessibility="no"/>
+
+</LinearLayout>
diff --git a/core/res/res/layout/input_method_switch_item_header.xml b/core/res/res/layout/input_method_switch_item_header.xml
new file mode 100644
index 0000000..f0080a6
--- /dev/null
+++ b/core/res/res/layout/input_method_switch_item_header.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:layout_marginHorizontal="16dp"
+    android:layout_marginTop="4dp"
+    android:layout_marginBottom="16dp">
+
+    <TextView
+        android:id="@+id/header_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="8dp"
+        android:ellipsize="end"
+        android:singleLine="true"
+        android:fontFamily="google-sans-text"
+        android:textAppearance="?attr/textAppearance"
+        android:accessibilityHeading="true"
+        android:textColor="?attr/materialColorPrimary"/>
+
+</LinearLayout>
diff --git a/core/res/res/layout/input_method_switch_item_new.xml b/core/res/res/layout/input_method_switch_item_new.xml
index 10d938c..f8710cc 100644
--- a/core/res/res/layout/input_method_switch_item_new.xml
+++ b/core/res/res/layout/input_method_switch_item_new.xml
@@ -16,76 +16,45 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list_item"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:paddingHorizontal="16dp"
-    android:paddingBottom="8dp">
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:background="?attr/materialColorSurfaceVariant"
-        android:layout_marginStart="20dp"
-        android:layout_marginTop="8dp"
-        android:layout_marginEnd="24dp"
-        android:layout_marginBottom="12dp"
-        android:visibility="gone"
-        android:importantForAccessibility="no"/>
-
-    <TextView
-        android:id="@+id/header_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:padding="8dp"
-        android:ellipsize="end"
-        android:singleLine="true"
-        android:fontFamily="google-sans-text"
-        android:textAppearance="?attr/textAppearance"
-        android:textColor="?attr/materialColorPrimary"
-        android:visibility="gone"/>
+    android:layout_height="72dp"
+    android:background="@drawable/input_method_switch_item_background"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:layout_marginHorizontal="16dp"
+    android:layout_marginBottom="8dp"
+    android:paddingStart="20dp"
+    android:paddingEnd="24dp">
 
     <LinearLayout
-        android:id="@+id/list_item"
-        android:layout_width="match_parent"
-        android:layout_height="72dp"
-        android:background="@drawable/input_method_switch_item_background"
-        android:gravity="center_vertical"
-        android:orientation="horizontal"
-        android:paddingStart="20dp"
-        android:paddingEnd="24dp">
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="start|center_vertical"
+        android:orientation="vertical">
 
-        <LinearLayout
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:gravity="start|center_vertical"
-            android:orientation="vertical">
-
-            <TextView
-                android:id="@+id/text"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="marquee"
-                android:singleLine="true"
-                android:fontFamily="google-sans-text"
-                android:textColor="@color/input_method_switch_on_item"
-                android:textAppearance="?attr/textAppearanceListItem"/>
-
-        </LinearLayout>
-
-        <ImageView
-            android:id="@+id/image"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:gravity="center_vertical"
-            android:layout_marginStart="12dp"
-            android:src="@drawable/ic_check_24dp"
-            android:tint="@color/input_method_switch_on_item"
-            android:visibility="gone"
-            android:importantForAccessibility="no"/>
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:singleLine="true"
+            android:fontFamily="google-sans-text"
+            android:textColor="@color/input_method_switch_on_item"
+            android:textAppearance="?attr/textAppearanceListItem"/>
 
     </LinearLayout>
 
+    <ImageView
+        android:id="@+id/image"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:gravity="center_vertical"
+        android:layout_marginStart="12dp"
+        android:src="@drawable/ic_check_24dp"
+        android:tint="@color/input_method_switch_on_item"
+        android:visibility="gone"
+        android:importantForAccessibility="no"/>
+
 </LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml
index b413c70..75827a2 100644
--- a/core/res/res/layout/notification_template_material_progress.xml
+++ b/core/res/res/layout/notification_template_material_progress.xml
@@ -77,9 +77,9 @@
 
                     <include
                         android:layout_width="0dp"
-                        android:layout_height="@dimen/notification_progress_bar_height"
-                        layout="@layout/notification_template_progress"
                         android:layout_weight="1"
+                        android:layout_height="@dimen/notification_progress_tracker_height"
+                        layout="@layout/notification_template_notification_progress_bar"
                         />
 
                     <com.android.internal.widget.CachingIconView
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/layout/notification_template_notification_progress_bar.xml
similarity index 61%
rename from packages/SystemUI/res/layout/udfps_bp_view.xml
rename to core/res/res/layout/notification_template_notification_progress_bar.xml
index f1c55ef..3574896 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/layout/notification_template_notification_progress_bar.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2014 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.
@@ -12,11 +12,12 @@
   ~ 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.
+  ~ limitations under the License
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
+
+<com.android.internal.widget.NotificationProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/progress"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+    android:layout_height="@dimen/notification_progress_tracker_height"
+    style="@style/Widget.Material.Notification.NotificationProgressBar"
+    />
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1d20526..957e835 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1904,7 +1904,7 @@
     <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"Upaya ke-2 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"Upaya ke-3 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="clone_profile_label_badge" msgid="1871997694718793964">"Clone <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="private_profile_label_badge" msgid="1712086003787839183">"<xliff:g id="LABEL">%1$s</xliff:g> Pribadi"</string>
+    <string name="private_profile_label_badge" msgid="1712086003787839183">"<xliff:g id="LABEL">%1$s</xliff:g> Privasi"</string>
     <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Meminta PIN sebelum melepas sematan"</string>
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Meminta pola pembukaan kunci sebelum melepas sematan"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Meminta sandi sebelum melepas sematan"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 8f4fbcd..9323276 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -2122,7 +2122,7 @@
     <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"לא ניתן היה לשחזר את קיצור הדרך עקב חוסר התאמה בחתימה על האפליקציות"</string>
     <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"לא ניתן היה לשחזר את קיצור הדרך"</string>
     <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"מקש הקיצור מושבת"</string>
-    <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"הסרת התקנה"</string>
+    <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"הסרה"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"לפתוח בכל זאת"</string>
     <string name="harmful_app_warning_title" msgid="8794823880881113856">"אותרה אפליקציה מזיקה"</string>
     <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> רוצה להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/values-watch-v36/colors.xml
similarity index 60%
copy from packages/SystemUI/res/layout/udfps_bp_view.xml
copy to core/res/res/values-watch-v36/colors.xml
index f1c55ef..4bc2a66 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/values-watch-v36/colors.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,9 +13,6 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+<!-- TODO(b/372524566): update color token's value to match material3 design. -->
+<resources>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/values-watch-v36/config.xml
similarity index 60%
copy from packages/SystemUI/res/layout/udfps_bp_view.xml
copy to core/res/res/values-watch-v36/config.xml
index f1c55ef..c8f347af 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/values-watch-v36/config.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,9 +13,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+
+<resources>
+    <!-- Overrides system value -->
+    <dimen name="config_buttonCornerRadius">26dp</dimen>
+</resources>
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
new file mode 100644
index 0000000..ad3c1a3
--- /dev/null
+++ b/core/res/res/values-watch-v36/dimens_material.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+    <!-- values for material3 button -->
+    <dimen name="btn_material_width">172dp</dimen>
+    <dimen name="btn_material_height">52dp</dimen>
+    <dimen name="btn_horizontal_edge_padding">14dp</dimen>
+    <dimen name="btn_drawable_padding">6dp</dimen>
+    <dimen name="btn_lineHeight">18sp</dimen>
+    <dimen name="btn_textSize">15sp</dimen>
+
+    <!-- Opacity factor for disabled material3 widget -->
+    <dimen name="disabled_alpha_device_default">0.12</dimen>
+    <dimen name="primary_content_alpha_device_default">0.38</dimen>
+</resources>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
new file mode 100644
index 0000000..32a22bb
--- /dev/null
+++ b/core/res/res/values-watch-v36/styles_material.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <!--  Button Styles  -->
+    <!-- Material Button - Filled  -->
+    <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button">
+        <item name="android:background">@drawable/btn_background_material_filled</item>
+        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
+    </style>
+
+    <!-- Material Button - Filled Tonal(Override system default button styles) -->
+    <style name="Widget.DeviceDefault.Button">
+        <item name="background">@drawable/btn_background_material_filled_tonal</item>
+        <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
+        <item name="minHeight">@dimen/btn_material_height</item>
+        <item name="maxWidth">@dimen/btn_material_width</item>
+        <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
+        <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
+        <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
+        <item name="android:maxLines">2</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:breakStrategy">simple</item>
+        <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
+        <item name="focusable">true</item>
+        <item name="clickable">true</item>
+        <item name="gravity">center_vertical</item>
+    </style>
+
+    <!--  Text Styles  -->
+    <!-- TextAppearance for Material Button - Filled  -->
+    <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material">
+        <item name="textColor">@color/btn_material_filled_text_color</item>
+    </style>
+
+    <!-- TextAppearance for Material Button - Filled Tonal  -->
+    <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
+        <item name="android:fontFamily">font-family-flex-device-default</item>
+        <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
+        <item name="textSize">@dimen/btn_textSize</item>
+        <item name="textColor">@color/btn_material_filled_tonal_text_color</item>
+        <item name="lineHeight">@dimen/btn_lineHeight</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8a2d767..4198171 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -287,6 +287,11 @@
          vibration params. -->
     <integer name="config_requestVibrationParamsTimeout">50</integer>
 
+    <!-- The max duration (in milliseconds) that the vibrator service will allow effects to be
+         pipelined (i.e. service will wait for ongoing vibration to finish instead of cancelling it
+         to start the new one). Value should be positive. Zero will disable effect pipelining. -->
+    <integer name="config_vibrationPipelineMaxDuration">25</integer>
+
     <!-- Array containing the usages that should request vibration params before they are played.
          These usages don't have strong latency requirements, e.g. ringtone and notification, and
          can be slightly delayed. -->
@@ -4447,17 +4452,25 @@
     <string-array translatable="false" name="config_defaultPinnerServiceFiles">
     </string-array>
 
-    <!-- True if camera app should be pinned via Pinner Service -->
+    <!-- Note: This config is deprecated, use config_pinnerCameraPinBytes instead.
+         True if camera app should be pinned via Pinner Service -->
     <bool name="config_pinnerCameraApp">false</bool>
 
-    <!-- Bytes that the PinnerService will pin for Home app -->
-    <integer name="config_pinnerHomePinBytes">0</integer>
+    <!-- Default: 60 MB. Bytes that the PinnerService will pin for Home app -->
+    <integer name="config_pinnerHomePinBytes">62914560</integer>
 
-    <!-- True if assistant app should be pinned via Pinner Service -->
+    <!-- Default: 80 MB. Bytes that the PinnerService will pin for Camera app -->
+    <integer name="config_pinnerCameraPinBytes">83886080</integer>
+
+    <!-- Note: This config is deprecated, use config_pinnerAssistantPinBytes instead.
+         True if assistant app should be pinned via Pinner Service -->
     <bool name="config_pinnerAssistantApp">false</bool>
 
-    <!-- Bytes that the PinnerService will pin for WebView -->
-    <integer name="config_pinnerWebviewPinBytes">0</integer>
+     <!-- Default: 60 MB. Bytes that the PinnerService will pin for Assistant -->
+    <integer name="config_pinnerAssistantPinBytes">62914560</integer>
+
+    <!-- Default: 20 MB. Bytes that the PinnerService will pin for WebView -->
+    <integer name="config_pinnerWebviewPinBytes">20971520</integer>
 
     <!-- Maximum memory that PinnerService will pin for apps expressed
          as a percentage of total device memory [0,100].
@@ -4857,7 +4870,7 @@
      See android.credentials.CredentialManager
     -->
     <string name="config_defaultCredentialManagerHybridService" translatable="false"></string>
-    
+
     <!-- The component name, flattened to a string, for the system's credential manager
       autofill service. This service allows interceding autofill requests and routing
       them to credential manager.
@@ -5770,6 +5783,9 @@
          of known compatibility issues. -->
     <string-array name="config_highRefreshRateBlacklist"></string-array>
 
+    <!-- The list of packages that will use ANGLE as the GLES driver -->
+    <string-array name="config_angleAllowList"></string-array>
+
     <!-- The list of packages to automatically opt in to refresh rate suppressing by small area
     detection. Format of this array should be packageName:threshold and threshold value should
      be between 0 to 1-->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b92aa2f..7184d9a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -817,6 +817,22 @@
     <dimen name="notification_progress_tracker_width">40dp</dimen>
     <!-- The size of the progress tracker height -->
     <dimen name="notification_progress_tracker_height">20dp</dimen>
+    <!-- The gap between segments in the notification progress bar -->
+    <dimen name="notification_progress_segSeg_gap">2dp</dimen>
+    <!-- The gap between a segment and a point in the notification progress bar -->
+    <dimen name="notification_progress_segPoint_gap">4dp</dimen>
+    <!-- The dash gap of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_dash_gap">9dp</dimen>
+    <!-- The dash width of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_dash_width">3dp</dimen>
+    <!-- The height of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_height">6dp</dimen>
+    <!-- The radius of the notification progress bar points -->
+    <dimen name="notification_progress_points_radius">10dp</dimen>
+    <!-- The corner radius of the notification progress bar points drawn as rects -->
+    <dimen name="notification_progress_points_corner_radius">4dp</dimen>
+    <!-- The inset of the notification progress bar points drawn as rects -->
+    <dimen name="notification_progress_points_inset">0dp</dimen>
 
     <!-- The maximum size of the small notification icon on low memory devices. -->
     <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index eec6ae3..cb8e4aa 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -502,6 +502,10 @@
 
     <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
 
+    <style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
+        <item name="progressDrawable">@drawable/notification_progress</item>
+    </style>
+
     <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
         <item name="lineHeight">20sp</item>
         <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1cb50f4..0b2b345 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1582,6 +1582,8 @@
   <java-symbol type="layout" name="input_method" />
   <java-symbol type="layout" name="input_method_extract_view" />
   <java-symbol type="layout" name="input_method_switch_item" />
+  <java-symbol type="layout" name="input_method_switch_item_divider" />
+  <java-symbol type="layout" name="input_method_switch_item_header" />
   <java-symbol type="layout" name="input_method_switch_item_new" />
   <java-symbol type="layout" name="input_method_switch_dialog_new" />
   <java-symbol type="layout" name="input_method_switch_dialog_title" />
@@ -2139,6 +2141,7 @@
   <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
   <java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
   <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
+  <java-symbol type="integer" name="config_vibrationPipelineMaxDuration" />
   <java-symbol type="integer" name="config_radioScanningTimeout" />
   <java-symbol type="integer" name="config_requestVibrationParamsTimeout" />
   <java-symbol type="array" name="config_requestVibrationParamsForUsages" />
@@ -3469,6 +3472,8 @@
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
   <java-symbol type="bool" name="config_pinnerCameraApp" />
   <java-symbol type="integer" name="config_pinnerHomePinBytes" />
+  <java-symbol type="integer" name="config_pinnerCameraPinBytes" />
+  <java-symbol type="integer" name="config_pinnerAssistantPinBytes" />
   <java-symbol type="bool" name="config_pinnerAssistantApp" />
   <java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
   <java-symbol type="integer" name="config_pinnerMaxPinnedMemoryPercentage" />
@@ -3862,6 +3867,14 @@
   <java-symbol type="dimen" name="notification_progress_icon_size" />
   <java-symbol type="dimen" name="notification_progress_tracker_width" />
   <java-symbol type="dimen" name="notification_progress_tracker_height" />
+  <java-symbol type="dimen" name="notification_progress_segSeg_gap" />
+  <java-symbol type="dimen" name="notification_progress_segPoint_gap" />
+  <java-symbol type="dimen" name="notification_progress_segments_dash_gap" />
+  <java-symbol type="dimen" name="notification_progress_segments_dash_width" />
+  <java-symbol type="dimen" name="notification_progress_segments_height" />
+  <java-symbol type="dimen" name="notification_progress_points_radius" />
+  <java-symbol type="dimen" name="notification_progress_points_corner_radius" />
+  <java-symbol type="dimen" name="notification_progress_points_inset" />
 
   <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
   <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
@@ -4446,6 +4459,7 @@
 
   <java-symbol type="string" name="config_factoryResetPackage" />
   <java-symbol type="array" name="config_highRefreshRateBlacklist" />
+  <java-symbol type="array" name="config_angleAllowList" />
   <java-symbol type="array" name="config_forceSlowJpegModeList" />
   <java-symbol type="array" name="pause_wallpaper_render_when_state_change" />
   <java-symbol type="bool" name="config_pauseWallpaperRenderWhenStateChangeEnabled" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index d35bfb7..352c390 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -212,9 +212,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -314,9 +314,9 @@
     <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -431,9 +431,9 @@
     <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -550,9 +550,9 @@
     <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -668,9 +668,9 @@
     <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -801,9 +801,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -911,9 +911,9 @@
     <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1027,9 +1027,9 @@
     <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1144,9 +1144,9 @@
     <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1277,9 +1277,9 @@
     <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1395,9 +1395,9 @@
     <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1511,9 +1511,9 @@
     <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1629,9 +1629,9 @@
     <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1746,9 +1746,9 @@
     <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1863,9 +1863,9 @@
     <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -1980,9 +1980,9 @@
     <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -2097,9 +2097,9 @@
     <style name="Theme.DeviceDefault.VoiceInteractionSession" parent="Theme.Material.VoiceInteractionSession">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -2218,9 +2218,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -2336,9 +2336,9 @@
     <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -2451,9 +2451,9 @@
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -2720,9 +2720,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -2821,9 +2821,9 @@
     <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -2937,9 +2937,9 @@
     <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3054,9 +3054,9 @@
     <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3173,9 +3173,9 @@
     <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3291,9 +3291,9 @@
     <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3426,9 +3426,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3535,9 +3535,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3654,9 +3654,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3774,9 +3774,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3895,9 +3895,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -3996,9 +3996,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4096,9 +4096,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4217,9 +4217,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4336,9 +4336,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4454,9 +4454,9 @@
     <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Material.Light.Panel">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4571,9 +4571,9 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4688,9 +4688,9 @@
     <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4803,9 +4803,9 @@
     <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -4937,7 +4937,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -5044,7 +5044,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -5148,7 +5148,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -5245,7 +5245,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_dark</item>
@@ -5361,7 +5361,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -5487,7 +5487,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -5606,7 +5606,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
         <item name="colorAccentPrimaryVariant">@color/system_primary_container_light</item>
@@ -5788,7 +5788,7 @@
 
     <style name="ThemeOverlay.DeviceDefault.Accent">
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
 
@@ -5863,7 +5863,7 @@
 
     <style name="ThemeOverlay.DeviceDefault.Accent.Light">
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
 
@@ -5942,7 +5942,7 @@
 
     <style name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" parent="ThemeOverlay.Material.Dark.ActionBar">
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
         <item name="colorAccentSecondary">@color/system_secondary_dark</item>
         <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
 
diff --git a/core/tests/coretests/src/android/app/ActivityManagerTest.java b/core/tests/coretests/src/android/app/ActivityManagerTest.java
index d850f86..85ff846 100644
--- a/core/tests/coretests/src/android/app/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/ActivityManagerTest.java
@@ -60,7 +60,6 @@
     public void testProcState() throws Exception {
         // For the moment mostly want to confirm we don't crash
         assertNotNull(ActivityManager.procStateToString(PROCESS_STATE_SERVICE));
-        assertNotNull(ActivityManager.processStateAmToProto(PROCESS_STATE_SERVICE));
         assertTrue(ActivityManager.isProcStateBackground(PROCESS_STATE_SERVICE));
         assertFalse(ActivityManager.isProcStateCached(PROCESS_STATE_SERVICE));
         assertFalse(ActivityManager.isForegroundService(PROCESS_STATE_SERVICE));
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index e19f887..e4b5407 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -51,6 +52,7 @@
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.MediaStore.Audio.AudioColumns;
+import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 import android.util.Xml;
 
@@ -399,6 +401,29 @@
     }
 
     @Test
+    public void testWriteXmlForBackup_noAccessToFile() throws Exception {
+        Uri uri = Uri.parse("content://media/1");
+
+        AudioAttributes mAudioAttributes =
+                new AudioAttributes.Builder()
+                        .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+                        .setUsage(AudioAttributes.USAGE_NOTIFICATION)
+                        .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
+                        .build();
+
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        channel.setSound(uri, mAudioAttributes);
+
+        when(mIContentProvider.canonicalize(any(), any())).thenThrow(new SecurityException(""));
+        doThrow(new SecurityException("")).when(mIContentProvider)
+                .canonicalizeAsync(any(), any(), any());
+
+        NotificationChannel restoredChannel = backUpAndRestore(channel);
+        assertThat(restoredChannel.getSound())
+                .isEqualTo(Settings.System.DEFAULT_NOTIFICATION_URI);
+    }
+
+    @Test
     public void testVibrationGetters_nonPatternBasedVibrationEffect_waveform() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
         NotificationChannel channel = new NotificationChannel("id", "name", 3);
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 48e2620..23a0985 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -2397,10 +2397,25 @@
     public void progressStyle_getProgressMax_returnsSumOfSegmentLength() {
         final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
         progressStyle
+                .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15),
+                        new Notification.ProgressStyle.Segment(25)))
                 .addProgressSegment(new Notification.ProgressStyle.Segment(10))
                 .addProgressSegment(new Notification.ProgressStyle.Segment(20));
 
-        assertThat(progressStyle.getProgressMax()).isEqualTo(30);
+        assertThat(progressStyle.getProgressMax()).isEqualTo(70);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_getProgressMax_onSetProgressSegments_resets() {
+        final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+        progressStyle
+                .addProgressSegment(new Notification.ProgressStyle.Segment(10))
+                .addProgressSegment(new Notification.ProgressStyle.Segment(20))
+                .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15),
+                        new Notification.ProgressStyle.Segment(25)));
+
+        assertThat(progressStyle.getProgressMax()).isEqualTo(40);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index 5636f9b..6d14ccb 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -14,3 +14,5 @@
 # Files related to background activity launches
 per-file Background*Start* = file:/BAL_OWNERS
 
+# Files related to caching
+per-file PropertyInvalidatedCache* = file:/PERFORMANCE_OWNERS
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index b972882..cd52421 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -111,12 +111,6 @@
         assertEquals(config.reqKeyboardType, vconfig.keyboard);
         assertEquals(config.reqTouchScreen, vconfig.touchscreen);
         assertEquals(config.reqNavigation, vconfig.navigation);
-        if (vconfig.navigation == Configuration.NAVIGATION_NONAV) {
-            assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV);
-        }
-        if (vconfig.keyboard != Configuration.KEYBOARD_UNDEFINED) {
-            assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD);
-        }    
     }
 
     @SmallTest
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index a28b2f6..51e79e7 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -24,6 +24,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.ViewNodeBuilder;
@@ -37,6 +38,7 @@
 import android.os.LocaleList;
 import android.os.OutcomeReceiver;
 import android.os.Parcel;
+import android.os.PooledStringWriter;
 import android.os.SystemClock;
 import android.text.InputFilter;
 import android.util.Log;
@@ -355,6 +357,18 @@
 
     }
 
+    @Test
+    public void testParcelTransferWriter_writeNull() {
+        AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+        Parcel parcel = Parcel.obtain();
+        AssistStructure.ParcelTransferWriter writer =
+                new AssistStructure.ParcelTransferWriter(structure, parcel);
+        writer.writeView(null, parcel, new PooledStringWriter(parcel), 0);
+
+        // No throw any exception.
+        assertTrue(true);
+    }
+
     private EditText newSmallView() {
         EditText view = new EditText(mContext);
         view.setText("I AM GROOT");
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
index 987f68d..90ae306 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
@@ -16,6 +16,9 @@
 
 package android.content.pm.verify;
 
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -23,12 +26,13 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningInfo;
 import android.content.pm.VersionedPackage;
-import android.content.pm.verify.pkg.IVerificationSessionCallback;
 import android.content.pm.verify.pkg.IVerificationSessionInterface;
 import android.content.pm.verify.pkg.VerificationSession;
 import android.content.pm.verify.pkg.VerificationStatus;
@@ -73,13 +77,12 @@
     private static final long TEST_EXTEND_TIME = 2000L;
     private static final String TEST_KEY = "test key";
     private static final String TEST_VALUE = "test value";
+    private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
 
     private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>();
     private final PersistableBundle mTestExtensionParams = new PersistableBundle();
     @Mock
     private IVerificationSessionInterface mTestSessionInterface;
-    @Mock
-    private IVerificationSessionCallback mTestCallback;
     private VerificationSession mTestSession;
 
     @Before
@@ -90,7 +93,7 @@
         mTestExtensionParams.putString(TEST_KEY, TEST_VALUE);
         mTestSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID,
                 TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO, mTestDeclaredLibraries,
-                mTestExtensionParams, mTestSessionInterface, mTestCallback);
+                mTestExtensionParams, TEST_POLICY, mTestSessionInterface);
     }
 
     @Test
@@ -118,6 +121,7 @@
         // structure is different, but all the key/value pairs should be preserved as before.
         assertThat(sessionFromParcel.getExtensionParams().getString(TEST_KEY))
                 .isEqualTo(mTestExtensionParams.getString(TEST_KEY));
+        assertThat(sessionFromParcel.getVerificationPolicy()).isEqualTo(TEST_POLICY);
     }
 
     @Test
@@ -131,25 +135,60 @@
         assertThat(mTestSession.extendTimeRemaining(TEST_EXTEND_TIME)).isEqualTo(TEST_EXTEND_TIME);
         verify(mTestSessionInterface, times(1)).extendTimeRemaining(
                 eq(TEST_ID), eq(TEST_EXTEND_TIME));
-    }
 
-    @Test
-    public void testCallback() throws Exception {
         PersistableBundle response = new PersistableBundle();
         response.putString("test key", "test value");
         final VerificationStatus status =
                 new VerificationStatus.Builder().setVerified(true).build();
         mTestSession.reportVerificationComplete(status);
-        verify(mTestCallback, times(1)).reportVerificationComplete(
+        verify(mTestSessionInterface, times(1)).reportVerificationComplete(
                 eq(TEST_ID), eq(status));
         mTestSession.reportVerificationComplete(status, response);
-        verify(mTestCallback, times(1))
+        verify(mTestSessionInterface, times(1))
                 .reportVerificationCompleteWithExtensionResponse(
                         eq(TEST_ID), eq(status), eq(response));
 
         final int reason = VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
         mTestSession.reportVerificationIncomplete(reason);
-        verify(mTestCallback, times(1)).reportVerificationIncomplete(
+        verify(mTestSessionInterface, times(1)).reportVerificationIncomplete(
                 eq(TEST_ID), eq(reason));
     }
+
+    @Test
+    public void testPolicyNoOverride() {
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+        // This "set" is a no-op
+        assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue();
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+        verifyZeroInteractions(mTestSessionInterface);
+    }
+
+    @Test
+    public void testPolicyOverrideFail() throws Exception {
+        final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+        when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(false);
+        assertThat(mTestSession.setVerificationPolicy(newPolicy)).isFalse();
+        verify(mTestSessionInterface, times(1))
+                .setVerificationPolicy(eq(TEST_ID), eq(newPolicy));
+        // Next "get" should not trigger binder call because the previous "set" has failed
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+        verifyNoMoreInteractions(mTestSessionInterface);
+    }
+
+    @Test
+    public void testPolicyOverrideSuccess() throws Exception {
+        final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+        when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(true);
+        assertThat(mTestSession.setVerificationPolicy(newPolicy)).isTrue();
+        verify(mTestSessionInterface, times(1))
+                .setVerificationPolicy(eq(TEST_ID), eq(newPolicy));
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy);
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy);
+
+        // Setting back to the original policy should still trigger binder calls
+        assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue();
+        verify(mTestSessionInterface, times(1))
+                .setVerificationPolicy(eq(TEST_ID), eq(TEST_POLICY));
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+    }
 }
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
index 7f73a1e..56fc66a 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
@@ -16,6 +16,8 @@
 
 package android.content.pm.verify;
 
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.eq;
@@ -25,6 +27,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.SigningInfo;
+import android.content.pm.verify.pkg.IVerificationSessionInterface;
 import android.content.pm.verify.pkg.IVerifierService;
 import android.content.pm.verify.pkg.VerificationSession;
 import android.content.pm.verify.pkg.VerifierService;
@@ -52,6 +55,7 @@
     private static final String TEST_PACKAGE_NAME = "com.foo";
     private static final Uri TEST_PACKAGE_URI = Uri.parse("test://test");
     private static final SigningInfo TEST_SIGNING_INFO = new SigningInfo();
+    private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
     private VerifierService mService;
     private VerificationSession mSession;
 
@@ -60,8 +64,8 @@
         mService = Mockito.mock(VerifierService.class, Answers.CALLS_REAL_METHODS);
         mSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID,
                 TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO,
-                new ArrayList<>(),
-                new PersistableBundle(), null, null);
+                new ArrayList<>(), new PersistableBundle(), TEST_POLICY, Mockito.mock(
+                IVerificationSessionInterface.class));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index b16c237..be8ecbe 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -470,6 +470,7 @@
 
     @Test
     @SmallTest
+    @DisabledOnRavenwood(blockedBy = ResourcesManager.class)
     public void testResourceConfigurationAppliedWhenOverrideDoesNotExist() {
         final int width = 240;
         final int height = 360;
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 1c00734..6149382 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -9,3 +9,6 @@
 
 # PerformanceHintManager
 per-file PerformanceHintManagerTest.java = file:/ADPF_OWNERS
+
+# Caching
+per-file IpcDataCache* = file:/PERFORMANCE_OWNERS
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 25f9cb7..c7d85d4 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -42,6 +42,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StrikethroughSpan;
 
+import androidx.annotation.NonNull;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -72,6 +73,7 @@
     private static final int STATIC_LINE_COUNT = 9;
     private static final int LINE_HEIGHT = 12;
     private static final int LINE_DESCENT = 4;
+    private static final int LINE_HEIGHT_TOLERANCE_PER_ITERATION = 3;
     private static final CharSequence LAYOUT_TEXT = "alwei\t;sdfs\ndf @";
 
     private SpannableString mSpannedText;
@@ -697,7 +699,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW);
                 expect.that(drawCommand.paint.getBlendMode()).isNotNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -750,7 +752,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW);
                 expect.that(drawCommand.paint.getBlendMode()).isNotNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -802,7 +804,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN);
                 expect.that(drawCommand.paint.getBlendMode()).isNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -855,7 +857,7 @@
 
             if (drawCommand.path != null) {
                 expect.that(drawCommand.path).isEqualTo(selectionPath);
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN);
                 expect.that(drawCommand.paint.getBlendMode()).isNull();
                 highlightsFound++;
             } else if (drawCommand.text != null) {
@@ -914,10 +916,11 @@
 
             if (drawCommand.rect != null) {
                 numBackgroundsFound++;
-                expect.that(drawCommand.paint.getColor()).isEqualTo(Color.BLACK);
+                expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.BLACK);
                 expect.that(drawCommand.rect.height()).isAtLeast(LINE_HEIGHT);
                 expect.that(drawCommand.rect.width()).isGreaterThan(0);
-                float expectedY = (numBackgroundsFound) * (LINE_HEIGHT + LINE_DESCENT);
+                float expectedY = numBackgroundsFound
+                        * (LINE_HEIGHT + LINE_DESCENT - LINE_HEIGHT_TOLERANCE_PER_ITERATION);
                 expect.that(drawCommand.rect.bottom).isAtLeast(expectedY);
             } else if (drawCommand.text != null) {
                 // draw text
@@ -997,20 +1000,38 @@
                 .filter(it -> it.rect != null)
                 .toList();
 
-        expect.that(backgroundCommands.get(0).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(1).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(2).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(3).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(4).paint.getColor()).isEqualTo(Color.WHITE);
-        expect.that(backgroundCommands.get(5).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(6).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(7).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(8).paint.getColor()).isEqualTo(Color.BLACK);
-        expect.that(backgroundCommands.get(9).paint.getColor()).isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(0).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(1).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(2).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(3).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(4).paint.getColor()))
+                .isEqualTo(Color.WHITE);
+        expect.that(removeAlpha(backgroundCommands.get(5).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(6).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(7).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(8).paint.getColor()))
+                .isEqualTo(Color.BLACK);
+        expect.that(removeAlpha(backgroundCommands.get(9).paint.getColor()))
+                .isEqualTo(Color.BLACK);
 
         expect.that(backgroundCommands.size()).isEqualTo(backgroundRectsDrawn);
     }
 
+    private int removeAlpha(int color) {
+        return Color.rgb(
+                Color.red(color),
+                Color.green(color),
+                Color.blue(color)
+        );
+    }
+
     private static final class MockCanvas extends Canvas {
 
         static class DrawCommand {
@@ -1122,6 +1143,11 @@
             mDrawCommands.add(new DrawCommand(rect, p));
         }
 
+        @Override
+        public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
+            mDrawCommands.add(new DrawCommand(rect, paint));
+        }
+
         List<DrawCommand> getDrawCommands() {
             return mDrawCommands;
         }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index ba6f62c..d7f6a29 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -100,14 +100,14 @@
         topConsumer.setControl(
                 new InsetsSourceControl(ID_STATUS_BAR, WindowInsets.Type.statusBars(),
                         mStatusLeash, true, new Point(0, 0), Insets.of(0, 100, 0, 0)),
-                new int[1], new int[1]);
+                new int[1], new int[1], new int[1]);
 
         InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ID_NAVIGATION_BAR,
                 WindowInsets.Type.navigationBars(), mInsetsState, mMockController);
         navConsumer.setControl(
                 new InsetsSourceControl(ID_NAVIGATION_BAR, WindowInsets.Type.navigationBars(),
                         mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)),
-                new int[1], new int[1]);
+                new int[1], new int[1], new int[1]);
         mMockController.setRequestedVisibleTypes(0, WindowInsets.Type.navigationBars());
         navConsumer.applyLocalVisibilityOverride();
 
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index d6d45e8..3a8f7ee 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -117,7 +117,23 @@
         mConsumer.setControl(
                 new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
                         true /* initialVisible */, new Point(), Insets.NONE),
-                new int[1], new int[1]);
+                new int[1], new int[1], new int[1]);
+    }
+
+    @Test
+    public void testSetControl_cancelAnimation() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final InsetsSourceControl newControl = new InsetsSourceControl(mConsumer.getControl());
+
+            // Change the side of the insets hint.
+            newControl.setInsetsHint(Insets.of(0, 0, 0, 100));
+
+            int[] cancelTypes = {0};
+            mConsumer.setControl(newControl, new int[1], new int[1], cancelTypes);
+
+            assertEquals(statusBars(), cancelTypes[0]);
+        });
+
     }
 
     @Test
@@ -180,7 +196,7 @@
     @Test
     public void testRestore() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mConsumer.setControl(null, new int[1], new int[1]);
+            mConsumer.setControl(null, new int[1], new int[1], new int[1]);
             mSurfaceParamsApplied = false;
             mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
             assertFalse(mSurfaceParamsApplied);
@@ -188,7 +204,7 @@
             mConsumer.setControl(
                     new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
                             true /* initialVisible */, new Point(), Insets.NONE),
-                    new int[1], hideTypes);
+                    new int[1], hideTypes, new int[1]);
             assertEquals(statusBars(), hideTypes[0]);
             assertFalse(mRemoveSurfaceCalled);
         });
@@ -198,7 +214,7 @@
     public void testRestore_noAnimation() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
-            mConsumer.setControl(null, new int[1], new int[1]);
+            mConsumer.setControl(null, new int[1], new int[1], new int[1]);
             mLeash = new SurfaceControl.Builder(mSession)
                     .setName("testSurface")
                     .build();
@@ -207,7 +223,7 @@
             mConsumer.setControl(
                     new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
                             false /* initialVisible */, new Point(), Insets.NONE),
-                    new int[1], hideTypes);
+                    new int[1], hideTypes, new int[1]);
             assertTrue(mRemoveSurfaceCalled);
             assertEquals(0, hideTypes[0]);
         });
@@ -235,7 +251,8 @@
 
             // Initial IME insets source control with its leash.
             imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
-                    false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
+                    false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1],
+                    new int[1]);
             mSurfaceParamsApplied = false;
 
             // Verify when the app requests controlling show IME animation, the IME leash
@@ -244,7 +261,8 @@
                     null /* interpolator */, null /* cancellationSignal */, null /* listener */);
             assertEquals(ANIMATION_TYPE_USER, insetsController.getAnimationType(ime()));
             imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
-                    true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
+                    true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1],
+                    new int[1]);
             assertFalse(mSurfaceParamsApplied);
         });
     }
diff --git a/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java b/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
new file mode 100644
index 0000000..262bd5c
--- /dev/null
+++ b/core/tests/coretests/src/android/view/RoundScrollbarRendererTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.flags.Flags;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link RoundScrollbarRenderer}.
+ *
+ * <p>Build/Install/Run: atest FrameworksCoreTests:android.view.RoundScrollbarRendererTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RoundScrollbarRendererTest {
+
+    private static final int DEFAULT_VERTICAL_SCROLL_RANGE = 100;
+    private static final int DEFAULT_VERTICAL_SCROLL_EXTENT = 20;
+    private static final int DEFAULT_VERTICAL_SCROLL_OFFSET = 40;
+    private static final float DEFAULT_ALPHA = 0.5f;
+    private static final Rect BOUNDS = new Rect(0, 0, 200, 200);
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Mock private Canvas mCanvas;
+    @Captor private ArgumentCaptor<Paint> mPaintCaptor;
+    private RoundScrollbarRenderer mScrollbar;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        MockView view = spy(new MockView(ApplicationProvider.getApplicationContext()));
+        when(view.canScrollVertically(anyInt())).thenReturn(true);
+        when(view.computeVerticalScrollRange()).thenReturn(DEFAULT_VERTICAL_SCROLL_RANGE);
+        when(view.computeVerticalScrollExtent()).thenReturn(DEFAULT_VERTICAL_SCROLL_EXTENT);
+        when(view.computeVerticalScrollOffset()).thenReturn(DEFAULT_VERTICAL_SCROLL_OFFSET);
+        mPaintCaptor = ArgumentCaptor.forClass(Paint.class);
+
+        mScrollbar = new RoundScrollbarRenderer(view);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_USE_REFACTORED_ROUND_SCROLLBAR)
+    public void testScrollbarDrawn_legacy() {
+        mScrollbar.drawRoundScrollbars(mCanvas, DEFAULT_ALPHA, BOUNDS, /* drawToLeft= */ false);
+
+        // The arc will be drawn twice, i.e. once for track and once for thumb
+        verify(mCanvas, times(2))
+                .drawArc(any(), anyFloat(), anyFloat(), eq(false), mPaintCaptor.capture());
+
+        Paint thumbPaint = mPaintCaptor.getAllValues().getFirst();
+        assertEquals(Paint.Cap.ROUND, thumbPaint.getStrokeCap());
+        assertEquals(Paint.Style.STROKE, thumbPaint.getStyle());
+        Paint trackPaint = mPaintCaptor.getAllValues().get(1);
+        assertEquals(Paint.Cap.ROUND, trackPaint.getStrokeCap());
+        assertEquals(Paint.Style.STROKE, trackPaint.getStyle());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_USE_REFACTORED_ROUND_SCROLLBAR)
+    public void testScrollbarDrawn() {
+        mScrollbar.drawRoundScrollbars(mCanvas, DEFAULT_ALPHA, BOUNDS, /* drawToLeft= */ false);
+
+        // The arc will be drawn thrice, i.e. twice for track and once for thumb
+        verify(mCanvas, times(3))
+                .drawArc(any(), anyFloat(), anyFloat(), eq(false), mPaintCaptor.capture());
+
+        // Verify paint styles
+        Paint thumbPaint = mPaintCaptor.getAllValues().getFirst();
+        assertEquals(Paint.Cap.ROUND, thumbPaint.getStrokeCap());
+        assertEquals(Paint.Style.STROKE, thumbPaint.getStyle());
+        Paint trackPaint = mPaintCaptor.getAllValues().get(1);
+        assertEquals(Paint.Cap.ROUND, trackPaint.getStrokeCap());
+        assertEquals(Paint.Style.STROKE, trackPaint.getStyle());
+    }
+
+    public static class MockView extends View {
+
+        public MockView(Context context) {
+            super(context);
+        }
+
+        @Override
+        public int computeVerticalScrollRange() {
+            return super.getHeight();
+        }
+
+        @Override
+        public int computeVerticalScrollOffset() {
+            return super.computeVerticalScrollOffset();
+        }
+
+        @Override
+        public int computeVerticalScrollExtent() {
+            return super.computeVerticalScrollExtent();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index c981423..483ebc2 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_ANIMATION_BUGFIX_25Q1;
 import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY;
+import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_TOUCH_BOOST_25Q1;
 import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
@@ -118,6 +119,67 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_TOUCH_BOOST_25Q1)
+    public void shouldNotSuppressTouchBoost() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
+
+        mActivityRule.runOnUiThread(() -> {
+            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
+            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+            mMovingView.setLayoutParams(layoutParams);
+            mMovingView.setOnClickListener((v) -> {});
+        });
+        waitForFrameRateCategoryToSettle();
+
+        int[] position = new int[2];
+        mActivityRule.runOnUiThread(() -> {
+            mMovingView.getLocationOnScreen(position);
+            position[0] += mMovingView.getWidth() / 2;
+            position[1] += mMovingView.getHeight() / 2;
+        });
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+        // update the window type to TYPE_INPUT_METHOD
+        int windowType = mViewRoot.mWindowAttributes.type;
+        final WindowManager.LayoutParams attrs = mViewRoot.mWindowAttributes;
+        attrs.type = TYPE_INPUT_METHOD;
+        instrumentation.runOnMainSync(() -> {
+            mViewRoot.setLayoutParams(attrs, false);
+        });
+        instrumentation.waitForIdleSync();
+
+        final WindowManager.LayoutParams newAttrs = mViewRoot.mWindowAttributes;
+        assertTrue(newAttrs.type == TYPE_INPUT_METHOD);
+
+        long now = SystemClock.uptimeMillis();
+        MotionEvent down = MotionEvent.obtain(
+                now, // downTime
+                now, // eventTime
+                MotionEvent.ACTION_DOWN, // action
+                position[0], // x
+                position[1], // y
+                0 // metaState
+        );
+        down.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        instrumentation.sendPointerSync(down);
+        down.recycle();
+
+        // should have touch boost
+        assertTrue(mViewRoot.getIsTouchBoosting());
+
+        // Reset the window type back to the original one.
+        newAttrs.type = windowType;
+        instrumentation.runOnMainSync(() -> {
+            mViewRoot.setLayoutParams(newAttrs, false);
+        });
+        instrumentation.waitForIdleSync();
+        assertTrue(mViewRoot.mWindowAttributes.type == windowType);
+    }
+
+    @Test
     @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY)
     public void inputMethodWithContentMoves() throws Throwable {
         if (!ViewProperties.vrr_enabled().orElse(true)) {
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 6327211..ed9fc1c 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -71,6 +71,7 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
 import android.os.SystemProperties;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -82,6 +83,7 @@
 import android.util.Log;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Type;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -1628,6 +1630,42 @@
         });
     }
 
+    @Test
+    @EnableFlags(android.view.accessibility.Flags.FLAG_FOCUS_RECT_MIN_SIZE)
+    public void testAdjustAccessibilityFocusedBounds_largeEnoughBoundsAreUnchanged() {
+        final int strokeWidth = sContext.getSystemService(AccessibilityManager.class)
+                .getAccessibilityFocusStrokeWidth();
+        final int left, top, width, height;
+        left = top = 100;
+        width = height = strokeWidth * 2;
+        final Rect bounds = new Rect(left, top, left + width, top + height);
+        final Rect originalBounds = new Rect(bounds);
+
+        mViewRootImpl.adjustAccessibilityFocusedRectBoundsIfNeeded(bounds);
+
+        assertThat(bounds).isEqualTo(originalBounds);
+    }
+
+    @Test
+    @EnableFlags(android.view.accessibility.Flags.FLAG_FOCUS_RECT_MIN_SIZE)
+    public void testAdjustAccessibilityFocusedBounds_smallBoundsAreExpanded() {
+        final int strokeWidth = sContext.getSystemService(AccessibilityManager.class)
+                .getAccessibilityFocusStrokeWidth();
+        final int left, top, width, height;
+        left = top = 100;
+        width = height = strokeWidth;
+        final Rect bounds = new Rect(left, top, left + width, top + height);
+        final Rect originalBounds = new Rect(bounds);
+
+        mViewRootImpl.adjustAccessibilityFocusedRectBoundsIfNeeded(bounds);
+
+        // Bounds should be centered on the same point, but expanded to at least strokeWidth * 2
+        assertThat(bounds.centerX()).isEqualTo(originalBounds.centerX());
+        assertThat(bounds.centerY()).isEqualTo(originalBounds.centerY());
+        assertThat(bounds.width()).isAtLeast(strokeWidth * 2);
+        assertThat(bounds.height()).isAtLeast(strokeWidth * 2);
+    }
+
     private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
         try {
             SystemProperties.set(
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 6e563ff..da202b6 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
     // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
     // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
     // and assertAccessibilityNodeInfoCleared in that class.
-    private static final int NUM_MARSHALLED_PROPERTIES = 44;
+    private static final int NUM_MARSHALLED_PROPERTIES = 45;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 0621d82..2880ecf 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import static android.appwidget.flags.Flags.remoteAdapterConversion;
+
 import static com.android.internal.R.id.pending_intent_tag;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -282,7 +284,10 @@
         widget.addView(view);
 
         ListView listView = (ListView) view.findViewById(R.id.list);
-        listView.onRemoteAdapterConnected();
+
+        if (!remoteAdapterConversion()) {
+            listView.onRemoteAdapterConnected();
+        }
         assertNotNull(listView.getAdapter());
 
         top.reapply(mContext, view);
@@ -414,6 +419,58 @@
         assertNotNull(view.findViewById(R.id.light_background_text));
     }
 
+    @Test
+    public void remoteCollectionItemsAdapter_lightBackgroundLayoutFlagSet() {
+        AppWidgetHostView widget = new AppWidgetHostView(mContext);
+        RemoteViews container = new RemoteViews(mPackage, R.layout.remote_view_host);
+        RemoteViews listRemoteViews = new RemoteViews(mPackage, R.layout.remote_views_list);
+        RemoteViews item = new RemoteViews(mPackage, R.layout.remote_views_text);
+        item.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+        listRemoteViews.setRemoteAdapter(
+                R.id.list,
+                new RemoteViews.RemoteCollectionItems.Builder().addItem(0, item).build());
+        container.addView(R.id.container, listRemoteViews);
+
+        widget.setOnLightBackground(true);
+        widget.updateAppWidget(container);
+
+        // Populate the list view
+        ListView listView = (ListView) widget.findViewById(R.id.list);
+        int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
+        listView.measure(measureSpec, measureSpec);
+        listView.layout(0, 0, 100, 100);
+
+        // Picks the light background layout id for the item
+        assertNotNull(listView.getChildAt(0).findViewById(R.id.light_background_text));
+        assertNull(listView.getChildAt(0).findViewById(R.id.text));
+    }
+
+    @Test
+    public void remoteCollectionItemsAdapter_lightBackgroundLayoutFlagNotSet() {
+        AppWidgetHostView widget = new AppWidgetHostView(mContext);
+        RemoteViews container = new RemoteViews(mPackage, R.layout.remote_view_host);
+        RemoteViews listRemoteViews = new RemoteViews(mPackage, R.layout.remote_views_list);
+        RemoteViews item = new RemoteViews(mPackage, R.layout.remote_views_text);
+        item.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+        listRemoteViews.setRemoteAdapter(
+                R.id.list,
+                new RemoteViews.RemoteCollectionItems.Builder().addItem(0, item).build());
+        container.addView(R.id.container, listRemoteViews);
+
+        widget.setOnLightBackground(false);
+        widget.updateAppWidget(container);
+
+        // Populate the list view
+        ListView listView = (ListView) widget.findViewById(R.id.list);
+        int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
+        listView.measure(measureSpec, measureSpec);
+        listView.layout(0, 0, 100, 100);
+
+        // Does not pick the light background layout id for the item
+        assertNotNull(listView.getChildAt(0).findViewById(R.id.text));
+        assertNull(listView.getChildAt(0).findViewById(R.id.light_background_text));
+    }
+
     private RemoteViews createViewChained(int depth, String... texts) {
         RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host);
 
diff --git a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
similarity index 88%
rename from core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
rename to core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
index a311d0d..b28e2b0 100644
--- a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
+++ b/core/tests/coretests/src/android/window/DesktopModeFlagsTest.java
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-package android.window.flags;
+package android.window;
 
-import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE;
-import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF;
-import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_ON;
-import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET;
-import static android.window.flags.DesktopModeFlags.ToggleOverride.fromSetting;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE;
+import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF;
+import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_ON;
+import static android.window.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET;
+import static android.window.DesktopModeFlags.ToggleOverride.fromSetting;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS;
 
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS;
@@ -40,6 +41,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -48,7 +50,7 @@
 import java.lang.reflect.Field;
 
 /**
- * Test class for {@link DesktopModeFlags}
+ * Test class for {@link android.window.DesktopModeFlags}
  *
  * Build/Install/Run:
  * atest FrameworksCoreTests:DesktopModeFlagsTest
@@ -68,8 +70,12 @@
     private static final int OVERRIDE_UNSET_SETTING = -1;
 
     @Before
-    public void setUp() throws Exception {
+    public void setUp() {
         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
+
+    @After
+    public void tearDown() throws Exception {
         resetCache();
     }
 
@@ -203,7 +209,7 @@
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
@@ -212,7 +218,7 @@
     public void isTrue_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_UNSET_SETTING);
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
@@ -225,7 +231,7 @@
         setOverride(OVERRIDE_ON_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
@@ -235,7 +241,7 @@
         setOverride(OVERRIDE_ON_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
@@ -248,7 +254,7 @@
         setOverride(OVERRIDE_OFF_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
@@ -258,7 +264,7 @@
         setOverride(OVERRIDE_OFF_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
@@ -271,7 +277,7 @@
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
@@ -284,7 +290,7 @@
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
@@ -297,7 +303,7 @@
         setOverride(OVERRIDE_ON_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
@@ -310,7 +316,7 @@
         setOverride(OVERRIDE_ON_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
@@ -323,7 +329,7 @@
         setOverride(OVERRIDE_OFF_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
@@ -336,7 +342,7 @@
         setOverride(OVERRIDE_OFF_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
@@ -375,5 +381,6 @@
                 "sCachedToggleOverride");
         cachedToggleOverride.setAccessible(true);
         cachedToggleOverride.set(null, null);
+        setOverride(OVERRIDE_UNSET_SETTING);
     }
 }
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index b001cc2..46bd73e 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -21,6 +21,7 @@
 import static android.window.OnBackInvokedDispatcher.PRIORITY_SYSTEM_NAVIGATION_OBSERVER;
 
 import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER;
+import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -486,6 +487,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(FLAG_PREDICTIVE_BACK_TIMESTAMP_API)
     public void onBackInvoked_notCalledAfterCallbackUnregistration()
             throws RemoteException, InterruptedException {
         // Setup a callback that unregisters itself after the gesture is finished but before the
@@ -684,6 +686,7 @@
         return new BackMotionEvent(
                 /* touchX = */ 0,
                 /* touchY = */ 0,
+                /* frameTime = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ false,
                 /* swipeEdge = */ BackEvent.EDGE_LEFT,
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java
index 362eeea..8e906fd 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityServiceWarningTest.java
@@ -25,6 +25,8 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
@@ -33,6 +35,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.Window;
+import android.view.accessibility.Flags;
 import android.widget.TextView;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -89,7 +92,19 @@
     }
 
     @Test
-    public void createAccessibilityServiceWarningDialog_hasExpectedWindowParams() {
+    @RequiresFlagsDisabled(Flags.FLAG_WARNING_USE_DEFAULT_DIALOG_TYPE)
+    public void createAccessibilityServiceWarningDialog_hasExpectedWindowParams_isSystemDialog() {
+        createAccessibilityServiceWarningDialog_hasExpectedWindowParams(true);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_WARNING_USE_DEFAULT_DIALOG_TYPE)
+    public void createAccessibilityServiceWarningDialog_hasExpectedWindowParams_notSystemDialog() {
+        createAccessibilityServiceWarningDialog_hasExpectedWindowParams(false);
+    }
+
+    private void createAccessibilityServiceWarningDialog_hasExpectedWindowParams(
+            boolean expectSystemDialog) {
         final AlertDialog dialog =
                 AccessibilityServiceWarning.createAccessibilityServiceWarningDialog(
                         mContext,
@@ -101,7 +116,11 @@
         expect.that(dialogWindow.getAttributes().privateFlags
                 & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(
                 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-        expect.that(dialogWindow.getAttributes().type).isEqualTo(TYPE_SYSTEM_DIALOG);
+        if (expectSystemDialog) {
+            expect.that(dialogWindow.getAttributes().type).isEqualTo(TYPE_SYSTEM_DIALOG);
+        } else {
+            expect.that(dialogWindow.getAttributes().type).isNotEqualTo(TYPE_SYSTEM_DIALOG);
+        }
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
new file mode 100644
index 0000000..79a478a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification.ProgressStyle;
+import android.graphics.Color;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.widget.NotificationProgressDrawable.Part;
+import com.android.internal.widget.NotificationProgressDrawable.Point;
+import com.android.internal.widget.NotificationProgressDrawable.Segment;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class NotificationProgressBarTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_segmentsIsEmpty() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_segmentLengthIsNegative() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(-50));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_segmentLengthIsZero() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(0));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_progressIsNegative() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = -50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test
+    public void processAndConvertToDrawableParts_progressIsZero() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 0;
+        boolean isStyledByProgress = true;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
+                segments, points, progress, isStyledByProgress);
+
+        int fadedRed = 0x7FFF0000;
+        List<Part> expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true)));
+
+        assertThat(parts).isEqualTo(expected);
+    }
+
+    @Test
+    public void processAndConvertToDrawableParts_progressAtMax() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 100;
+        boolean isStyledByProgress = true;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
+                segments, points, progress, isStyledByProgress);
+
+        List<Part> expected = new ArrayList<>(List.of(new Segment(1f, Color.RED)));
+
+        assertThat(parts).isEqualTo(expected);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_progressAboveMax() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 150;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_pointPositionIsNegative() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(-50).setColor(Color.RED));
+        int progress = 50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_pointPositionIsZero() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(0).setColor(Color.RED));
+        int progress = 50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_pointPositionAtMax() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(100).setColor(Color.RED));
+        int progress = 50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void processAndConvertToDrawableParts_pointPositionAboveMax() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(150).setColor(Color.RED));
+        int progress = 50;
+        boolean isStyledByProgress = true;
+
+        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+                isStyledByProgress);
+    }
+
+    @Test
+    public void processAndConvertToDrawableParts_multipleSegmentsWithoutPoints() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 60;
+        boolean isStyledByProgress = true;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
+                segments, points, progress, isStyledByProgress);
+
+        // Colors with 50% opacity
+        int fadedGreen = 0x7F00FF00;
+
+        List<Part> expected = new ArrayList<>(List.of(
+                new Segment(0.50f, Color.RED),
+                new Segment(0.10f, Color.GREEN),
+                new Segment(0.40f, fadedGreen, true)));
+
+        assertThat(parts).isEqualTo(expected);
+    }
+
+    @Test
+    public void processAndConvertToDrawableParts_singleSegmentWithPoints() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100).setColor(Color.BLUE));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(15).setColor(Color.RED));
+        points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
+        int progress = 60;
+        boolean isStyledByProgress = true;
+
+        // Colors with 50% opacity
+        int fadedBlue = 0x7F0000FF;
+        int fadedYellow = 0x7FFFFF00;
+
+        List<Part> expected = new ArrayList<>(List.of(
+                new Segment(0.15f, Color.BLUE),
+                new Point(null, Color.RED),
+                new Segment(0.10f, Color.BLUE),
+                new Point(null, Color.BLUE),
+                new Segment(0.35f, Color.BLUE),
+                new Point(null, Color.BLUE),
+                new Segment(0.15f, fadedBlue, true),
+                new Point(null, fadedYellow, true),
+                new Segment(0.25f, fadedBlue, true)));
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
+                segments, points, progress, isStyledByProgress);
+
+        assertThat(parts).isEqualTo(expected);
+    }
+
+    @Test
+    public void processAndConvertToDrawableParts_multipleSegmentsWithPoints() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(15).setColor(Color.RED));
+        points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
+        int progress = 60;
+        boolean isStyledByProgress = true;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
+                segments, points, progress, isStyledByProgress);
+
+        // Colors with 50% opacity
+        int fadedGreen = 0x7F00FF00;
+        int fadedYellow = 0x7FFFFF00;
+
+        List<Part> expected = new ArrayList<>(List.of(
+                new Segment(0.15f, Color.RED),
+                new Point(null, Color.RED),
+                new Segment(0.10f, Color.RED),
+                new Point(null, Color.BLUE),
+                new Segment(0.25f, Color.RED),
+                new Segment(0.10f, Color.GREEN),
+                new Point(null, Color.BLUE),
+                new Segment(0.15f, fadedGreen, true),
+                new Point(null, fadedYellow, true),
+                new Segment(0.25f, fadedGreen, true)));
+
+        assertThat(parts).isEqualTo(expected);
+    }
+
+    @Test
+    public void processAndConvertToDrawableParts_multipleSegmentsWithPoints_notStyledByProgress() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(15).setColor(Color.RED));
+        points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
+        points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
+        int progress = 60;
+        boolean isStyledByProgress = false;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
+                segments, points, progress, isStyledByProgress);
+
+        List<Part> expected = new ArrayList<>(List.of(
+                new Segment(0.15f, Color.RED),
+                new Point(null, Color.RED),
+                new Segment(0.10f, Color.RED),
+                new Point(null, Color.BLUE),
+                new Segment(0.25f, Color.RED),
+                new Segment(0.25f, Color.GREEN),
+                new Point(null, Color.YELLOW),
+                new Segment(0.25f, Color.GREEN)));
+
+        assertThat(parts).isEqualTo(expected);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressModelTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressModelTest.java
new file mode 100644
index 0000000..962399e
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressModelTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Flags;
+import android.app.Notification;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
+@SmallTest
+@EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+public class NotificationProgressModelTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Test(expected = IllegalArgumentException.class)
+    public void throw_exception_on_transparent_indeterminate_color() {
+        new NotificationProgressModel(Color.TRANSPARENT);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void throw_exception_on_empty_segments() {
+        new NotificationProgressModel(List.of(),
+                List.of(),
+                10,
+                false);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void throw_exception_on_negative_progress() {
+        new NotificationProgressModel(
+                List.of(new Notification.ProgressStyle.Segment(50).setColor(Color.YELLOW)),
+                List.of(),
+                -1,
+                false);
+    }
+
+    @Test
+    public void save_and_restore_indeterminate_progress_model() {
+        // GIVEN
+        final NotificationProgressModel savedModel = new NotificationProgressModel(Color.RED);
+        final Bundle bundle = savedModel.toBundle();
+
+        // WHEN
+        final NotificationProgressModel restoredModel =
+                NotificationProgressModel.fromBundle(bundle);
+
+        // THEN
+        assertThat(restoredModel.getIndeterminateColor()).isEqualTo(Color.RED);
+        assertThat(restoredModel.isIndeterminate()).isTrue();
+        assertThat(restoredModel.getProgress()).isEqualTo(-1);
+        assertThat(restoredModel.getSegments()).isEmpty();
+        assertThat(restoredModel.getPoints()).isEmpty();
+        assertThat(restoredModel.isStyledByProgress()).isFalse();
+    }
+
+    @Test
+    public void save_and_restore_non_indeterminate_progress_model() {
+        // GIVEN
+        final List<Notification.ProgressStyle.Segment> segments = List.of(
+                new Notification.ProgressStyle.Segment(50).setColor(Color.YELLOW),
+                new Notification.ProgressStyle.Segment(50).setColor(Color.LTGRAY));
+        final List<Notification.ProgressStyle.Point> points = List.of(
+                new Notification.ProgressStyle.Point(0).setColor(Color.RED),
+                new Notification.ProgressStyle.Point(20).setColor(Color.BLUE));
+        final NotificationProgressModel savedModel = new NotificationProgressModel(segments,
+                points,
+                100,
+                true);
+
+        final Bundle bundle = savedModel.toBundle();
+
+        // WHEN
+        final NotificationProgressModel restoredModel =
+                NotificationProgressModel.fromBundle(bundle);
+
+        // THEN
+        assertThat(restoredModel.isIndeterminate()).isFalse();
+        assertThat(restoredModel.getSegments()).isEqualTo(segments);
+        assertThat(restoredModel.getPoints()).isEqualTo(points);
+        assertThat(restoredModel.getProgress()).isEqualTo(100);
+        assertThat(restoredModel.isStyledByProgress()).isTrue();
+        assertThat(restoredModel.getIndeterminateColor()).isEqualTo(-1);
+    }
+}
diff --git a/core/tests/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp
index a3303c6..e573a51 100644
--- a/core/tests/devicestatetests/Android.bp
+++ b/core/tests/devicestatetests/Android.bp
@@ -26,11 +26,14 @@
     // Include all test java files
     srcs: ["src/**/*.java"],
     static_libs: [
+        "androidx.test.ext.junit",
         "androidx.test.rules",
+        "flag-junit",
         "frameworks-base-testutils",
         "mockito-target-minus-junit4",
+        "platform-parametric-runner-lib",
         "platform-test-annotations",
-        "testng",
+        "truth",
     ],
     libs: ["android.test.runner.stubs.system"],
     platform_apis: true,
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java
index cf7c549..1b78433 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java
@@ -22,21 +22,15 @@
 import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
 import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -44,13 +38,13 @@
 
 /**
  * Unit tests for {@link DeviceStateInfo}.
- * <p/>
- * Run with <code>atest DeviceStateInfoTest</code>.
+ *
+ * <p> Build/Install/Run:
+ * atest FrameworksCoreDeviceStateManagerTests:DeviceStateInfoTest
  */
-@RunWith(JUnit4.class)
 @SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class DeviceStateInfoTest {
-
     private static final DeviceState DEVICE_STATE_0 = new DeviceState(
             new DeviceState.Configuration.Builder(0, "STATE_0")
                     .setSystemProperties(
@@ -74,88 +68,113 @@
 
     @Test
     public void create() {
-        final ArrayList<DeviceState> supportedStates = new ArrayList<>(
-                List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
+        final ArrayList<DeviceState> supportedStates =
+                new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
         final DeviceState baseState = DEVICE_STATE_0;
         final DeviceState currentState = DEVICE_STATE_2;
 
-        final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
-        assertNotNull(info.supportedStates);
-        assertEquals(supportedStates, info.supportedStates);
-        assertEquals(baseState, info.baseState);
-        assertEquals(currentState, info.currentState);
+        final DeviceStateInfo info =
+                new DeviceStateInfo(supportedStates, baseState, currentState);
+
+        assertThat(info.supportedStates).containsExactlyElementsIn(supportedStates).inOrder();
+        assertThat(info.baseState).isEqualTo(baseState);
+        assertThat(info.currentState).isEqualTo(currentState);
     }
 
     @Test
     public void equals() {
-        final ArrayList<DeviceState> supportedStates = new ArrayList<>(
-                List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
+        final ArrayList<DeviceState> supportedStates =
+                new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
         final DeviceState baseState = DEVICE_STATE_0;
         final DeviceState currentState = DEVICE_STATE_2;
 
         final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
-        Assert.assertEquals(info, info);
+        final DeviceStateInfo sameInstance = info;
+        assertThat(info).isEqualTo(sameInstance);
 
-        final DeviceStateInfo sameInfo = new DeviceStateInfo(supportedStates, baseState,
-                currentState);
-        Assert.assertEquals(info, sameInfo);
+        final DeviceStateInfo sameInfo =
+                new DeviceStateInfo(supportedStates, baseState, currentState);
+        assertThat(info).isEqualTo(sameInfo);
+
+        final DeviceStateInfo copiedInfo = new DeviceStateInfo(info);
+        assertThat(info).isEqualTo(copiedInfo);
 
         final DeviceStateInfo differentInfo = new DeviceStateInfo(
                 new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_2)), baseState, currentState);
-        assertNotEquals(info, differentInfo);
+        assertThat(differentInfo).isNotEqualTo(info);
+    }
+
+    @Test
+    public void hashCode_sameObject() {
+        final ArrayList<DeviceState> supportedStates =
+                new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
+        final DeviceState baseState = DEVICE_STATE_0;
+        final DeviceState currentState = DEVICE_STATE_2;
+        final DeviceStateInfo info =
+                new DeviceStateInfo(supportedStates, baseState, currentState);
+        final DeviceStateInfo copiedInfo = new DeviceStateInfo(info);
+
+        assertThat(info.hashCode()).isEqualTo(copiedInfo.hashCode());
     }
 
     @Test
     public void diff_sameObject() {
-        final ArrayList<DeviceState> supportedStates = new ArrayList<>(
-                List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
+        final ArrayList<DeviceState> supportedStates =
+                new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
         final DeviceState baseState = DEVICE_STATE_0;
         final DeviceState currentState = DEVICE_STATE_2;
 
         final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
-        assertEquals(0, info.diff(info));
+
+        assertThat(info.diff(info)).isEqualTo(0);
     }
 
     @Test
     public void diff_differentSupportedStates() {
-        final DeviceStateInfo info = new DeviceStateInfo(new ArrayList<>(List.of(DEVICE_STATE_1)),
-                DEVICE_STATE_0, DEVICE_STATE_0);
+        final DeviceStateInfo info = new DeviceStateInfo(
+                new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_0, DEVICE_STATE_0);
         final DeviceStateInfo otherInfo = new DeviceStateInfo(
                 new ArrayList<>(List.of(DEVICE_STATE_2)), DEVICE_STATE_0, DEVICE_STATE_0);
+
         final int diff = info.diff(otherInfo);
-        assertTrue((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
-        assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
-        assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+
+        assertThat(diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES).isGreaterThan(0);
+        assertThat(diff & DeviceStateInfo.CHANGED_BASE_STATE).isEqualTo(0);
+        assertThat(diff & DeviceStateInfo.CHANGED_CURRENT_STATE).isEqualTo(0);
     }
 
     @Test
     public void diff_differentNonOverrideState() {
-        final DeviceStateInfo info = new DeviceStateInfo(new ArrayList<>(List.of(DEVICE_STATE_1)),
-                DEVICE_STATE_1, DEVICE_STATE_0);
+        final DeviceStateInfo info = new DeviceStateInfo(
+                new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_1, DEVICE_STATE_0);
         final DeviceStateInfo otherInfo = new DeviceStateInfo(
                 new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_2, DEVICE_STATE_0);
+
         final int diff = info.diff(otherInfo);
-        assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
-        assertTrue((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
-        assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+
+        assertThat(diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES).isEqualTo(0);
+        assertThat(diff & DeviceStateInfo.CHANGED_BASE_STATE).isGreaterThan(0);
+        assertThat(diff & DeviceStateInfo.CHANGED_CURRENT_STATE).isEqualTo(0);
     }
 
     @Test
     public void diff_differentState() {
-        final DeviceStateInfo info = new DeviceStateInfo(new ArrayList<>(List.of(DEVICE_STATE_1)),
-                DEVICE_STATE_0, DEVICE_STATE_1);
+        final DeviceStateInfo info = new DeviceStateInfo(
+                new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_0, DEVICE_STATE_1);
         final DeviceStateInfo otherInfo = new DeviceStateInfo(
                 new ArrayList<>(List.of(DEVICE_STATE_1)), DEVICE_STATE_0, DEVICE_STATE_2);
+
         final int diff = info.diff(otherInfo);
-        assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
-        assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
-        assertTrue((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+
+        assertThat(diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES).isEqualTo(0);
+        assertThat(diff & DeviceStateInfo.CHANGED_BASE_STATE).isEqualTo(0);
+        assertThat(diff & DeviceStateInfo.CHANGED_CURRENT_STATE).isGreaterThan(0);
     }
 
     @Test
     public void writeToParcel() {
-        final ArrayList<DeviceState> supportedStates = new ArrayList<>(
-                List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
+        final ArrayList<DeviceState> supportedStates =
+                new ArrayList<>(List.of(DEVICE_STATE_0, DEVICE_STATE_1, DEVICE_STATE_2));
         final DeviceState nonOverrideState = DEVICE_STATE_0;
         final DeviceState state = DEVICE_STATE_2;
         final DeviceStateInfo originalInfo =
@@ -166,6 +185,6 @@
         parcel.setDataPosition(0);
 
         final DeviceStateInfo info = DeviceStateInfo.CREATOR.createFromParcel(parcel);
-        assertEquals(originalInfo, info);
+        assertThat(info).isEqualTo(originalInfo);
     }
 }
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index f4d3631..e640ce5 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -16,11 +16,12 @@
 
 package android.hardware.devicestate;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
+import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -30,49 +31,104 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.ConcurrentUtils;
+import com.android.window.flags.Flags;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 /**
  * Unit tests for {@link DeviceStateManagerGlobal}.
- * <p/>
- * Run with <code>atest DeviceStateManagerGlobalTest</code>.
+ *
+ * <p> Build/Install/Run:
+ * atest FrameworksCoreDeviceStateManagerTests:DeviceStateManagerGlobalTest
  */
-@RunWith(JUnit4.class)
 @SmallTest
+@RunWith(ParameterizedAndroidJunit4.class)
 public final class DeviceStateManagerGlobalTest {
     private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(
             new DeviceState.Configuration.Builder(0 /* identifier */, "" /* name */).build());
     private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(
             new DeviceState.Configuration.Builder(1 /* identifier */, "" /* name */).build());
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule;
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(Flags.FLAG_WLINFO_ONCREATE);
+    }
+
+    @NonNull
     private TestDeviceStateManagerService mService;
+    @NonNull
     private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
 
+    public DeviceStateManagerGlobalTest(FlagsParameterization flags) {
+        mSetFlagsRule = new SetFlagsRule(flags);
+    }
+
     @Before
     public void setUp() {
-        FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer();
+        final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer();
         mService = new TestDeviceStateManagerService(permissionEnforcer);
         mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
-        assertFalse(mService.mCallbacks.isEmpty());
+        assertThat(mService.mCallbacks).isNotEmpty();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_WLINFO_ONCREATE)
+    public void create_whenWlinfoOncreateIsDisabled_receivesDeviceStateInfoFromCallback() {
+        final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer();
+        final TestDeviceStateManagerService service = new TestDeviceStateManagerService(
+                permissionEnforcer, true /* simulatePostCallback */);
+        final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service);
+        final DeviceStateCallback callback = mock(DeviceStateCallback.class);
+        dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+
+        verify(callback, never()).onDeviceStateChanged(any());
+
+        // Simulate DeviceStateManagerService#registerProcess by notifying clients of current device
+        // state via callback.
+        service.notifyDeviceStateInfoChanged();
+        verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_WLINFO_ONCREATE)
+    public void create_whenWlinfoOncreateIsEnabled_returnsDeviceStateInfoFromRegistration() {
+        final FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer();
+        final IDeviceStateManager service = new TestDeviceStateManagerService(permissionEnforcer);
+        final DeviceStateManagerGlobal dsmGlobal = new DeviceStateManagerGlobal(service);
+        final DeviceStateCallback callback = mock(DeviceStateCallback.class);
+        dsmGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
+
+        verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE));
     }
 
     @Test
     public void registerCallback() {
-        DeviceStateCallback callback1 = mock(DeviceStateCallback.class);
-        DeviceStateCallback callback2 = mock(DeviceStateCallback.class);
+        final DeviceStateCallback callback1 = mock(DeviceStateCallback.class);
+        final DeviceStateCallback callback2 = mock(DeviceStateCallback.class);
 
         mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1,
                 ConcurrentUtils.DIRECT_EXECUTOR);
@@ -105,8 +161,8 @@
         reset(callback2);
 
         // Change the requested state and verify callback
-        DeviceStateRequest request = DeviceStateRequest.newBuilder(
-                DEFAULT_DEVICE_STATE.getIdentifier()).build();
+        final DeviceStateRequest request =
+                DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build();
         mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
 
         verify(callback1).onDeviceStateChanged(eq(mService.getMergedState()));
@@ -115,10 +171,10 @@
 
     @Test
     public void unregisterCallback() {
-        DeviceStateCallback callback = mock(DeviceStateCallback.class);
+        final DeviceStateCallback callback = mock(DeviceStateCallback.class);
 
-        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
-                ConcurrentUtils.DIRECT_EXECUTOR);
+        mDeviceStateManagerGlobal
+                .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
 
         // Verify initial callbacks
         verify(callback).onSupportedStatesChanged(eq(mService.getSupportedDeviceStates()));
@@ -134,15 +190,15 @@
 
     @Test
     public void submitRequest() {
-        DeviceStateCallback callback = mock(DeviceStateCallback.class);
-        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
-                ConcurrentUtils.DIRECT_EXECUTOR);
+        final DeviceStateCallback callback = mock(DeviceStateCallback.class);
+        mDeviceStateManagerGlobal
+                .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
 
         verify(callback).onDeviceStateChanged(eq(mService.getBaseState()));
         reset(callback);
 
-        DeviceStateRequest request = DeviceStateRequest.newBuilder(
-                OTHER_DEVICE_STATE.getIdentifier()).build();
+        final DeviceStateRequest request =
+                DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build();
         mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
 
         verify(callback).onDeviceStateChanged(eq(OTHER_DEVICE_STATE));
@@ -155,15 +211,15 @@
 
     @Test
     public void submitBaseStateOverrideRequest() {
-        DeviceStateCallback callback = mock(DeviceStateCallback.class);
-        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
-                ConcurrentUtils.DIRECT_EXECUTOR);
+        final DeviceStateCallback callback = mock(DeviceStateCallback.class);
+        mDeviceStateManagerGlobal
+                .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
 
         verify(callback).onDeviceStateChanged(eq(mService.getBaseState()));
         reset(callback);
 
-        DeviceStateRequest request = DeviceStateRequest.newBuilder(
-                OTHER_DEVICE_STATE.getIdentifier()).build();
+        final DeviceStateRequest request =
+                DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build();
         mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
                 null /* callback */);
 
@@ -177,28 +233,28 @@
 
     @Test
     public void submitBaseAndEmulatedStateOverride() {
-        DeviceStateCallback callback = mock(DeviceStateCallback.class);
-        mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
-                ConcurrentUtils.DIRECT_EXECUTOR);
+        final DeviceStateCallback callback = mock(DeviceStateCallback.class);
+        mDeviceStateManagerGlobal
+                .registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR);
 
         verify(callback).onDeviceStateChanged(eq(mService.getBaseState()));
         reset(callback);
 
-        DeviceStateRequest request = DeviceStateRequest.newBuilder(
-                OTHER_DEVICE_STATE.getIdentifier()).build();
+        final DeviceStateRequest request =
+                DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build();
         mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
                 null /* callback */);
 
         verify(callback).onDeviceStateChanged(eq(OTHER_DEVICE_STATE));
-        assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+        assertThat(mService.getBaseState()).isEqualTo(OTHER_DEVICE_STATE);
         reset(callback);
 
-        DeviceStateRequest secondRequest = DeviceStateRequest.newBuilder(
-                DEFAULT_DEVICE_STATE.getIdentifier()).build();
+        final DeviceStateRequest secondRequest =
+                DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE.getIdentifier()).build();
 
         mDeviceStateManagerGlobal.requestState(secondRequest, null, null);
 
-        assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+        assertThat(mService.getBaseState()).isEqualTo(OTHER_DEVICE_STATE);
         verify(callback).onDeviceStateChanged(eq(DEFAULT_DEVICE_STATE));
         reset(callback);
 
@@ -214,10 +270,10 @@
 
     @Test
     public void verifyDeviceStateRequestCallbacksCalled() {
-        DeviceStateRequest.Callback callback = mock(TestDeviceStateRequestCallback.class);
+        final DeviceStateRequest.Callback callback = mock(TestDeviceStateRequestCallback.class);
 
-        DeviceStateRequest request = DeviceStateRequest.newBuilder(
-                OTHER_DEVICE_STATE.getIdentifier()).build();
+        final DeviceStateRequest request =
+                DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE.getIdentifier()).build();
         mDeviceStateManagerGlobal.requestState(request,
                 ConcurrentUtils.DIRECT_EXECUTOR /* executor */,
                 callback /* callback */);
@@ -232,52 +288,62 @@
 
     public static class TestDeviceStateRequestCallback implements DeviceStateRequest.Callback {
         @Override
-        public void onRequestActivated(DeviceStateRequest request) { }
+        public void onRequestActivated(@NonNull DeviceStateRequest request) { }
 
         @Override
-        public void onRequestCanceled(DeviceStateRequest request) { }
+        public void onRequestCanceled(@NonNull DeviceStateRequest request) { }
 
         @Override
-        public void onRequestSuspended(DeviceStateRequest request) { }
+        public void onRequestSuspended(@NonNull DeviceStateRequest request) { }
     }
 
     private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
-        public static final class Request {
-            public final IBinder token;
-            public final int state;
-            public final int flags;
+        static final class Request {
+            @NonNull
+            final IBinder mToken;
+            final int mState;
 
-            private Request(IBinder token, int state, int flags) {
-                this.token = token;
-                this.state = state;
-                this.flags = flags;
+            private Request(@NonNull IBinder token, int state) {
+                this.mToken = token;
+                this.mState = state;
             }
         }
 
-        private List<DeviceState> mSupportedDeviceStates = List.of(DEFAULT_DEVICE_STATE,
-                OTHER_DEVICE_STATE);
-
+        @NonNull
+        private List<DeviceState> mSupportedDeviceStates =
+                List.of(DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE);
+        @NonNull
         private DeviceState mBaseState = DEFAULT_DEVICE_STATE;
+        @Nullable
         private Request mRequest;
+        @Nullable
         private Request mBaseStateRequest;
 
+        private final boolean mSimulatePostCallback;
         private final Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
 
-        TestDeviceStateManagerService(FakePermissionEnforcer enforcer) {
-            super(enforcer);
+        TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer) {
+            this(enforcer, false /* simulatePostCallback */);
         }
 
+        TestDeviceStateManagerService(@NonNull FakePermissionEnforcer enforcer,
+                boolean simulatePostCallback) {
+            super(enforcer);
+            mSimulatePostCallback = simulatePostCallback;
+        }
+
+        @NonNull
         private DeviceStateInfo getInfo() {
             final int mergedBaseState = mBaseStateRequest == null
-                    ? mBaseState.getIdentifier() : mBaseStateRequest.state;
-            final int mergedState = mRequest == null
-                    ? mergedBaseState : mRequest.state;
+                    ? mBaseState.getIdentifier() : mBaseStateRequest.mState;
+            final int mergedState = mRequest == null ? mergedBaseState : mRequest.mState;
 
+            final ArrayList<DeviceState> supportedStates = new ArrayList<>(mSupportedDeviceStates);
             final DeviceState baseState = new DeviceState(
                     new DeviceState.Configuration.Builder(mergedBaseState, "" /* name */).build());
             final DeviceState state = new DeviceState(
                     new DeviceState.Configuration.Builder(mergedState, "" /* name */).build());
-            return new DeviceStateInfo(new ArrayList<>(mSupportedDeviceStates), baseState, state);
+            return new DeviceStateInfo(supportedStates, baseState, state);
         }
 
         private void notifyDeviceStateInfoChanged() {
@@ -291,38 +357,47 @@
             }
         }
 
+        @NonNull
         @Override
         public DeviceStateInfo getDeviceStateInfo() {
             return getInfo();
         }
 
+        @Nullable
         @Override
-        public void registerCallback(IDeviceStateManagerCallback callback) {
+        public DeviceStateInfo registerCallback(IDeviceStateManagerCallback callback) {
             if (mCallbacks.contains(callback)) {
                 throw new SecurityException("Callback is already registered.");
             }
 
             mCallbacks.add(callback);
-            try {
-                callback.onDeviceStateInfoChanged(getInfo());
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
+            if (Flags.wlinfoOncreate()) {
+                return getInfo();
             }
+
+            if (!mSimulatePostCallback) {
+                try {
+                    callback.onDeviceStateInfoChanged(getInfo());
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            }
+            return null;
         }
 
         @Override
-        public void requestState(IBinder token, int state, int flags) {
+        public void requestState(@NonNull IBinder token, int state, int unusedFlags) {
             if (mRequest != null) {
                 for (IDeviceStateManagerCallback callback : mCallbacks) {
                     try {
-                        callback.onRequestCanceled(mRequest.token);
+                        callback.onRequestCanceled(mRequest.mToken);
                     } catch (RemoteException e) {
                         e.rethrowFromSystemServer();
                     }
                 }
             }
 
-            final Request request = new Request(token, state, flags);
+            final Request request = new Request(token, state);
             mRequest = request;
             notifyDeviceStateInfoChanged();
 
@@ -337,7 +412,7 @@
 
         @Override
         public void cancelStateRequest() {
-            IBinder token = mRequest.token;
+            final IBinder token = mRequest.mToken;
             mRequest = null;
             for (IDeviceStateManagerCallback callback : mCallbacks) {
                 try {
@@ -350,19 +425,18 @@
         }
 
         @Override
-        public void requestBaseStateOverride(IBinder token, int state, int flags) {
+        public void requestBaseStateOverride(@NonNull IBinder token, int state, int unusedFlags) {
             if (mBaseStateRequest != null) {
                 for (IDeviceStateManagerCallback callback : mCallbacks) {
                     try {
-                        callback.onRequestCanceled(mBaseStateRequest.token);
+                        callback.onRequestCanceled(mBaseStateRequest.mToken);
                     } catch (RemoteException e) {
                         e.rethrowFromSystemServer();
                     }
                 }
             }
 
-            final Request request = new Request(token, state, flags);
-            mBaseStateRequest = request;
+            mBaseStateRequest = new Request(token, state);
             notifyDeviceStateInfoChanged();
 
             for (IDeviceStateManagerCallback callback : mCallbacks) {
@@ -376,7 +450,7 @@
 
         @Override
         public void cancelBaseStateOverride() throws RemoteException {
-            IBinder token = mBaseStateRequest.token;
+            final IBinder token = mBaseStateRequest.mToken;
             mBaseStateRequest = null;
             for (IDeviceStateManagerCallback callback : mCallbacks) {
                 try {
@@ -396,24 +470,27 @@
             onStateRequestOverlayDismissed_enforcePermission();
         }
 
-        public void setSupportedStates(List<DeviceState> states) {
+        public void setSupportedStates(@NonNull List<DeviceState> states) {
             mSupportedDeviceStates = states;
             notifyDeviceStateInfoChanged();
         }
 
+        @NonNull
         public List<DeviceState> getSupportedDeviceStates() {
             return mSupportedDeviceStates;
         }
 
-        public void setBaseState(DeviceState state) {
+        public void setBaseState(@NonNull DeviceState state) {
             mBaseState = state;
             notifyDeviceStateInfoChanged();
         }
 
+        @NonNull
         public DeviceState getBaseState() {
             return getInfo().baseState;
         }
 
+        @NonNull
         public DeviceState getMergedState() {
             return getInfo().currentState;
         }
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
index 78d4324..83b5ff3 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
@@ -21,17 +21,16 @@
 import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
 
-import junit.framework.Assert;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import java.util.HashSet;
 import java.util.List;
@@ -39,28 +38,32 @@
 
 /**
  * Unit tests for {@link android.hardware.devicestate.DeviceState}.
- * <p/>
- * Run with <code>atest DeviceStateTest</code>.
+ *
+ * <p> Build/Install/Run:
+ * atest FrameworksCoreDeviceStateManagerTests:DeviceStateTest
  */
 @Presubmit
-@RunWith(JUnit4.class)
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public final class DeviceStateTest {
     @Test
     public void testConstruct() {
-        DeviceState.Configuration config = new DeviceState.Configuration.Builder(
+        final DeviceState.Configuration config = new DeviceState.Configuration.Builder(
                 MINIMUM_DEVICE_STATE_IDENTIFIER, "TEST_CLOSED")
                 .setSystemProperties(
                         new HashSet<>(List.of(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)))
                 .build();
+
         final DeviceState state = new DeviceState(config);
-        assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE_IDENTIFIER);
-        assertEquals(state.getName(), "TEST_CLOSED");
-        assertTrue(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS));
+
+        assertThat(state.getIdentifier()).isEqualTo(MINIMUM_DEVICE_STATE_IDENTIFIER);
+        assertThat(state.getName()).isEqualTo("TEST_CLOSED");
+        assertThat(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)).isTrue();
     }
 
     @Test
     public void testHasProperties() {
-        DeviceState.Configuration config = new DeviceState.Configuration.Builder(
+        final DeviceState.Configuration config = new DeviceState.Configuration.Builder(
                 MINIMUM_DEVICE_STATE_IDENTIFIER, "TEST")
                 .setSystemProperties(new HashSet<>(List.of(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS,
                         PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST)))
@@ -68,10 +71,10 @@
 
         final DeviceState state = new DeviceState(config);
 
-        assertTrue(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS));
-        assertTrue(state.hasProperty(PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST));
-        assertTrue(state.hasProperties(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS,
-                PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST));
+        assertThat(state.hasProperty(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS)).isTrue();
+        assertThat(state.hasProperty(PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST)).isTrue();
+        assertThat(state.hasProperties(PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS,
+                PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST)).isTrue();
     }
 
     @Test
@@ -91,7 +94,7 @@
         final DeviceState.Configuration stateConfiguration =
                 DeviceState.Configuration.CREATOR.createFromParcel(parcel);
 
-        Assert.assertEquals(originalState, new DeviceState(stateConfiguration));
+        assertThat(originalState).isEqualTo(new DeviceState(stateConfiguration));
     }
 
     @Test
@@ -109,6 +112,6 @@
         final DeviceState.Configuration stateConfiguration =
                 DeviceState.Configuration.CREATOR.createFromParcel(parcel);
 
-        Assert.assertEquals(originalState, new DeviceState(stateConfiguration));
+        assertThat(originalState).isEqualTo(new DeviceState(stateConfiguration));
     }
 }
diff --git a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
index 244fcff..37ddfd2 100644
--- a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
@@ -22,6 +22,9 @@
 
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.vibrator.IVibrator;
+import android.util.SparseArray;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -134,6 +137,54 @@
     }
 
     @Test
+    public void testDurationMono_withVibratorSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Use max duration from all vibrators.
+        assertEquals(10, CombinedVibration.createParallel(
+                VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos));
+        assertEquals(111, CombinedVibration.createParallel(
+                        VibrationEffect.startComposition()
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                                .compose())
+                .getDuration(infos));
+    }
+
+    @Test
+    public void testDurationMono_withVibratorNotSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Use max duration from all vibrators.
+        assertEquals(-1, CombinedVibration.createParallel(
+                VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos));
+        assertEquals(-1, CombinedVibration.createParallel(
+                        VibrationEffect.startComposition()
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                                .compose())
+                .getDuration(infos));
+    }
+
+    @Test
     public void testDurationStereo() {
         assertEquals(6, CombinedVibration.startParallel()
                 .addVibrator(1, VibrationEffect.createOneShot(1, 1))
@@ -156,6 +207,75 @@
     }
 
     @Test
+    public void testDurationStereo_withVibratorSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Use specific vibrator durations, then max effect duration
+        assertEquals(111, CombinedVibration.startParallel()
+                .addVibrator(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .addVibrator(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+        assertEquals(110, CombinedVibration.startParallel()
+                .addVibrator(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
+    public void testDurationStereo_withVibratorNotSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // One vibrator does not support primitives
+        assertEquals(-1, CombinedVibration.startParallel()
+                .addVibrator(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .addVibrator(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+        // Invalid vibrator ID
+        assertEquals(-1, CombinedVibration.startParallel()
+                .addVibrator(3, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
     public void testDurationSequential() {
         assertEquals(26, CombinedVibration.startSequential()
                 .addNext(1, VibrationEffect.createOneShot(10, 10), 10)
@@ -178,6 +298,59 @@
     }
 
     @Test
+    public void testDurationSequential_withVibratorSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        // Add each duration and delay
+        assertEquals(321, CombinedVibration.startSequential()
+                .addNext(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose(), 100)
+                .addNext(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
+    public void testDurationSequential_withVibratorNotSupportingPrimitives() {
+        SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+        infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build());
+        infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+                .build());
+
+        assertEquals(-1, CombinedVibration.startSequential()
+                .addNext(1, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose(), 100)
+                .addNext(2, VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                        .compose())
+                .combine()
+                .getDuration(infos));
+    }
+
+    @Test
     public void testIsHapticFeedbackCandidateMono() {
         assertTrue(CombinedVibration.createParallel(
                 VibrationEffect.createOneShot(1, 1)).isHapticFeedbackCandidate());
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index f5b04ee..8acf2ed 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -1078,6 +1078,52 @@
     }
 
     @Test
+    public void testDuration_withVibratorSupportingPrimitives() {
+        VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+                .build();
+
+        VibrationEffect composition = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+                .compose();
+
+        assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration());
+        assertEquals(10, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info));
+        assertEquals(115, composition.getDuration(info));
+        assertEquals(Long.MAX_VALUE,
+                VibrationEffect.startComposition()
+                        .repeatEffectIndefinitely(composition)
+                        .compose()
+                        .getDuration(info));
+        if (Flags.vendorVibrationEffects()) {
+            assertEquals(-1,
+                    VibrationEffect.createVendorEffect(createNonEmptyBundle()).getDuration(info));
+        }
+    }
+
+    @Test
+    public void testDuration_withVibratorNotSupportingPrimitives() {
+        VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+                .build();
+
+        VibrationEffect composition = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .compose();
+
+        assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info));
+        assertEquals(-1, composition.getDuration(info));
+        assertEquals(Long.MAX_VALUE,
+                VibrationEffect.startComposition()
+                        .repeatEffectIndefinitely(composition)
+                        .compose()
+                        .getDuration(info));
+    }
+
+    @Test
     public void testAreVibrationFeaturesSupported_allSegmentsSupported() {
         VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
index 47d01c4..04945f3 100644
--- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -24,6 +26,7 @@
 
 import android.hardware.vibrator.Braking;
 import android.hardware.vibrator.IVibrator;
+import android.util.Range;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -39,12 +42,19 @@
     private static final float TEST_FREQUENCY_RESOLUTION = 25;
     private static final float[] TEST_AMPLITUDE_MAP = new float[]{
             /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
+    private static final float[] TEST_FREQUENCIES =
+            new float[]{90f, 120f, 150f, 60f, 30f, 210f, 270f, 300f, 240f, 180f};
+    private static final float[] TEST_OUTPUT_ACCELERATIONS =
+            new float[]{1.2f, 1.8f, 2.4f, 0.6f, 0.1f, 2.2f, 1.0f, 0.5f, 1.9f, 3.0f};
 
     private static final VibratorInfo.FrequencyProfileLegacy EMPTY_FREQUENCY_PROFILE =
             new VibratorInfo.FrequencyProfileLegacy(Float.NaN, Float.NaN, Float.NaN, null);
     private static final VibratorInfo.FrequencyProfileLegacy TEST_FREQUENCY_PROFILE_LEGACY =
             new VibratorInfo.FrequencyProfileLegacy(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
                     TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
+    private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
+            new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_FREQUENCIES,
+                    TEST_OUTPUT_ACCELERATIONS);
 
     @Test
     public void testHasAmplitudeControl() {
@@ -179,13 +189,123 @@
     }
 
     @Test
+    public void testGetFrequencyProfile_unsetProfileIsEmpty() {
+        assertTrue(new VibratorInfo.Builder(
+                TEST_VIBRATOR_ID).build().getFrequencyProfile().isEmpty());
+    }
+
+    @Test
+    public void testFrequencyProfile_invalidValuesCreatesEmptyProfile() {
+        // Invalid resonant frequency.
+        assertThat(new VibratorInfo.FrequencyProfile(Float.NaN,
+                TEST_FREQUENCIES, TEST_OUTPUT_ACCELERATIONS).isEmpty()).isTrue();
+        assertThat(new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/-1f,
+                TEST_FREQUENCIES, TEST_OUTPUT_ACCELERATIONS).isEmpty()).isTrue();
+        // No frequency-acceleration data
+        assertThat(new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f,
+                /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null).isEmpty()).isTrue();
+        // Mismatching frequency and output acceleration lists
+        assertThat(new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f,
+                /*frequenciesHz=*/ new float[]{30f, 40f, 50f, 100f},
+                /*outputAccelerationsGs=*/ new float[]{0.8f, 1.0f, 2.0f}).isEmpty()).isTrue();
+    }
+
+    @Test
+    public void testGetFrequenciesAndOutputAccelerations_noFrequencyAccelerationData_returnNull() {
+        VibratorInfo.FrequencyProfile emptyFrequencyProfile =
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f,
+                        /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null);
+        assertThat(emptyFrequencyProfile.getFrequenciesHz()).isNull();
+        assertThat(emptyFrequencyProfile.getOutputAccelerationsGs()).isNull();
+    }
+
+    @Test
+    public void testGetFrequenciesAndOutputAccelerations_mismatchingDataLength_returnNull() {
+        VibratorInfo.FrequencyProfile emptyFrequencyProfile =
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f,
+                        /*frequenciesHz=*/ new float[]{150f, 200f},
+                        /*outputAccelerationsGs=*/ new float[]{1.2f, 2.2f, 3.0f});
+        assertThat(emptyFrequencyProfile.getFrequenciesHz()).isNull();
+        assertThat(emptyFrequencyProfile.getOutputAccelerationsGs()).isNull();
+    }
+
+    @Test
+    public void testGetFrequenciesAndOutputAccelerations_dataIsDedupedAndSorted() {
+        VibratorInfo.FrequencyProfile frequencyProfile =
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f,
+                        /*frequenciesHz=*/ new float[]{150f, 150f, 150f, 130f, 200f, 160f},
+                        /*outputAccelerationsGs=*/ new float[]{1.2f, 1.5f, 1.9f, 1.0f, 2.2f, 3.0f});
+        float[] frequencies = frequencyProfile.getFrequenciesHz();
+        assertThat(frequencies).isEqualTo(
+                new float[]{130f, 150f, 160f, 200f});
+        assertThat(frequencyProfile.getOutputAccelerationsGs()).isEqualTo(
+                new float[]{1.0f, 1.2f, 3.0f, 2.2f});
+    }
+
+    @Test
+    public void testGetFrequencyRangeHz_emptyProfileReturnsNull() {
+        VibratorInfo.FrequencyProfile emptyFrequencyProfile =
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f,
+                        /*frequenciesHz=*/ null, /*outputAccelerationsGs=*/ null);
+        assertThat(
+                emptyFrequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/0.2f)).isNull();
+    }
+
+    @Test
+    public void testGetFrequencyRangeHz_validProfileReturnsMappedValues() {
+        VibratorInfo.FrequencyProfile frequencyProfile =
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/150f,
+                /*frequenciesHz=*/new float[]{90f, 120f, 150f, 60f, 30f, 210f, 180f},
+                /*outputAccelerationsGs=*/ new float[]{1.2f, 1.8f, 2.4f, 0.6f, 0.4f, 2.2f, 3.0f});
+
+        // lower and upper bounds are min and max frequencies
+        assertThat(frequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/0.33f)).isEqualTo(
+                new Range<>(frequencyProfile.getMinFrequencyHz(),
+                        frequencyProfile.getMaxFrequencyHz()));
+
+        // lower and upper bounds are within frequency range and use interpolation
+        assertThat(frequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/2.6f))
+                .isEqualTo(new Range<>(160f, 195f));
+
+        // upper bound is max frequency
+        assertThat(frequencyProfile.getFrequencyRangeHz(/*minOutputAcceleration=*/2.0f))
+                .isEqualTo(new Range<>(130f, frequencyProfile.getMaxFrequencyHz()));
+    }
+
+    @Test
+    public void testFrequencyProfile_emptyProfileReturnsNanValues() {
+        VibratorInfo.FrequencyProfile frequencyProfile = new VibratorInfo.FrequencyProfile(
+                /*resonantFrequencyHz=*/150f, /*frequenciesHz=*/ null,
+                /*outputAccelerationsGs=*/ null);
+
+        assertThat(frequencyProfile.getMaxOutputAccelerationGs()).isNaN();
+        assertThat(frequencyProfile.getMinFrequencyHz()).isNaN();
+        assertThat(frequencyProfile.getMaxFrequencyHz()).isNaN();
+        assertThat(frequencyProfile.getOutputAccelerationGs(/*frequencyHz=*/150f)).isNaN();
+    }
+
+    @Test
+    public void testFrequencyProfile_validProfileReturnsAppropriateValues() {
+        VibratorInfo.FrequencyProfile frequencyProfile = new VibratorInfo.FrequencyProfile(
+                /*resonantFrequencyHz=*/150f, TEST_FREQUENCIES, TEST_OUTPUT_ACCELERATIONS);
+
+        assertThat(frequencyProfile.getMaxOutputAccelerationGs()).isEqualTo(3f);
+        assertThat(frequencyProfile.getMinFrequencyHz()).isEqualTo(30f);
+        assertThat(frequencyProfile.getMaxFrequencyHz()).isEqualTo(300f);
+        assertThat(frequencyProfile.getOutputAccelerationGs(/*frequencyHz=*/150f)).isEqualTo(2.4f);
+        // Test getting output acceleration using linear interpolation
+        assertThat(frequencyProfile.getOutputAccelerationGs(/*frequencyHz=*/166f)).isEqualTo(
+                2.72f);
+    }
+
+    @Test
     public void testGetFrequencyProfileLegacy_unsetProfileIsEmpty() {
         assertTrue(new VibratorInfo.Builder(
                 TEST_VIBRATOR_ID).build().getFrequencyProfileLegacy().isEmpty());
     }
 
     @Test
-    public void testFrequencyProfile_invalidValuesCreatesEmptyProfile() {
+    public void testFrequencyProfileLegacy_invalidValuesCreatesEmptyProfile() {
         // Invalid, contains NaN values or empty array.
         assertTrue(new VibratorInfo.FrequencyProfileLegacy(
                 Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).isEmpty());
@@ -216,7 +336,7 @@
     }
 
     @Test
-    public void testGetFrequencyRangeHz_emptyProfileReturnsNull() {
+    public void testLegacyGetFrequencyRangeHz_emptyProfileReturnsNull() {
         assertNull(new VibratorInfo.FrequencyProfileLegacy(
                 Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).getFrequencyRangeHz());
         assertNull(new VibratorInfo.FrequencyProfileLegacy(
@@ -228,7 +348,7 @@
     }
 
     @Test
-    public void testGetFrequencyRangeHz_validProfileReturnsMappedValues() {
+    public void testLegacyGetFrequencyRangeHz_validProfileReturnsMappedValues() {
         VibratorInfo.FrequencyProfileLegacy profile = new VibratorInfo.FrequencyProfileLegacy(
                 /* resonantFrequencyHz= */ 150,
                 /* minFrequencyHz= */ 50,
@@ -306,6 +426,7 @@
                     .setPwleSizeMax(20)
                     .setQFactor(2f)
                     .setFrequencyProfileLegacy(TEST_FREQUENCY_PROFILE_LEGACY)
+                    .setFrequencyProfile(TEST_FREQUENCY_PROFILE)
                     .setMaxEnvelopeEffectSize(16)
                     .setMinEnvelopeEffectControlPointDurationMillis(20)
                     .setMaxEnvelopeEffectControlPointDurationMillis(1_000);
@@ -347,18 +468,33 @@
         assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
         assertFalse(complete.equalContent(completeWithDifferentPrimitiveDuration));
 
-        VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder
+        VibratorInfo completeWithDifferentFrequencyProfileLegacy = completeBuilder
                 .setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(
                         TEST_RESONANT_FREQUENCY + 20,
                         TEST_MIN_FREQUENCY + 10,
                         TEST_FREQUENCY_RESOLUTION + 5,
                         TEST_AMPLITUDE_MAP))
                 .build();
+        assertNotEquals(complete, completeWithDifferentFrequencyProfileLegacy);
+        assertFalse(complete.equalContent(completeWithDifferentFrequencyProfileLegacy));
+
+        VibratorInfo completeWithEmptyFrequencyProfileLegacy = completeBuilder
+                .setFrequencyProfileLegacy(EMPTY_FREQUENCY_PROFILE)
+                .build();
+        assertNotEquals(complete, completeWithEmptyFrequencyProfileLegacy);
+        assertFalse(complete.equalContent(completeWithEmptyFrequencyProfileLegacy));
+
+        VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder
+                .setFrequencyProfile(
+                        new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY + 20,
+                                new float[]{90f, 150f}, new float[]{1.2f, 2.2f}))
+                .build();
         assertNotEquals(complete, completeWithDifferentFrequencyProfile);
         assertFalse(complete.equalContent(completeWithDifferentFrequencyProfile));
 
         VibratorInfo completeWithEmptyFrequencyProfile = completeBuilder
-                .setFrequencyProfileLegacy(EMPTY_FREQUENCY_PROFILE)
+                .setFrequencyProfile(
+                        new VibratorInfo.FrequencyProfile(Float.NaN, null, null))
                 .build();
         assertNotEquals(complete, completeWithEmptyFrequencyProfile);
         assertFalse(complete.equalContent(completeWithEmptyFrequencyProfile));
@@ -396,6 +532,7 @@
                 .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
                 .setQFactor(Float.NaN)
                 .setFrequencyProfileLegacy(TEST_FREQUENCY_PROFILE_LEGACY)
+                .setFrequencyProfile(TEST_FREQUENCY_PROFILE)
                 .build();
 
         Parcel parcel = Parcel.obtain();
diff --git a/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java
index f192b89..c9ab297 100644
--- a/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java
@@ -16,6 +16,8 @@
 
 package android.os.vibrator;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertEquals;
@@ -24,7 +26,11 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.VibratorInfo;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -33,6 +39,9 @@
 public class MultiVibratorInfoTest {
     private static final float TEST_TOLERANCE = 1e-5f;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Test
     public void testGetId() {
         VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
@@ -157,6 +166,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testGetQFactorAndResonantFrequency_differentValues_returnsNaN() {
         VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
@@ -187,6 +197,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testGetQFactorAndResonantFrequency_sameValues_returnsValue() {
         VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
@@ -212,6 +223,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testGetFrequencyProfileLegacy_differentResonantFreqOrResolutions_returnsEmpty() {
         VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
@@ -240,6 +252,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testGetFrequencyProfileLegacy_missingValues_returnsEmpty() {
         VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
@@ -288,6 +301,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testGetFrequencyProfileLegacy_unalignedMaxAmplitudes_returnsEmpty() {
         VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
@@ -312,6 +326,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testGetFrequencyProfileLegacy_alignedProfiles_returnsIntersection() {
         VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
@@ -353,6 +368,132 @@
         assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
     }
 
+    @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testGetFrequencyProfile_alignedProfiles_returnsIntersection() {
+        VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f,
+                /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f},
+                /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f});
+
+        VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f,
+                /*frequencies=*/new float[]{120f, 150f, 180f, 210f},
+                /*accelerations=*/new float[]{1.5f, 2.6f, 2.7f, 2.1f});
+
+        VibratorInfo.FrequencyProfile expectedFrequencyProfile =
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/
+                        180f, /*frequenciesHz=*/new float[]{120.0f, 150.0f, 180.0f, 210.0f},
+                        /*outputAccelerationsGs=*/new float[]{1.5f, 2.4f, 2.7f, 2.1f});
+
+        VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+                new VibratorInfo[]{firstInfo, secondInfo});
+
+        assertThat(info.getFrequencyProfile()).isEqualTo(expectedFrequencyProfile);
+    }
+
+    @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testGetFrequencyProfile_alignedProfilesUsingInterpolation_returnsIntersection() {
+        VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f,
+                /*frequencies=*/new float[]{30f, 60f, 120f},
+                /*accelerations=*/new float[]{0.25f, 1.0f, 4.0f});
+
+        VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f,
+                /*frequencies=*/new float[]{40f, 70f, 110f},
+                /*accelerations=*/new float[]{1.0f, 2.5f, 4.0f});
+
+        VibratorInfo.FrequencyProfile expectedFrequencyProfile =
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/
+                        180f, /*frequenciesHz=*/new float[]{40f, 60f, 70f, 110f},
+                        /*outputAccelerationsGs=*/new float[]{0.5f, 1.0f, 1.5f, 3.5f});
+
+        VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+                new VibratorInfo[]{firstInfo, secondInfo});
+
+        assertThat(info.getFrequencyProfile()).isEqualTo(expectedFrequencyProfile);
+    }
+
+    @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testGetFrequencyProfile_disjointFrequencyRange_returnsEmpty() {
+
+        VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f,
+                /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f},
+                /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f});
+
+        VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f,
+                /*frequencies=*/new float[]{310f, 320f, 350f, 380f, 410f, 440f},
+                /*accelerations=*/new float[]{0.3f, 0.75f, 1.82f, 2.11f, 2.8f, 2.12f, 1.4f, 0.42f});
+
+        VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+                new VibratorInfo[]{firstInfo, secondInfo});
+
+        assertThat(info.getFrequencyProfile()).isEqualTo(
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ Float.NaN,
+                        /*frequenciesHz=*/null, /*outputAccelerationsGs=*/null));
+        assertThat(info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testGetFrequencyProfile_emptyFrequencyRange_returnsEmpty() {
+        VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/180f,
+                /*frequencies=*/null, /*accelerations=*/null);
+
+        VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/180f,
+                /*frequencies=*/new float[]{30f, 60f, 150f, 180f, 210f, 240f, 300f},
+                /*accelerations=*/new float[]{0.1f, 0.6f, 2.4f, 3.0f, 2.2f, 1.9f, 0.5f});
+
+        VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+                new VibratorInfo[]{firstInfo, secondInfo});
+
+        assertThat(info.getFrequencyProfile()).isEqualTo(
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ Float.NaN,
+                        /*frequenciesHz=*/null,
+                        /*outputAccelerationsGs=*/null));
+        assertThat(info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testGetFrequencyProfile_differentResonantFrequency_returnsEmpty() {
+        VibratorInfo firstInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 1,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 160f,
+                /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f},
+                /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f});
+
+        VibratorInfo secondInfo = createVibratorInfoWithFrequencyProfile(/*id=*/ 2,
+                IVibrator.CAP_FREQUENCY_CONTROL, /*resonantFrequencyHz=*/ 180f,
+                /*frequencies=*/new float[]{30f, 60f, 120f, 150f, 180f, 210f, 270f, 300f},
+                /*accelerations=*/new float[]{0.1f, 0.6f, 1.8f, 2.4f, 3.0f, 2.2f, 1.0f, 0.5f});
+
+        VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+                new VibratorInfo[]{firstInfo, secondInfo});
+
+        assertThat(info.getFrequencyProfile()).isEqualTo(
+                new VibratorInfo.FrequencyProfile(/*resonantFrequencyHz=*/ Float.NaN,
+                        /*frequenciesHz=*/null,
+                        /*outputAccelerationsGs=*/null));
+        assertThat(info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)).isFalse();
+    }
+
+    private VibratorInfo createVibratorInfoWithFrequencyProfile(int id, long capabilities,
+            float resonantFrequencyHz, float[] frequencies, float[] accelerations) {
+        return new VibratorInfo.Builder(id)
+                .setCapabilities(capabilities)
+                .setFrequencyProfile(
+                        new VibratorInfo.FrequencyProfile(resonantFrequencyHz, frequencies,
+                                accelerations))
+                .build();
+    }
+
     /**
      * Asserts that the frequency profile is empty, and therefore frequency control isn't supported.
      */
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 7dd9e55..f9ec5f0 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -24,6 +24,7 @@
 import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertThrows;
 
+import android.hardware.vibrator.IVibrator;
 import android.os.Parcel;
 import android.os.VibrationEffect;
 import android.os.VibratorInfo;
@@ -114,39 +115,82 @@
 
     @Test
     public void testDuration() {
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
-                .getDuration());
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
-                .getDuration());
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
-                .getDuration());
-        assertEquals(-1, new PrebakedSegment(
-                VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK).getDuration());
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK).getDuration());
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD).getDuration());
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
                 .getDuration());
     }
 
     @Test
+    public void testDuration_withVibratorSupportingPrimitives_returnsPrimitiveDuration() {
+        int tickDuration = 5;
+        int clickDuration = 10;
+        int thudDuration = 15;
+
+        VibratorInfo vibratorInfo = new VibratorInfo.Builder(/* id= */ 1)
+                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, tickDuration)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, clickDuration)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, thudDuration)
+                .build();
+
+        assertEquals(5, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(20, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(15, createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+                .getDuration(vibratorInfo));
+
+        // Unknown effects
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0])
+                .getDuration(vibratorInfo));
+    }
+
+    @Test
+    public void testDuration_withVibratorNotSupportingPrimitives_returnsUnknown() {
+        VibratorInfo vibratorInfo = createVibratorInfoWithSupportedEffects(
+                VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_POP);
+
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+                .getDuration(vibratorInfo));
+        assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0])
+                .getDuration(vibratorInfo));
+    }
+
+    @Test
     public void testIsHapticFeedbackCandidate_prebakedConstants_areCandidates() {
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_HEAVY_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
                 .isHapticFeedbackCandidate());
-        assertTrue(new PrebakedSegment(
-                VibrationEffect.EFFECT_TEXTURE_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
                 .isHapticFeedbackCandidate());
     }
 
@@ -271,8 +315,7 @@
 
     @Test
     public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() {
-        assertFalse(new PrebakedSegment(
-                VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+        assertFalse(createSegmentWithFallback(VibrationEffect.RINGTONES[1])
                 .isHapticFeedbackCandidate());
     }
 
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index 97f1d5e..a6d9dc5 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -201,6 +201,22 @@
     }
 
     @Test
+    public void testDuration_withVibratorSupportingPrimitives_returnsVibratorDurationWithDelay() {
+        VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_CLICK, /* durationMs= */ 10);
+        assertEquals(15, new PrimitiveSegment(
+                VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 5).getDuration(vibratorInfo));
+    }
+
+    @Test
+    public void testDuration_withVibratorNotSupportingPrimitive_returnsUnknown() {
+        VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive(
+                VibrationEffect.Composition.PRIMITIVE_CLICK);
+        assertEquals(-1, new PrimitiveSegment(
+                VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 5).getDuration(vibratorInfo));
+    }
+
+    @Test
     public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() {
         assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
                 .areVibrationFeaturesSupported(
@@ -252,9 +268,14 @@
     }
 
     private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) {
+        return createVibratorInfoWithSupportedPrimitive(primitiveId, /* durationMs= */ 10);
+    }
+
+    private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId,
+            int durationMs) {
         return new VibratorInfo.Builder(/* id= */ 1)
                 .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
-                .setSupportedPrimitive(primitiveId, 10)
+                .setSupportedPrimitive(primitiveId, durationMs)
                 .build();
     }
 }
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index bea8293..df874bcb 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -195,7 +195,14 @@
 
     @Test
     public void testDuration() {
+        VibratorInfo infoWithSupport =
+                createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
+        VibratorInfo infoWithoutSupport =
+                createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ false);
+
         assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration());
+        assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithSupport));
+        assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithoutSupport));
     }
 
     @Test
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 411074a..914117c 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -213,7 +213,13 @@
 
     @Test
     public void testDuration() {
+        VibratorInfo infoWithSupport = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
+        VibratorInfo infoWithoutSupport =
+                createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
+
         assertEquals(5, new StepSegment(0, 0, 5).getDuration());
+        assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithSupport));
+        assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithoutSupport));
     }
 
     @Test
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2e72f0e..a028e18 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -572,6 +572,7 @@
         <permission name="android.permission.READ_BLOCKED_NUMBERS" />
         <!-- Permission required for CTS test - PackageManagerTest -->
         <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
+        <permission name="android.permission.VERIFICATION_AGENT"/>
         <!-- Permission required for CTS test CtsInputTestCases -->
         <permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" />
         <!-- Permission required for CTS test - PackageManagerShellCommandInstallTest -->
@@ -587,6 +588,9 @@
         <permission name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
         <!-- Permission required for CTS test - CtsAppTestCases -->
         <permission name="android.permission.KILL_UID" />
+        <!-- Permission required for CTS test - AdvancedProtectionManagerTest -->
+        <permission name="android.permission.SET_ADVANCED_PROTECTION_MODE" />
+        <permission name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index c1d2235..b559a15 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -22,6 +22,7 @@
 
     static_libs: [
         "annotations",
+        "jsr305",
         "framework-annotations-lib",
         "//external/error_prone:error_prone_core",
     ],
diff --git a/errorprone/OWNERS b/errorprone/OWNERS
index bddbdb3..aa8c126 100644
--- a/errorprone/OWNERS
+++ b/errorprone/OWNERS
@@ -1,2 +1 @@
-jsharkey@android.com
-jsharkey@google.com
+colefaust@google.com
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
index 8dc9579..6d5e4484 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -29,6 +29,7 @@
 import com.google.errorprone.fixes.SuggestedFix;
 import com.google.errorprone.matchers.Description;
 import com.google.errorprone.util.ASTHelpers;
+import com.google.errorprone.util.ErrorProneComment;
 import com.google.errorprone.util.ErrorProneToken;
 import com.google.errorprone.util.ErrorProneTokens;
 import com.sun.source.tree.ClassTree;
@@ -37,7 +38,6 @@
 import com.sun.source.tree.NewClassTree;
 import com.sun.source.tree.Tree;
 import com.sun.source.tree.VariableTree;
-import com.sun.tools.javac.parser.Tokens;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -66,7 +66,7 @@
         final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state);
         final String sourceCode = state.getSourceCode().toString();
         for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
-            for (Tokens.Comment comment : token.comments()) {
+            for (ErrorProneComment comment : token.comments()) {
                 if (!javadocableTrees.containsKey(token.pos())) {
                     continue;
                 }
@@ -81,7 +81,7 @@
         return NO_MATCH;
     }
 
-    private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) {
+    private static Optional<SuggestedFix> generateFix(ErrorProneComment comment) {
         final String text = comment.getText();
         if (text.startsWith("/**")) {
             return Optional.empty();
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
index 5ad97e6..a63cbee 100644
--- a/graphics/java/android/framework_graphics.aconfig
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -33,3 +33,12 @@
   description: "Add OkLab ColorSpace support"
   bug: "344038816"
 }
+
+flag {
+  name: "display_bt2020_colorspace"
+  is_exported: true
+  is_fixed_read_only: true
+  namespace: "core_graphics"
+  description: "Add DISPLAY_BT2020 ColorSpace support"
+  bug: "344038816"
+}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6d31578..461a5ae 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -128,6 +128,22 @@
     private static final WeakHashMap<Bitmap, Void> sAllBitmaps = new WeakHashMap<>();
 
     /**
+     * @hide
+     */
+    private static NativeAllocationRegistry getRegistry(boolean malloc, long size) {
+        final long free = nativeGetNativeFinalizer();
+        if (com.android.libcore.readonly.Flags.nativeMetrics()) {
+            Class cls = Bitmap.class;
+            return malloc ? NativeAllocationRegistry.createMalloced(cls, free, size)
+                          : NativeAllocationRegistry.createNonmalloced(cls, free, size);
+        } else {
+            ClassLoader loader = Bitmap.class.getClassLoader();
+            return malloc ? NativeAllocationRegistry.createMalloced(loader, free, size)
+                          : NativeAllocationRegistry.createNonmalloced(loader, free, size);
+        }
+    }
+
+    /**
      * Private constructor that must receive an already allocated native bitmap
      * int (pointer).
      */
@@ -151,7 +167,6 @@
         mWidth = width;
         mHeight = height;
         mRequestPremultiplied = requestPremultiplied;
-
         mNinePatchChunk = ninePatchChunk;
         mNinePatchInsets = ninePatchInsets;
         if (density >= 0) {
@@ -159,17 +174,9 @@
         }
 
         mNativePtr = nativeBitmap;
-
         final int allocationByteCount = getAllocationByteCount();
-        NativeAllocationRegistry registry;
-        if (fromMalloc) {
-            registry = NativeAllocationRegistry.createMalloced(
-                    Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount);
-        } else {
-            registry = NativeAllocationRegistry.createNonmalloced(
-                    Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount);
-        }
-        registry.registerNativeAllocation(this, nativeBitmap);
+        getRegistry(fromMalloc, allocationByteCount).registerNativeAllocation(this, mNativePtr);
+
         synchronized (Bitmap.class) {
           sAllBitmaps.put(this, null);
         }
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 3752257..4c47de0 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -768,7 +768,44 @@
          * </table>
          */
         @FlaggedApi(Flags.FLAG_OK_LAB_COLORSPACE)
-        OK_LAB
+        OK_LAB,
+
+        /**
+         * <p>{@link ColorSpace.Rgb RGB} color space BT.2020 based on Rec. ITU-R BT.2020-1 and IEC 61966-2.1:1999.</p></p>
+         * <table summary="Color space definition">
+         *     <tr>
+         *         <th>Chromaticity</th><th>Red</th><th>Green</th><th>Blue</th><th>White point</th>
+         *     </tr>
+         *     <tr><td>x</td><td>0.708</td><td>0.170</td><td>0.131</td><td>0.3127</td></tr>
+         *     <tr><td>y</td><td>0.292</td><td>0.797</td><td>0.046</td><td>0.3290</td></tr>
+         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
+         *     <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.2020-1</td></tr>
+         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+         *     <tr>
+         *         <td>Opto-electronic transfer function (OETF)</td>
+         *         <td colspan="4">\(\begin{equation}
+         *             C_{DisplayBT2020} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0030186 \\\
+         *             1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0030186 \end{cases}
+         *             \end{equation}\)
+         *         </td>
+         *     </tr>
+         *     <tr>
+         *         <td>Electro-optical transfer function (EOTF)</td>
+         *         <td colspan="4">\(\begin{equation}
+         *             C_{linear} = \begin{cases}\frac{C_{DisplayBT2020}}{12.92} & C_{sRGB} \lt 0.04045 \\\
+         *             \left( \frac{C_{DisplayBT2020} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
+         *             \end{equation}\)
+         *         </td>
+         *     </tr>
+         *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
+         * </table>
+         * <p>
+         *     <img style="display: block; margin: 0 auto;" src="{@docRoot}reference/android/images/graphics/colorspace_bt2020.png" />
+         *     <figcaption style="text-align: center;">BT.2020 (orange) vs sRGB (white)</figcaption>
+         * </p>
+         */
+        @FlaggedApi(Flags.FLAG_DISPLAY_BT2020_COLORSPACE)
+        DISPLAY_BT2020
         // Update the initialization block next to #get(Named) when adding new values
     }
 
@@ -1721,6 +1758,19 @@
                     Named.OK_LAB.ordinal()
             ));
         }
+
+        if (Flags.displayBt2020Colorspace()) {
+            sNamedColorSpaceMap.put(Named.DISPLAY_BT2020.ordinal(), new ColorSpace.Rgb(
+                    "BT 2020",
+                    BT2020_PRIMARIES,
+                    ILLUMINANT_D65,
+                    null,
+                    SRGB_TRANSFER_PARAMETERS,
+                    Named.DISPLAY_BT2020.ordinal()
+            ));
+            sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_BT2020,
+                    Named.DISPLAY_BT2020.ordinal());
+        }
     }
 
     private static double transferHLGOETF(Rgb.TransferParameters params, double x) {
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 671eb6e..ed17fde 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -26,6 +26,7 @@
 import android.graphics.fonts.Font;
 
 import com.android.internal.util.Preconditions;
+import com.android.text.flags.Flags;
 
 import dalvik.annotation.optimization.CriticalNative;
 
@@ -132,6 +133,9 @@
     @NonNull
     public Font getFont(@IntRange(from = 0) int index) {
         Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+        if (Flags.typefaceRedesign()) {
+            return mFonts.get(nGetFontId(mLayoutPtr, index));
+        }
         return mFonts.get(index);
     }
 
@@ -245,20 +249,29 @@
      */
     public PositionedGlyphs(long layoutPtr, float xOffset, float yOffset) {
         mLayoutPtr = layoutPtr;
-        int glyphCount = nGetGlyphCount(layoutPtr);
-        mFonts = new ArrayList<>(glyphCount);
         mXOffset = xOffset;
         mYOffset = yOffset;
 
-        long prevPtr = 0;
-        Font prevFont = null;
-        for (int i = 0; i < glyphCount; ++i) {
-            long ptr = nGetFont(layoutPtr, i);
-            if (prevPtr != ptr) {
-                prevPtr = ptr;
-                prevFont = new Font(ptr);
+        if (Flags.typefaceRedesign()) {
+            int fontCount = nGetFontCount(layoutPtr);
+            mFonts = new ArrayList<>(fontCount);
+            for (int i = 0; i < fontCount; ++i) {
+                mFonts.add(new Font(nGetFontRef(layoutPtr, i)));
             }
-            mFonts.add(prevFont);
+        } else {
+            int glyphCount = nGetGlyphCount(layoutPtr);
+            mFonts = new ArrayList<>(glyphCount);
+
+            long prevPtr = 0;
+            Font prevFont = null;
+            for (int i = 0; i < glyphCount; ++i) {
+                long ptr = nGetFont(layoutPtr, i);
+                if (prevPtr != ptr) {
+                    prevPtr = ptr;
+                    prevFont = new Font(ptr);
+                }
+                mFonts.add(prevFont);
+            }
         }
 
         NoImagePreloadHolder.REGISTRY.registerNativeAllocation(this, layoutPtr);
@@ -290,6 +303,12 @@
     private static native float nGetWeightOverride(long minikinLayout, int i);
     @CriticalNative
     private static native float nGetItalicOverride(long minikinLayout, int i);
+    @CriticalNative
+    private static native int nGetFontCount(long minikinLayout);
+    @CriticalNative
+    private static native long nGetFontRef(long minikinLayout, int fontId);
+    @CriticalNative
+    private static native int nGetFontId(long minikinLayout, int glyphIndex);
 
     @Override
     public boolean equals(Object o) {
diff --git a/keystore/java/android/security/OWNERS b/keystore/java/android/security/OWNERS
index ed30587..32759b2 100644
--- a/keystore/java/android/security/OWNERS
+++ b/keystore/java/android/security/OWNERS
@@ -1 +1,2 @@
 per-file *.java,*.aidl = eranm@google.com,pgrafov@google.com,rubinxu@google.com
+per-file KeyStoreManager.java = mpgroover@google.com
diff --git a/keystore/java/android/security/keystore/KeyStoreManager.java b/keystore/java/android/security/keystore/KeyStoreManager.java
new file mode 100644
index 0000000..197aaba
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeyStoreManager.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.security.KeyStore2;
+import android.security.KeyStoreException;
+import android.security.keystore2.AndroidKeyStoreProvider;
+import android.security.keystore2.AndroidKeyStorePublicKey;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyPermission;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.ByteArrayInputStream;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class provides methods for interacting with keys stored within the
+ * <a href="/privacy-and-security/keystore">Android Keystore</a>.
+ */
+@FlaggedApi(android.security.Flags.FLAG_KEYSTORE_GRANT_API)
+@SystemService(Context.KEYSTORE_SERVICE)
+public class KeyStoreManager {
+    private static final String TAG = "KeyStoreManager";
+
+    private static final Object sInstanceLock = new Object();
+    @GuardedBy("sInstanceLock")
+    private static KeyStoreManager sInstance;
+
+    private final KeyStore2 mKeyStore2;
+
+    /**
+     * Private constructor to ensure only a single instance is created.
+     */
+    private KeyStoreManager() {
+        mKeyStore2 = KeyStore2.getInstance();
+    }
+
+    /**
+     * Returns the single instance of the {@code KeyStoreManager}.
+     *
+     * @hide
+     */
+    public static KeyStoreManager getInstance() {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                sInstance = new KeyStoreManager();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Grants access to the key owned by the calling app stored under the specified {@code alias}
+     * to another app on the device with the provided {@code uid}.
+     *
+     * <p>This method supports granting access to instances of both {@link javax.crypto.SecretKey}
+     * and {@link java.security.PrivateKey}. The resulting ID will persist across reboots and can be
+     * used by the grantee app for the life of the key or until access is revoked with {@link
+     * #revokeKeyAccess(String, int)}.
+     *
+     * <p>If the provided {@code alias} does not correspond to a key in the Android KeyStore, then
+     * an {@link UnrecoverableKeyException} is thrown.
+     *
+     * @param alias the alias of the key to be granted to another app
+     * @param uid   the uid of the app to which the key should be granted
+     * @return the ID of the granted key; this can be shared with the specified app, and that
+     * app can use {@link #getGrantedKeyFromId(long)} to access the key
+     * @throws UnrecoverableKeyException if the specified key cannot be recovered
+     * @throws KeyStoreException if an error is encountered when attempting to grant access to
+     * the key
+     * @see #getGrantedKeyFromId(long)
+     */
+    public long grantKeyAccess(@NonNull String alias, int uid)
+            throws KeyStoreException, UnrecoverableKeyException {
+        KeyDescriptor keyDescriptor = createKeyDescriptorFromAlias(alias);
+        final int grantAccessVector = KeyPermission.USE | KeyPermission.GET_INFO;
+        // When a key is in the GRANT domain, the nspace field of the KeyDescriptor contains its ID.
+        KeyDescriptor result = null;
+        try {
+            result = mKeyStore2.grant(keyDescriptor, uid, grantAccessVector);
+        } catch (KeyStoreException e) {
+            // If the provided alias does not correspond to a valid key in the KeyStore, then throw
+            // an UnrecoverableKeyException to remain consistent with other APIs in this class.
+            if (e.getNumericErrorCode() == KeyStoreException.ERROR_KEY_DOES_NOT_EXIST) {
+                throw new UnrecoverableKeyException("No key found by the given alias");
+            }
+            throw e;
+        }
+        if (result == null) {
+            Log.e(TAG, "Received a null KeyDescriptor from grant");
+            throw new KeyStoreException(KeyStoreException.ERROR_INTERNAL_SYSTEM_ERROR,
+                    "No ID was returned for the grant request for alias " + alias + " to uid "
+                            + uid);
+        } else if (result.domain != Domain.GRANT) {
+            Log.e(TAG, "Received a result outside the grant domain: " + result.domain);
+            throw new KeyStoreException(KeyStoreException.ERROR_INTERNAL_SYSTEM_ERROR,
+                    "Unable to obtain a grant ID for alias " + alias + " to uid " + uid);
+        }
+        return result.nspace;
+    }
+
+    /**
+     * Revokes access to the key in the app's namespace stored under the specified {@code
+     * alias} that was previously granted to another app on the device with the provided
+     * {@code uid}.
+     *
+     * <p>If the provided {@code alias} does not correspond to a key in the Android KeyStore, then
+     * an {@link UnrecoverableKeyException} is thrown.
+     *
+     * @param alias the alias of the key to be revoked from another app
+     * @param uid   the uid of the app from which the key access should be revoked
+     * @throws UnrecoverableKeyException if the specified key cannot be recovered
+     * @throws KeyStoreException if an error is encountered when attempting to revoke access
+     * to the key
+     */
+    public void revokeKeyAccess(@NonNull String alias, int uid)
+            throws KeyStoreException, UnrecoverableKeyException {
+        KeyDescriptor keyDescriptor = createKeyDescriptorFromAlias(alias);
+        try {
+            mKeyStore2.ungrant(keyDescriptor, uid);
+        } catch (KeyStoreException e) {
+            // If the provided alias does not correspond to a valid key in the KeyStore, then throw
+            // an UnrecoverableKeyException to remain consistent with other APIs in this class.
+            if (e.getNumericErrorCode() == KeyStoreException.ERROR_KEY_DOES_NOT_EXIST) {
+                throw new UnrecoverableKeyException("No key found by the given alias");
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Returns the key with the specified {@code id} that was previously shared with the
+     * app.
+     *
+     * <p>This method can return instances of both {@link javax.crypto.SecretKey} and {@link
+     * java.security.PrivateKey}. If a key with the provide {@code id} has not been granted to the
+     * caller, then an {@link UnrecoverableKeyException} is thrown.
+     *
+     * @param id the ID of the key that was shared with the app
+     * @return the {@link Key} that was shared with the app
+     * @throws UnrecoverableKeyException          if the specified key cannot be recovered
+     * @throws KeyPermanentlyInvalidatedException if the specified key was authorized to only
+     *                                            be used if the user has been authenticated and a
+     *                                            change has been made to the users
+     *                                            lockscreen or biometric enrollment that
+     *                                            permanently invalidates the key
+     * @see #grantKeyAccess(String, int)
+     */
+    public @NonNull Key getGrantedKeyFromId(long id)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+        Key result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore2, null,
+                id, Domain.GRANT);
+        if (result == null) {
+            throw new UnrecoverableKeyException("No key found by the given alias");
+        }
+        return result;
+    }
+
+    /**
+     * Returns a {@link KeyPair} containing the public and private key associated with
+     * the key that was previously shared with the app under the provided {@code id}.
+     *
+     * <p>If a {@link java.security.PrivateKey} has not been granted to the caller with the
+     * specified {@code id}, then an {@link UnrecoverableKeyException} is thrown.
+     *
+     * @param id the ID of the private key that was shared with the app
+     * @return a KeyPair containing the public and private key shared with the app
+     * @throws UnrecoverableKeyException          if the specified key cannot be recovered
+     * @throws KeyPermanentlyInvalidatedException if the specified key was authorized to only
+     *                                            be used if the user has been authenticated and a
+     *                                            change has been made to the users
+     *                                            lockscreen or biometric enrollment that
+     *                                            permanently invalidates the key
+     */
+    public @NonNull KeyPair getGrantedKeyPairFromId(long id)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+        KeyDescriptor keyDescriptor = createKeyDescriptorFromId(id, Domain.GRANT);
+        return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(mKeyStore2,
+                keyDescriptor);
+    }
+
+    /**
+     * Returns a {@link List} of {@link X509Certificate} instances representing the certificate
+     * chain for the key that was previously shared with the app under the provided {@code id}.
+     *
+     * <p>If a {@link java.security.PrivateKey} has not been granted to the caller with the
+     * specified {@code id}, then an {@link UnrecoverableKeyException} is thrown.
+     *
+     * @param id the ID of the asymmetric key that was shared with the app
+     * @return a List of X509Certificates with the certificate at index 0 corresponding to
+     * the private key shared with the app
+     * @throws UnrecoverableKeyException          if the specified key cannot be recovered
+     * @throws KeyPermanentlyInvalidatedException if the specified key was authorized to only
+     *                                            be used if the user has been authenticated and a
+     *                                            change has been made to the users
+     *                                            lockscreen or biometric enrollment that
+     *                                            permanently invalidates the key
+     * @see #grantKeyAccess(String, int)
+     */
+    // Java APIs should prefer mutable collection return types with the exception being
+    // Collection.empty return types.
+    @SuppressWarnings("MixedMutabilityReturnType")
+    public @NonNull List<X509Certificate> getGrantedCertificateChainFromId(long id)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+        KeyDescriptor keyDescriptor = createKeyDescriptorFromId(id, Domain.GRANT);
+        KeyPair keyPair = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(mKeyStore2,
+                keyDescriptor);
+        PublicKey keyStoreKey = keyPair.getPublic();
+        if (keyStoreKey instanceof AndroidKeyStorePublicKey) {
+            AndroidKeyStorePublicKey androidKeyStorePublicKey =
+                    (AndroidKeyStorePublicKey) keyStoreKey;
+            byte[] certBytes = androidKeyStorePublicKey.getCertificate();
+            X509Certificate cert = getCertificate(certBytes);
+            // If the leaf certificate is null, then a chain should not exist either
+            if (cert == null) {
+                return Collections.emptyList();
+            }
+            List<X509Certificate> result = new ArrayList<>();
+            result.add(cert);
+            byte[] certificateChain = androidKeyStorePublicKey.getCertificateChain();
+            Collection<X509Certificate> certificates = getCertificates(certificateChain);
+            result.addAll(certificates);
+            return result;
+        } else {
+            Log.e(TAG, "keyStoreKey is not of the expected type: " + keyStoreKey);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns an {@link X509Certificate} instance from the provided {@code certificate} byte
+     * encoding of the certificate, or null if the provided encoding is null.
+     */
+    private static X509Certificate getCertificate(byte[] certificate) {
+        X509Certificate result = null;
+        if (certificate != null) {
+            try {
+                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+                result = (X509Certificate) certificateFactory.generateCertificate(
+                        new ByteArrayInputStream(certificate));
+            } catch (Exception e) {
+                Log.e(TAG, "Caught an exception parsing the certificate: ", e);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns a {@link Collection} of {@link X509Certificate} instances from the provided
+     * {@code certificateChain} byte encoding of the certificates, or null if the provided
+     * encoding is null.
+     */
+    private static Collection<X509Certificate> getCertificates(byte[] certificateChain) {
+        if (certificateChain != null) {
+            try {
+                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+                Collection<X509Certificate> certificates =
+                        (Collection<X509Certificate>) certificateFactory.generateCertificates(
+                                new ByteArrayInputStream(certificateChain));
+                if (certificates == null) {
+                    Log.e(TAG, "Received null certificates from a non-null certificateChain");
+                    return Collections.emptyList();
+                }
+                return certificates;
+            } catch (Exception e) {
+                Log.e(TAG, "Caught an exception parsing the certs: ", e);
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns a new {@link KeyDescriptor} instance in the app domain / namespace with the {@code
+     * alias} set to the provided value.
+     */
+    private static KeyDescriptor createKeyDescriptorFromAlias(String alias) {
+        KeyDescriptor keyDescriptor = new KeyDescriptor();
+        keyDescriptor.domain = Domain.APP;
+        keyDescriptor.nspace = KeyProperties.NAMESPACE_APPLICATION;
+        keyDescriptor.alias = alias;
+        keyDescriptor.blob = null;
+        return keyDescriptor;
+    }
+
+    /**
+     * Returns a new {@link KeyDescriptor} instance in the provided {@code domain} with the nspace
+     * field set to the provided {@code id}.
+     */
+    private static KeyDescriptor createKeyDescriptorFromId(long id, int domain) {
+        KeyDescriptor keyDescriptor = new KeyDescriptor();
+        keyDescriptor.domain = domain;
+        keyDescriptor.nspace = id;
+        keyDescriptor.alias = null;
+        keyDescriptor.blob = null;
+        return keyDescriptor;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 99100de..dcc8844 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -17,6 +17,7 @@
 package android.security.keystore2;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.security.KeyStore2;
 import android.security.KeyStoreSecurityLevel;
 import android.security.keymaster.KeymasterDefs;
@@ -335,11 +336,11 @@
     }
 
     /**
-     * Loads an an AndroidKeyStoreKey from the AndroidKeyStore backend.
+     * Loads an AndroidKeyStoreKey from the AndroidKeyStore backend.
      *
      * @param keyStore The keystore2 backend.
      * @param alias The alias of the key in the Keystore database.
-     * @param namespace The a Keystore namespace. This is used by system api only to request
+     * @param namespace The Keystore namespace. This is used by system api only to request
      *         Android system specific keystore namespace, which can be configured
      *         in the device's SEPolicy. Third party apps and most system components
      *         set this parameter to -1 to indicate their application specific namespace.
@@ -351,14 +352,40 @@
     public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
             @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
             throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
-        KeyDescriptor descriptor = new KeyDescriptor();
+        int descriptorNamespace;
+        int descriptorDomain;
         if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
-            descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored;
-            descriptor.domain = Domain.APP;
+            descriptorNamespace = KeyProperties.NAMESPACE_APPLICATION; // ignored;
+            descriptorDomain = Domain.APP;
         } else {
-            descriptor.nspace = namespace;
-            descriptor.domain = Domain.SELINUX;
+            descriptorNamespace = namespace;
+            descriptorDomain = Domain.SELINUX;
         }
+        return loadAndroidKeyStoreKeyFromKeystore(keyStore, alias, descriptorNamespace,
+                descriptorDomain);
+    }
+
+    /**
+     * Loads an AndroidKeyStoreKey from the AndroidKeyStore backend.
+     *
+     * @param keyStore The keystore2 backend
+     * @param alias The alias of the key in the Keystore database
+     * @param namespace The Keystore namespace. This is used by system api only to request
+     *         Android system specific keystore namespace, which can be configured
+     *         in the device's SEPolicy. Third party apps and most system components
+     *         set this parameter to -1 to indicate their application specific namespace.
+     *         See <a href="https://source.android.com/security/keystore#access-control">
+     *             Keystore 2.0 access control</a>
+     * @param domain The Keystore domain
+     * @return an AndroidKeyStoreKey corresponding to the provided values for the KeyDescriptor
+     * @hide
+     */
+    public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(@NonNull KeyStore2 keyStore,
+            @Nullable String alias, long namespace, int domain)
+            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
+        KeyDescriptor descriptor = new KeyDescriptor();
+        descriptor.nspace = namespace;
+        descriptor.domain = domain;
         descriptor.alias = alias;
         descriptor.blob = null;
 
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
index 0b3be32..bcf619b 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
@@ -44,6 +44,22 @@
         mEncoded = x509EncodedForm;
     }
 
+    /**
+     * Returns the byte array encoding of the certificate corresponding to this public key.
+     * @hide
+     */
+    public byte[] getCertificate() {
+        return mCertificate;
+    }
+
+    /**
+     * Returns the byte array encoding of the certificate chain for this public key.
+     * @hide
+     */
+    public byte[] getCertificateChain() {
+        return mCertificateChain;
+    }
+
     abstract AndroidKeyStorePrivateKey getPrivateKey();
 
     @Override
diff --git a/libcore-readonly.aconfig b/libcore-readonly.aconfig
new file mode 100644
index 0000000..3cda92c
--- /dev/null
+++ b/libcore-readonly.aconfig
@@ -0,0 +1,25 @@
+package: "com.android.libcore.readonly"
+container: "system"
+
+# These are the read-only version of the aconfig flags in com.android.libcore
+# that will be built with 'force-read-only' mode.
+# See b/368409430 - these flags will be removed once the new aconfig API landed.
+flag {
+    namespace: "core_libraries"
+    name: "post_cleanup_apis"
+    is_exported: false
+    description: "This flag includes APIs to add/remove/call callbacks post-cleanup"
+    bug: "331243037"
+    # APIs provided by a mainline module can only use a frozen flag.
+    is_fixed_read_only: true
+}
+
+flag {
+    namespace: "core_libraries"
+    name: "native_metrics"
+    is_exported: false
+    description: "This flag includes APIs fo maintaining and exposing native allocation metrics"
+    bug: "331243037"
+    # APIs provided by a mainline module can only use a frozen flag.
+    is_fixed_read_only: true
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 7f11fea..2ab0310 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -57,9 +57,9 @@
      */
     private static final int NO_LEVEL_OVERRIDE = -1;
 
-    private static final int EXTENSIONS_VERSION_V7 = 7;
+    private static final int EXTENSIONS_VERSION_V8 = 8;
 
-    private static final int EXTENSIONS_VERSION_V6 = 6;
+    private static final int EXTENSIONS_VERSION_V7 = 7;
 
     private final Object mLock = new Object();
     private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
@@ -80,12 +80,10 @@
      */
     @VisibleForTesting
     static int getExtensionsVersionCurrentPlatform() {
-        if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
-            // Activity Embedding animation customization is the only major feature for v7.
-            return EXTENSIONS_VERSION_V7;
-        } else {
-            return EXTENSIONS_VERSION_V6;
+        if (Flags.aeBackStackRestore()) {
+            return EXTENSIONS_VERSION_V8;
         }
+        return EXTENSIONS_VERSION_V7;
     }
 
     private String generateLogMessage() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
index e3a1d8a..6ad2f08 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
@@ -148,8 +148,12 @@
             final TaskFragmentInfo info = mTaskFragmentInfos.valueAt(i);
             mPresenter.deleteTaskFragment(wct, info.getFragmentToken());
         }
-        mPresenter.setSavedState(new Bundle());
 
+        removeSavedState();
+    }
+
+    private void removeSavedState() {
+        mPresenter.setSavedState(new Bundle());
         mParcelableTaskContainerDataList.clear();
         mTaskFragmentInfos.clear();
         mTaskFragmentParentInfos.clear();
@@ -169,6 +173,19 @@
             return false;
         }
 
+        if (mTaskFragmentParentInfos.size() == 0) {
+            // No Task left in the WM hierarchy, remove the states and no need to restore.
+            if (DEBUG) Log.d(TAG, "Remove save states due to no task to restore.");
+            removeSavedState();
+            return false;
+        }
+
+        final ArrayList<Integer> taskIdsInSystem = new ArrayList<>();
+        for (int i = mTaskFragmentParentInfos.size() - 1; i >= 0; --i) {
+            final TaskFragmentParentInfo parentInfo = mTaskFragmentParentInfos.valueAt(i);
+            taskIdsInSystem.add(parentInfo.getTaskId());
+        }
+
         if (DEBUG) Log.d(TAG, "Rebuilding TaskContainers.");
         final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>();
         for (EmbeddingRule rule : rules) {
@@ -190,6 +207,14 @@
             }
 
             mParcelableTaskContainerDataList.remove(parcelableTaskContainerData);
+            if (!taskIdsInSystem.contains(parcelableTaskContainerData.mTaskId)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Rebuilding TaskContainer abort! Not existed. Task#"
+                            + parcelableTaskContainerData.mTaskId);
+                }
+                continue;
+            }
+
             final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData,
                     mController, mTaskFragmentInfos);
             if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 882a8d0..ad194f7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -1255,7 +1255,8 @@
 
             // Update divider line surface visibility and color.
             // If a container is fully expanded, the divider line is invisible unless dragging.
-            final boolean isDividerLineVisible = !mProperties.mIsDraggableExpandType || mIsDragging;
+            final boolean isDividerLineVisible = mProperties.mDividerWidthPx > 0
+                    && (!mProperties.mIsDraggableExpandType || mIsDragging);
             t.setVisibility(mDividerLineSurface, isDividerLineVisible);
             t.setColor(mDividerLineSurface, colorToFloatArray(
                     Color.valueOf(mProperties.mDividerAttributes.getDividerColor())));
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 8345b40..3368e2e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -239,6 +239,11 @@
         mActivityStartMonitor = new ActivityStartMonitor();
         instrumentation.addMonitor(mActivityStartMonitor);
         foldingFeatureProducer.addDataChangedCallback(new FoldingFeatureListener());
+
+        synchronized (mLock) {
+            // Abort the restoration if any and the application already has running activities.
+            abortRebuildingTaskContainersIfNeeded(null /* launchingActivity */);
+        }
     }
 
     private class FoldingFeatureListener
@@ -285,6 +290,10 @@
                 return;
             }
 
+            if (abortRebuildingTaskContainersIfNeeded(null /* launchingActivity */)) {
+                return;
+            }
+
             try {
                 final TransactionRecord transactionRecord =
                         mTransactionManager.startNewTransaction();
@@ -2883,6 +2892,53 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private boolean abortRebuildingTaskContainersIfNeeded(@Nullable Activity launchingActivity) {
+        if (mPresenter == null || !mPresenter.isWaitingToRebuildTaskContainers()) {
+            return false;
+        }
+
+        final ActivityThread activityThread = ActivityThread.currentActivityThread();
+        if (activityThread == null) {
+            return false;
+        }
+
+        final Activity lastCreatedActivity = activityThread.getLastCreatedActivity();
+        final Activity activity =
+                launchingActivity != null ? launchingActivity : lastCreatedActivity;
+        if (activity == null) {
+            return false;
+        }
+
+        Log.w(TAG, "Rebuilding aborted, clean up.");
+
+        // Retrieve the Task intent.
+        final int taskId = getTaskId(activity);
+
+        // Clean up and abort the restoration
+        // TODO(b/369488857): also to remove the non-organized activities in the Task?
+        final TransactionRecord transactionRecord =
+                mTransactionManager.startNewTransaction();
+        final WindowContainerTransaction wct = transactionRecord.getTransaction();
+        mPresenter.abortTaskContainerRebuilding(wct);
+        transactionRecord.apply(false /* shouldApplyIndependently */);
+
+        // Start the Task root activity if the task is now empty.
+        ActivityManager.RecentTaskInfo taskInfo = null;
+        final ActivityManager am = activity.getSystemService(ActivityManager.class);
+        final List<ActivityManager.AppTask> appTasks = am.getAppTasks();
+        for (ActivityManager.AppTask appTask : appTasks) {
+            if (appTask.getTaskInfo().taskId == taskId) {
+                taskInfo = appTask.getTaskInfo();
+                break;
+            }
+        }
+        if (taskInfo != null && !taskInfo.isRunning) {
+            activity.startActivity(taskInfo.baseIntent.cloneFilter());
+        }
+        return true;
+    }
+
     private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
 
         @Override
@@ -2894,33 +2950,7 @@
                 return;
             }
             synchronized (mLock) {
-                if (mPresenter.isWaitingToRebuildTaskContainers()) {
-                    Log.w(TAG, "Rebuilding aborted, clean up and restart");
-
-                    // Retrieve the Task intent.
-                    final int taskId = getTaskId(activity);
-                    Intent taskIntent = null;
-                    final ActivityManager am = activity.getSystemService(ActivityManager.class);
-                    final List<ActivityManager.AppTask> appTasks = am.getAppTasks();
-                    for (ActivityManager.AppTask appTask : appTasks) {
-                        if (appTask.getTaskInfo().taskId == taskId) {
-                            taskIntent = appTask.getTaskInfo().baseIntent.cloneFilter();
-                            break;
-                        }
-                    }
-
-                    // Clean up and abort the restoration
-                    // TODO(b/369488857): also to remove the non-organized activities in the Task?
-                    final TransactionRecord transactionRecord =
-                            mTransactionManager.startNewTransaction();
-                    final WindowContainerTransaction wct = transactionRecord.getTransaction();
-                    mPresenter.abortTaskContainerRebuilding(wct);
-                    transactionRecord.apply(false /* shouldApplyIndependently */);
-
-                    // Start the Task root activity.
-                    if (taskIntent != null) {
-                        activity.startActivity(taskIntent);
-                    }
+                if (abortRebuildingTaskContainersIfNeeded(activity)) {
                     return;
                 }
 
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 3b739c3..1260796 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -24,6 +24,7 @@
     <uses-permission android:name="android.permission.WAKEUP_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+    <uses-permission android:name="android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION" />
 
     <application>
         <activity
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 96ffa03..52ce8cb 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -67,6 +67,7 @@
 
     private val context = ApplicationProvider.getApplicationContext<Context>()
     private lateinit var positioner: BubblePositioner
+    private lateinit var bubbleLogger: BubbleLogger
     private lateinit var iconFactory: BubbleIconFactory
     private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
     private lateinit var bubbleStackView: BubbleStackView
@@ -96,10 +97,11 @@
                 )
             )
         positioner = BubblePositioner(context, windowManager)
+        bubbleLogger = BubbleLogger(UiEventLoggerFake())
         bubbleData =
             BubbleData(
                 context,
-                BubbleLogger(UiEventLoggerFake()),
+                bubbleLogger,
                 positioner,
                 BubbleEducationController(context),
                 shellExecutor,
@@ -394,6 +396,7 @@
             expandedViewManager,
             bubbleTaskViewFactory,
             positioner,
+            bubbleLogger,
             bubbleStackView,
             null,
             iconFactory,
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 9fdde128..712cc7c 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -29,6 +29,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
+import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.protolog.ProtoLog
 import com.android.internal.statusbar.IStatusBarService
 import com.android.launcher3.icons.BubbleIconFactory
@@ -70,6 +71,7 @@
     private lateinit var bgExecutor: TestExecutor
     private lateinit var bubbleStackView: BubbleStackView
     private lateinit var bubblePositioner: BubblePositioner
+    private lateinit var bubbleLogger: BubbleLogger
     private lateinit var expandedViewManager: BubbleExpandedViewManager
 
     private val bubbleTaskViewFactory = BubbleTaskViewFactory {
@@ -103,10 +105,11 @@
                 mainExecutor
             )
         bubblePositioner = BubblePositioner(context, windowManager)
+        bubbleLogger = BubbleLogger(UiEventLoggerFake())
         val bubbleData =
             BubbleData(
                 context,
-                mock<BubbleLogger>(),
+                bubbleLogger,
                 bubblePositioner,
                 BubbleEducationController(context),
                 mainExecutor,
@@ -138,7 +141,7 @@
                 WindowManagerShellWrapper(mainExecutor),
                 mock<UserManager>(),
                 mock<LauncherApps>(),
-                mock<BubbleLogger>(),
+                bubbleLogger,
                 mock<TaskStackListenerImpl>(),
                 mock<ShellTaskOrganizer>(),
                 bubblePositioner,
@@ -314,6 +317,7 @@
             expandedViewManager,
             bubbleTaskViewFactory,
             bubblePositioner,
+            bubbleLogger,
             bubbleStackView,
             null /* layerView */,
             iconFactory,
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index 35d459f..fa9d2ba 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager
 import android.content.Context
+import android.content.pm.ShortcutInfo
 import android.graphics.Insets
 import android.graphics.Rect
 import android.view.LayoutInflater
@@ -27,11 +28,13 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.Bubble
 import com.android.wm.shell.bubbles.BubbleData
 import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+import com.android.wm.shell.bubbles.BubbleLogger
 import com.android.wm.shell.bubbles.BubblePositioner
 import com.android.wm.shell.bubbles.BubbleTaskView
 import com.android.wm.shell.bubbles.BubbleTaskViewFactory
@@ -43,11 +46,14 @@
 import com.android.wm.shell.taskview.TaskView
 import com.android.wm.shell.taskview.TaskViewTaskController
 import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 import java.util.Collections
 import java.util.concurrent.Executor
@@ -70,14 +76,18 @@
     private lateinit var expandedViewManager: BubbleExpandedViewManager
     private lateinit var positioner: BubblePositioner
     private lateinit var bubbleTaskView: BubbleTaskView
+    private lateinit var bubble: Bubble
 
     private lateinit var bubbleExpandedView: BubbleBarExpandedView
     private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null
     private var regionSamplingProvider: TestRegionSamplingProvider? = null
 
+    private val bubbleLogger = spy(BubbleLogger(UiEventLoggerFake()))
+
     @Before
     fun setUp() {
         ProtoLog.REQUIRE_PROTOLOGTOOL = false
+        ProtoLog.init()
         mainExecutor = TestExecutor()
         bgExecutor = TestExecutor()
         positioner = BubblePositioner(context, windowManager)
@@ -106,11 +116,12 @@
         bubbleExpandedView.initialize(
             expandedViewManager,
             positioner,
+            bubbleLogger,
             false /* isOverflow */,
             bubbleTaskView,
             mainExecutor,
             bgExecutor,
-            regionSamplingProvider
+            regionSamplingProvider,
         )
 
         getInstrumentation().runOnMainSync(Runnable {
@@ -118,6 +129,20 @@
             // Helper should be created once attached to window
             testableRegionSamplingHelper = regionSamplingProvider!!.helper
         })
+
+        bubble = Bubble(
+            "key",
+            ShortcutInfo.Builder(context, "id").build(),
+            100 /* desiredHeight */,
+            0 /* desiredHeightResId */,
+            "title",
+            0 /* taskId */,
+            null /* locus */,
+            true /* isDismissable */,
+            directExecutor(),
+            directExecutor()
+        ) {}
+        bubbleExpandedView.update(bubble)
     }
 
     @After
@@ -191,6 +216,16 @@
         assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
     }
 
+    @Test
+    fun testEventLogging_dismissBubbleViaAppMenu() {
+        getInstrumentation().runOnMainSync { bubbleExpandedView.handleView.performClick() }
+        val dismissMenuItem =
+            bubbleExpandedView.findViewWithTag<View>(BubbleBarMenuView.DISMISS_ACTION_TAG)
+        assertThat(dismissMenuItem).isNotNull()
+        getInstrumentation().runOnMainSync { dismissMenuItem.performClick() }
+        verify(bubbleLogger).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU)
+    }
+
     private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
         override fun create(): BubbleTaskView {
             val taskViewTaskController = mock<TaskViewTaskController>()
diff --git a/libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml b/libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml
index b74d922..2d5597e 100644
--- a/libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml
+++ b/libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml
@@ -19,9 +19,15 @@
     android:width="24dp"
     android:height="24dp"
     android:tint="?android:attr/textColorTertiary"
-    android:viewportHeight="960"
-    android:viewportWidth="960">
+    android:viewportHeight="24"
+    android:viewportWidth="24">
     <path
         android:fillColor="@android:color/system_on_tertiary_container_light"
-        android:pathData="M419,880Q391,880 366.5,868Q342,856 325,834L107,557L126,537Q146,516 174,512Q202,508 226,523L300,568L300,240Q300,223 311.5,211.5Q323,200 340,200Q357,200 369,211.5Q381,223 381,240L381,712L284,652L388,785Q394,792 402,796Q410,800 419,800L640,800Q673,800 696.5,776.5Q720,753 720,720L720,560Q720,543 708.5,531.5Q697,520 680,520L461,520L461,440L680,440Q730,440 765,475Q800,510 800,560L800,720Q800,786 753,833Q706,880 640,880L419,880ZM167,340Q154,318 147,292.5Q140,267 140,240Q140,157 198.5,98.5Q257,40 340,40Q423,40 481.5,98.5Q540,157 540,240Q540,267 533,292.5Q526,318 513,340L444,300Q452,286 456,271.5Q460,257 460,240Q460,190 425,155Q390,120 340,120Q290,120 255,155Q220,190 220,240Q220,257 224,271.5Q228,286 236,300L167,340ZM502,620L502,620L502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620L502,620Z" />
-</vector>
+        android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/>
+    <path
+        android:fillColor="@android:color/system_on_tertiary_container_light"
+        android:pathData="M8,17h8v2h-8z"/>
+    <path
+        android:fillColor="@android:color/system_on_tertiary_container_light"
+        android:pathData="M12,2C7.86,2 4.5,5.36 4.5,9.5c0,3.82 2.66,5.86 3.77,6.5h7.46c1.11,-0.64 3.77,-2.68 3.77,-6.5C19.5,5.36 16.14,2 12,2z"/>
+  </vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml
index a12a746..473236c 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml
@@ -18,7 +18,7 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item>
         <shape android:shape="rectangle">
-            <corners android:radius="30dp" />
+            <corners android:radius="28dp" />
             <solid android:color="@android:color/system_tertiary_fixed" />
         </shape>
     </item>
diff --git a/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml
similarity index 100%
rename from libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_dismiss_button_background.xml
rename to libs/WindowManager/Shell/res/drawable/open_by_default_settings_dialog_confirm_button_background.xml
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index aeb734e..5609663 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -184,6 +184,7 @@
             android:layout_height="20dp"
             android:layout_gravity="end|center_vertical"
             android:layout_marginEnd="16dp"
+            android:layout_marginStart="10dp"
             android:contentDescription="@string/open_by_default_settings_text"
             android:src="@drawable/desktop_mode_ic_handle_menu_open_by_default_settings"
             android:tint="?androidprv:attr/materialColorOnSurface"/>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 35ef239..3759618 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -38,7 +38,7 @@
             <Button
                 android:layout_width="94dp"
                 android:layout_height="60dp"
-                android:id="@+id/maximize_menu_maximize_button"
+                android:id="@+id/maximize_menu_size_toggle_button"
                 style="?android:attr/buttonBarButtonStyle"
                 android:stateListAnimator="@null"
                 android:importantForAccessibility="yes"
@@ -48,7 +48,7 @@
                 android:alpha="0"/>
 
             <TextView
-                android:id="@+id/maximize_menu_maximize_window_text"
+                android:id="@+id/maximize_menu_size_toggle_button_text"
                 android:layout_width="94dp"
                 android:layout_height="18dp"
                 android:textSize="11sp"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml
index a269b9e..fd75827 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml
@@ -26,6 +26,7 @@
         android:id="@+id/arrow_icon"
         android:layout_width="10dp"
         android:layout_height="12dp"
+        android:elevation="2dp"
         android:layout_gravity="center_vertical"
         android:src="@drawable/desktop_windowing_education_tooltip_left_arrow" />
 
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml
index 09a049c..42f955d 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml
@@ -16,16 +16,18 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/tooltip_container"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
     android:background="@drawable/desktop_windowing_education_tooltip_background"
     android:orientation="horizontal"
+    android:elevation="2dp"
     android:padding="@dimen/desktop_windowing_education_tooltip_padding">
 
     <ImageView
         android:id="@+id/tooltip_icon"
         android:layout_width="32dp"
         android:layout_height="32dp"
+        android:layout_margin="8dp"
         android:layout_gravity="center_vertical"
         android:src="@drawable/app_handle_education_tooltip_icon" />
 
@@ -34,9 +36,9 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_marginStart="2dp"
+        android:layout_marginHorizontal="8dp"
         android:lineHeight="20dp"
-        android:maxWidth="150dp"
+        android:maxWidth="220dp"
         android:textColor="@android:color/system_on_tertiary_container_light"
         android:textFontWeight="500"
         android:textSize="14sp" />
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml
index c73c1da..83d7ef7 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml
@@ -25,6 +25,7 @@
         android:id="@+id/arrow_icon"
         android:layout_width="12dp"
         android:layout_height="9dp"
+        android:elevation="2dp"
         android:layout_gravity="center_horizontal"
         android:src="@drawable/desktop_windowing_education_tooltip_top_arrow" />
 
diff --git a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
index 8ff382b..b5bceda 100644
--- a/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
+++ b/libs/WindowManager/Shell/res/layout/open_by_default_settings_dialog.xml
@@ -111,7 +111,7 @@
                 </RadioGroup>
 
                 <Button
-                    android:id="@+id/open_by_default_settings_dialog_dismiss_button"
+                    android:id="@+id/open_by_default_settings_dialog_confirm_button"
                     android:layout_width="wrap_content"
                     android:layout_height="36dp"
                     android:text="@string/open_by_default_dialog_dismiss_button_text"
@@ -122,7 +122,7 @@
                     android:textSize="14sp"
                     android:textFontWeight="500"
                     android:textColor="?androidprv:attr/materialColorOnPrimary"
-                    android:background="@drawable/open_by_default_settings_dialog_dismiss_button_background"/>
+                    android:background="@drawable/open_by_default_settings_dialog_confirm_button_background"/>
             </LinearLayout>
         </ScrollView>
     </FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 582c1a2..a0718d9 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tik om die appkieslys oop te maak"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tik om verskeie apps saam te wys"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Keer terug na volskerm van die appkieslys af"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Die appkieslys kan hier gevind word"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Maak rekenaaraansig oop om veelvuldige apps saam oop te maak"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Keer enige tyd terug na volskerm vanaf die appkieslys"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sien en doen meer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep ’n ander app in vir verdeelde skerm"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik buite ’n program om dit te herposisioneer"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeer"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Spring na links"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Spring na regs"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Maak By Verstek Oop-instellings"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Kies hoe om webskakels vir hierdie app oop te maak"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In die app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In jou blaaier"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 0798c9a..f160c70b 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"የመተግበሪያ ምናሌውን ለመክፈት መታ ያድርጉ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"በርካታ መተግበሪያዎችን በአንድ ላይ ለማየት መታ ያድርጉ"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ከመተግበሪያ ምናሌው ወደ ሙሉ ማያ ገፅ ይመለሱ"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"የመተግበሪያ ምናሌው እዚህ መገኘት ይችላል"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"በርካታ መተግበሪያዎችን በአንድ ላይ ለመክፈት ወደ የዴስክቶፕ እይታ ይግቡ"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"በማንኛውም ጊዜ ከመተግበሪያ ምናሌው ላይ ወደ ሙሉ ገጽ እይታ ይመለሱ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ለተከፈለ ማያ ገፅ ሌላ መተግበሪያ ይጎትቱ"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ቦታውን ለመቀየር ከመተግበሪያው ውጭ ሁለቴ መታ ያድርጉ"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"አሳድግ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ወደ ግራ አሳድግ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ወደ ቀኝ አሳድግ"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"በነባሪ ቅንብሮች ክፈት"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ለዚህ የድር መተግበሪያ አገናኙን እንዴት እንደሚከፍቱ ይምረጡ"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"በመተግበሪያው ውስጥ"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"በአሳሽዎ ውስጥ"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"እሺ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 9dcb5ec..81706a2 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"انقر لفتح قائمة التطبيق"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"انقر لعرض عدة تطبيقات معًا"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"الرجوع إلى وضع ملء الشاشة من قائمة التطبيق"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"يمكن العثور على قائمة التطبيقات هنا"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"يمكنك الدخول إلى وضع العرض المخصّص للكمبيوتر المكتبي لفتح عدة تطبيقات معًا"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"يمكنك الرجوع إلى وضع ملء الشاشة في أي وقت من قائمة التطبيقات"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"استخدام تطبيقات متعدّدة في وقت واحد"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"انقر مرّتين خارج تطبيق لتغيير موضعه."</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"تكبير"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"المحاذاة إلى اليسار"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"المحاذاة إلى اليمين"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"إعدادات الفتح تلقائيًا"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"اختيار طريقة فتح روابط الويب لهذا التطبيق"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"في التطبيق"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"في المتصفِّح"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"حسنًا"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 484eef5..9fd4941 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"এপৰ মেনুখন খুলিবলৈ টিপক"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"একাধিক এপ্ একেলগে দেখুৱাবলৈ টিপক"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"এপৰ মেনুখনৰ পৰা পূৰ্ণ স্ক্ৰীনলৈ উভতি যাওক"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"এপৰ মেনু ইয়াত বিচাৰি পোৱা যাব"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"একেলগে একাধিক এপ্‌ খুলিবলৈ ডেস্কটপ ভিউলৈ যাওক"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"এপৰ মেনুৰ পৰা যিকোনো সময়তে পূৰ্ণ স্ক্ৰীনলৈ উভতি যাওক"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"চাওক আৰু অধিক কৰক"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"বিভাজিত স্ক্ৰীনৰ বাবে অন্য এটা এপ্‌ টানি আনি এৰক"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"এপ্‌টোৰ স্থান সলনি কৰিবলৈ ইয়াৰ বাহিৰত দুবাৰ টিপক"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাওঁফাললৈ স্নেপ কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"সোঁফাললৈ স্নেপ কৰক"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফ’ল্ট ছেটিং খোলক"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"এই এপ্‌টোৰ বাবে কিদৰে ৱেব লিংক খুলিব পাৰি সেয়া বাছনি কৰক"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"এপ্‌টোত"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপোনাৰ ব্ৰাউজাৰত"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index ded6da8..3ab6397 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tətbiq menyusunu açmaq üçün toxunun"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Bir neçə tətbiqi birlikdə göstərmək üçün toxunun"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Tətbiq menyusundan tam ekrana qayıdın"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tətbiq menyusunu burada tapa bilərsiniz"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Bir neçə tətbiqi birlikdə açmaq üçün masaüstü görünüşə daxil olun"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"İstənilən vaxt tətbiq menyusundan tam ekrana qayıdın"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ardını görün və edin"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran üçün başqa tətbiq sürüşdürün"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tətbiqin yerini dəyişmək üçün kənarına iki dəfə toxunun"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Böyüdün"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tərəf çəkin"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tərəf çəkin"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Defolt ayarlarla açın"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu tətbiq üçün veb-linklərin necə açılacağını seçin"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Tətbiqdə"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerinizdə"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 415547c..5076256 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Dodirnite da biste otvorili meni aplikacije"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Dodirnite da biste prikazali više aplikacija zajedno"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Vratite se iz menija aplikacije na prikaz preko celog ekrana"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije možete da pronađete ovde"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Uđite u prikaz za računare da biste istovremeno otvorili više aplikacija"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na ceo ekran bilo kada iz menija aplikacije"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vidite i uradite više"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste promenili njenu poziciju"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Uvećajte"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prikačite levo"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prikačite desno"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Podešavanje Podrazumevano otvaraj"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja veb-linkova za ovu aplikaciju"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledaču"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Potvrdi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index aded647..95530c4 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Адкрыць меню праграмы"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Паказаць некалькі праграм разам"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вярнуцца ў поўнаэкранны рэжым з меню праграмы"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Меню праграмы шукайце тут"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Каб адкрыць некалькі праграм адначасова, увайдзіце ў версію для камп’ютараў"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вы можаце вярнуцца ў поўнаэкранны рэжым у любы час з меню праграмы"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Адначасова выконвайце розныя задачы"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Двойчы націсніце экран па-за праграмай, каб перамясціць яе"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Разгарнуць"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Размясціць злева"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Размясціць справа"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налады параметра \"Адкрываць стандартна\""</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Выберыце, як гэта праграма будзе адкрываць вэб-спасылкі"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У праграме"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У браўзеры"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 8a69176..db498c1 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Докоснете, за да отворите менюто на приложението"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Докоснете, за да видите няколко приложения заедно"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Връщане към цял екран от менюто на приложението"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Можете да намерите менюто на приложението тук"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Активирайте изгледа за настолни компютри, за да отворите няколко приложения едновременно"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Преминете към цял екран по всяко време от менюто на приложението"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Преглеждайте и правете повече неща"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Преместете друго приложение с плъзгане, за да преминете в режим за разделен екран"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Докоснете два пъти извън дадено приложение, за да промените позицията му"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увеличаване"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прилепване наляво"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прилепване надясно"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отваряне на настройките по подразбиране"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Изберете как да се отварят уеб връзките за това приложение"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложението"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузъра ви"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 3799c9f..4c20253 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"অ্যাপ মেনু খুলতে ট্যাপ করুন"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"একাধিক অ্যাপ একসাথে দেখতে ট্যাপ করুন"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"অ্যাপ মেনু থেকে ফুল-স্ক্রিন মোডে ফিরে যান"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"অ্যাপ মেনু এখানে খুঁজে পাওয়া যাবে"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"একসাথে একাধিক অ্যাপ খোলার জন্য ডেস্কটপ ভিউতে যান"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"অ্যাপ মেনু থেকে ফুল-স্ক্রিন মোডে যেকোনও সময়ে ফিরে আসুন"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"দেখুন ও আরও অনেক কিছু করুন"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"স্প্লিট স্ক্রিনের ক্ষেত্রে অন্য কোনও অ্যাপ টেনে আনুন"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"কোনও অ্যাপের স্থান পরিবর্তন করতে তার বাইরে ডবল ট্যাপ করুন"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাঁদিকে স্ন্যাপ করুন"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ডানদিকে স্ন্যাপ করুন"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফল্ট হিসেবে থাকা সেটিংস খুলুন"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"এই অ্যাপের জন্য কীভাবে ওয়েব লিঙ্ক খুলবেন তা বেছে নিন"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"অ্যাপের মধ্যে"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপনার ব্রাউজারে"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index f0d172a..102a912 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Otvaranje menija aplikacije dodirom"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Istovremeni prikaz više aplikacija dodirom"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Povratak na prikaz preko cijelog ekrana putem menija aplikacije"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ovdje možete pronaći meni aplikacije"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Ulazak u prikaz na računaru radi istovremenog otvaranja više aplikacija"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Povratak na prikaz preko cijelog ekrana bilo kada putem menija aplikacije"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da promijenite njen položaj"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziranje"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pomicanje ulijevo"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pomicanje udesno"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvaranje prema zadanim postavkama"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja web linkova za ovu aplikaciju"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Uredu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index bf35c90..3e3fcd0 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toca per obrir el menú de l\'aplicació"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toca per mostrar diverses aplicacions alhora"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Torna a la pantalla completa des del menú de l\'aplicació"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Pots trobar el menú de l\'aplicació aquí"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accedeix a la visualització per a ordinadors per obrir diverses aplicacions alhora"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna a la pantalla completa en qualsevol moment des del menú de l\'aplicació"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta i fes més coses"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Fes doble toc fora d\'una aplicació per canviar-ne la posició"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximitza"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajusta a l\'esquerra"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajusta a la dreta"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configuració d\'obertura predeterminada"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Tria com vols obrir els enllaços web per a aquesta aplicació"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"A l\'aplicació"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Al navegador"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"D\'acord"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 7fc1033..0627f54 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Klepnutím otevřete nabídku aplikace"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Klepnutím zobrazíte několik aplikací najednou"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Návrat na celou obrazovku z nabídky aplikace"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Najdete tu nabídku aplikace"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Pokud chcete otevřít několik aplikací současně, přejděte na zobrazení na počítači"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Na celou obrazovku se můžete kdykoli vrátit z nabídky aplikace"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lepší zobrazení a více možností"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikaci změníte její umístění"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovat"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Přichytit vlevo"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Přichytit vpravo"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otevírat podle výchozího nastavení"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Určete, jak se v této aplikaci mají otevírat webové odkazy"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaci"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prohlížeči"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 717e6c4..ed9e5f0 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tryk for at åbne appmenuen"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tryk for at se flere apps på én gang"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Gå tilbage til fuld skærm via appmenuen"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenuen kan findes her"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Gå til computervenlig visning for at åbne flere apps på én gang"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Gå tilbage til fuld skærm når som helst via appmenuen"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gør mere"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Træk en anden app hertil for at bruge opdelt skærm"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryk to gange uden for en app for at justere dens placering"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimér"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fastgør til venstre"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fastgør til højre"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Indstillinger for automatisk åbning"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Vælg, hvordan denne app skal åben weblinks"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I din browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index bccd4ae..ec1cb03 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Zum Öffnen des App-Menüs tippen"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tippen, um mehrere Apps gleichzeitig anzuzeigen"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Über das App-Menü zum Vollbildmodus zurückkehren"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Das App-Menü findest du hier"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Über die Desktop-Ansicht kannst du mehrere Apps gleichzeitig öffnen"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Über das App-Menü kannst du jederzeit zum Vollbildmodus zurückkehren"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Für Splitscreen-Modus weitere App hineinziehen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Außerhalb einer App doppeltippen, um die Position zu ändern"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links andocken"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts andocken"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Einstellungen für die Option „Standardmäßig öffnen“"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Festlegen, wie Weblinks für diese App geöffnet werden"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In der App"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In deinem Browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 1039273..7a690ce 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Πατήστε για άνοιγμα του μενού της εφαρμογής"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Πατήστε για εμφάνιση πολλών εφαρμογών μαζί"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Επιστρέψτε στην πλήρη οθόνη από το μενού της εφαρμογής"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Μπορείτε να βρείτε το μενού εφαρμογών εδώ"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Μεταβείτε στην προβολή για υπολογιστές, για να ανοίξετε πολλές εφαρμογές μαζί"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Επιστρέψτε στην πλήρη οθόνη ανά πάσα στιγμή από το μενού της εφαρμογής"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Δείτε και κάντε περισσότερα"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Σύρετε σε μια άλλη εφαρμογή για διαχωρισμό οθόνης."</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Πατήστε δύο φορές έξω από μια εφαρμογή για να αλλάξετε τη θέση της"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Μεγιστοποίηση"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Κούμπωμα αριστερά"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Κούμπωμα δεξιά"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Άνοιγμα ρυθμίσεων από προεπιλογή"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Επιλογή τρόπου ανοίγματος συνδέσμων ιστού για την εφαρμογή"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Στην εφαρμογή"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Στο πρόγραμμα περιήγησής σας"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ΟΚ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 98da627..afb3f8e 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index e928fe0..aa39251 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen anytime from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximize"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 98da627..afb3f8e 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 98da627..afb3f8e 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tap to open the app menu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tap to show multiple apps together"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Return to fullscreen from the app menu"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Double-tap outside an app to reposition it"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index e48a9db..54346da 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‎‏‎Maximize‎‏‎‎‏‎"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎Snap left‎‏‎‎‏‎"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎Snap right‎‏‎‎‏‎"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎Open by default settings‎‏‎‎‏‎"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎Choose how to open web links for this app‎‏‎‎‏‎"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎In the app‎‏‎‎‏‎"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎In your browser‎‏‎‎‏‎"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎OK‎‏‎‎‏‎"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index f349cbb..5244a61 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Presiona para abrir el menú de la app"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Presiona para mostrar varias apps juntas"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Regresa a pantalla completa desde el menú de la app"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la app se encuentra aquí"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra a la vista de escritorio para abrir varias apps a la vez"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresa a pantalla completa en cualquier momento desde el menú de la app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Aprovecha más"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra app para el modo de pantalla dividida"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Presiona dos veces fuera de una app para cambiar su ubicación"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar a la izquierda"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar a la derecha"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con la configuración predeterminada"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Elige cómo abrir vínculos web para esta app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En un navegador"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 9f6b2e65..a673351 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toca para abrir el menú de aplicaciones"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toca para mostrar varias aplicaciones a la vez"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Vuelve a pantalla completa desde el menú de aplicaciones"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la aplicación se encuentra aquí"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra en la vista para ordenador si quieres abrir varias aplicaciones a la vez"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vuelve a la pantalla completa en cualquier momento desde el menú de aplicaciones"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta más información y haz más"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra aplicación para activar la pantalla dividida"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dos veces fuera de una aplicación para cambiarla de posición"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Acoplar a la izquierda"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Acoplar a la derecha"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con los ajustes predeterminados"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Elige cómo quieres abrir los enlaces web de esta aplicación"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la aplicación"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En el navegador"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index b2b06d6..686385c 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Puudutage rakenduse menüü avamiseks"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Puudutage mitme rakenduse koos kuvamiseks"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Minge rakenduse menüüst tagasi täisekraanile"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Rakenduse menüü leiate siit"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Mitme rakenduse koos avamiseks avage arvutivaade"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Saate rakenduse menüüst igal ajal täisekraanile naasta"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vaadake ja tehke rohkem"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Topeltpuudutage rakendusest väljaspool, et selle asendit muuta"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeeri"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Tõmmake vasakule"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Tõmmake paremale"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avamisviisi vaikeseaded"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Valige, kuidas avada selle rakenduse puhul veebilinke"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Rakenduses"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauseris"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 4a71c49..1f0e54c 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Sakatu hau aplikazioen menua irekitzeko"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Sakatu hau aplikazio bat baino gehiago aldi berean erakusteko"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Itzuli pantaila osora aplikazioen menutik"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aplikazioaren menua dago hemen"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Sartu ordenagailuetarako ikuspegian aplikazio bat baino gehiago batera irekitzeko"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Pantaila osoko modura itzultzeko, erabili aplikazioaren menua"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ikusi eta egin gauza gehiago"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Pantaila zatitua ikusteko, arrastatu beste aplikazio bat"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren kanpoaldea"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizatu"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ezarri ezkerrean"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ezarri eskuinean"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Modu lehenetsian irekitzearen ezarpenak"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Aukeratu nola ireki sareko estekak aplikazio honetan"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Aplikazioan"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Arakatzailean"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ados"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 941ff84..ad39b28 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه تک‌ضرب بزنید"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن تک‌ضرب بزنید"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن تک‌ضرب بزنید."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"برای باز کردن منو برنامه، تک‌ضرب بزنید"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"برای نمایش چند برنامه با هم، تک‌ضرب بزنید"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"از منو برنامه به تمام‌صفحه برگردید"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"منو برنامه را می‌توانید اینجا ببینید"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"برای باز کردن هم‌زمان چند برنامه، وارد نمای ویژه رایانه شوید"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"هروقت خواستید از منو برنامه به حالت تمام‌صفحه برگردید"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه به‌طور هم‌زمان استفاده کنید"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامه‌ای دیگر بکشید"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"برای جابه‌جا کردن برنامه، بیرون از آن دو تک‌ضرب بزنید"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بزرگ کردن"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"کشیدن به‌چپ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"کشیدن به‌راست"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"تنظیمات باز کردن به‌طور پیش‌فرض"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"انتخاب روش باز کردن پیوندهای وب مربوط به این برنامه"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"در برنامه"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"در مرورگر"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"تأیید"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index a45e9af..2f0edd3 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Avaa sovellusvalikko napauttamalla"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Näytä useita sovelluksia yhdessä napauttamalla"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Palaa koko näytön tilaan sovellusvalikosta"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Sovellusvalikko löytyy täältä"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Siirry työpöytänäkymään, niin voit avata useita sovelluksia kerralla"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Voit palata koko näytön tilaan milloin tahansa sovellusvalikosta"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Näe ja tee enemmän"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kaksoisnapauta sovelluksen ulkopuolella, jos haluat siirtää sitä"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Suurenna"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Siirrä vasemmalle"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Siirrä oikealle"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avaa oletusasetusten mukaan"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Valitse, miten verkkolinkit avataan tässä sovelluksessa"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sovelluksessa"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Selaimella"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index c163165..2453961 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toucher ici pour ouvrir le menu de l\'appli"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toucher ici pour afficher plusieurs applis ensemble"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Revenir au mode Plein écran à partir du menu de l\'appli"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'appli se trouve ici"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accéder à l\'affichage sur un ordinateur de bureau pour ouvrir plusieurs applis simultanément"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir au mode Plein écran à tout moment à partir du menu de l\'appli"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Touchez deux fois à côté d\'une appli pour la repositionner"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Épingler à gauche"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Épingler à droite"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choisissez comment ouvrir les liens Web pour cette appli"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'appli"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index b2a6f16..aee2345 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Appuyer pour ouvrir le menu de l\'appli"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Appuyer pour afficher plusieurs applis simultanément"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Revenir en plein écran depuis le menu de l\'appli"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'application se trouve ici"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Passer à l\'affichage sur ordinateur pour ouvrir plusieurs applications simultanément"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir en plein écran à tout moment depuis le menu de l\'application"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et interagir plus"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Appuyez deux fois en dehors d\'une appli pour la repositionner"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ancrer à gauche"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ancrer à droite"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choisir comment ouvrir les liens Web pour cette appli"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'application"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index ab972f9..b61588a 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toca para abrir o menú da aplicación"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toca para mostrar varias aplicacións xuntas"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Volve á pantalla completa desde o menú da aplicación"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aquí podes ver o menú da aplicación"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Vai á vista para ordenadores se queres abrir varias aplicacións á vez"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volve á pantalla completa en calquera momento desde o menú da aplicación"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ver e facer máis"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra outra aplicación para usar a pantalla dividida"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dúas veces fóra da aplicación para cambiala de posición"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Axustar á esquerda"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Axustar á dereita"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir coa configuración predeterminada"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escoller como abrir as ligazóns web para esta aplicación"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na aplicación"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 0dc2f73..fd4f2ba 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ઍપ મેનૂ ખોલવા માટે ટૅપ કરો"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"એકથી વધુ ઍપ એકસાથે બતાવવા માટે ટૅપ કરો"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ઍપ મેનૂમાંથી પૂર્ણસ્ક્રીન પર પાછા ફરો"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ઍપ મેનૂ અહીં જોવા મળી શકે છે"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"એકથી વધુ ઍપ એકસાથે ખોલવા માટે ડેસ્કટૉપ વ્યૂ દાખલ કરો"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ઍપ મેનૂમાંથી કોઈપણ સમયે પૂર્ણ સ્ક્રીન પર પાછા ફરો"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"જુઓ અને બીજું ઘણું કરો"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"વિભાજિત સ્ક્રીન માટે કોઈ અન્ય ઍપમાં ખેંચો"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"કોઈ ઍપની જગ્યા બદલવા માટે, તેની બહાર બે વાર ટૅપ કરો"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ડાબે સ્નૅપ કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"જમણે સ્નૅપ કરો"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"\'ડિફૉલ્ટ તરીકે ખોલો\' સેટિંગ"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"આ ઍપ માટે વેબ લિંક ખોલવાની રીત પસંદ કરો"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ઍપમાં"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"તમારા બ્રાઉઝરમાં"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ઓકે"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 679d800..d2cd23d 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ऐप्लिकेशन मेन्यू खोलने के लिए टैप करें"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"कई ऐप्लिकेशन एक साथ दिखाने के लिए टैप करें"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ऐप्लिकेशन मेन्यू से फ़ुलस्क्रीन मोड पर वापस जाएं"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ऐप्लिकेशन मेन्यू यहां पाया जा सकता है"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एक साथ कई ऐप्लिकेशन खोलने के लिए, डेस्कटॉप व्यू में जाएं"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ऐप्लिकेशन मेन्यू से फ़ुल स्क्रीन मोड पर किसी भी समय वापस जाएं"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पूरी जानकारी लेकर, बेहतर तरीके से काम करें"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन का इस्तेमाल करने के लिए, किसी अन्य ऐप्लिकेशन को खींचें और छोड़ें"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"किसी ऐप्लिकेशन की जगह बदलने के लिए, उसके बाहर दो बार टैप करें"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"बड़ा करें"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बाईं ओर स्नैप करें"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दाईं ओर स्नैप करें"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफ़ॉल्ट सेटिंग के हिसाब से खोलें"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"इस ऐप्लिकेशन के लिए वेब लिंक खोलने का तरीका चुनें"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ऐप्लिकेशन में"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"आपके ब्राउज़र में"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठीक है"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 1e5ffc8..80949b4 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Dodirnite za otvaranje izbornika aplikacije"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Dodirnite za prikaz više aplikacija zajedno"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Vratite se na cijeli zaslon iz izbornika aplikacije"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Izbornik aplikacije možete pronaći ovdje"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Otvorite prikaz na računalu da biste otvorili više aplikacija zajedno"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na cijeli zaslon bilo kad iz izbornika aplikacije"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Gledajte i učinite više"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Povucite drugu aplikaciju unutra da biste podijelili zaslon"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste je premjestili"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Poravnaj lijevo"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Poravnaj desno"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvori prema zadanim postavkama"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja web-veza za ovu aplikaciju"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"U redu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 7c90a19..cebf585 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Koppintson az alkalmazásmenü megnyitásához"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Koppintson több alkalmazás együttes megjelenítéséhez"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"A teljes képernyőre az alkalmazásmenüben térhet vissza"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Az alkalmazásmenü itt található"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Asztali nézetbe lépve több alkalmazást nyithat meg egyidejűleg"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Az alkalmazásmenüből bármikor visszatérhet a teljes képernyőre"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Koppintson duplán az alkalmazáson kívül az áthelyezéséhez"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Teljes méret"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Balra igazítás"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Jobbra igazítás"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Alapértelmezett beállítások megnyitása"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Az app webes linkjeinek megnyitásához használt módszer"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Az alkalmazásban"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"A böngészőben"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index e8ed4ac..63a9d6d 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Հպեք՝ հավելվածի ընտրացանկը բացելու համար"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Հպեք՝ էկրանին մի քանի հավելված միասին դիտելու համար"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Հավելվածի ընտրացանկից վերադառնալ լիաէկրան ռեժիմ"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Հավելվածի ընտրացանկն այստեղ է"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Անցեք համակարգչային տարբերակին՝ միաժամանակ մի քանի հավելված բացելու համար"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ցանկացած ժամանակ հավելվածի ընտրացանկից վերադարձեք լիաէկրան ռեժիմ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Միաժամանակ կատարեք մի քանի առաջադրանք"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Կրկնակի հպեք հավելվածի կողքին՝ այն տեղափոխելու համար"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ծավալել"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ամրացնել ձախ կողմում"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ամրացնել աջ կողմում"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Բացել կարգավորումներն ըստ կանխադրման"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Ընտրեք՝ ինչպես բացել այս հավելվածի վեբ հղումները"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Հավելվածում"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Ձեր դիտարկիչում"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Եղավ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 06b1634..a06d01c 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ketuk untuk membuka menu aplikasi"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Ketuk untuk menampilkan beberapa aplikasi secara bersamaan"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Kembali ke layar penuh dari menu aplikasi"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu aplikasi dapat ditemukan di sini"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Buka tampilan desktop untuk membuka beberapa aplikasi secara bersamaan"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali ke layar penuh kapan saja dari menu aplikasi"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih banyak hal"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketuk dua kali di luar aplikasi untuk mengubah posisinya"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimalkan"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Maksimalkan ke kiri"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Maksimalkan ke kanan"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka dengan setelan default"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pilih cara membuka link web untuk aplikasi ini"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Di aplikasi"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Di browser Anda"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Oke"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 2d44777..a20f460 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ýttu til að opna forritavalmyndina"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Ýttu til að sjá mörg forrit saman"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Opnaðu allan skjáinn aftur á forritavalmyndinni"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Hér finnurðu forritavalmyndina"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Veldu tölvuútgáfu til að opna mörg forrit samtímis"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Þú getur skipt aftur í allan skjáinn hvenær sem er af forritavalmyndinni"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sjáðu og gerðu meira"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dragðu annað forrit inn til að nota skjáskiptingu"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ýttu tvisvar utan við forrit til að færa það"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Stækka"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Smella til vinstri"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Smella til hægri"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Stillingar sjálfvirkrar opnunar"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Veldu hvernig veftenglar opnast í forritinu"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Í forritinu"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Í vafranum"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Í lagi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index b564245..39fd6ba 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tocca per aprire il menu dell\'app"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tocca per mostrare più app insieme"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Torna allo schermo intero dal menu dell\'app"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Il menu dell\'app si trova qui"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra nella visualizzazione desktop per aprire più app contemporaneamente"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna allo schermo intero in qualsiasi momento dal menu dell\'app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trascina in un\'altra app per usare lo schermo diviso"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tocca due volte fuori da un\'app per riposizionarla"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ingrandisci"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Aggancia a sinistra"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Aggancia a destra"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Apri in base alle impostazioni predefinite"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Scegli come aprire i link web per questa app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"All\'interno dell\'app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Nel browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index fa07263..0586d1f 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"צריך להקיש כדי לפתוח את תפריט האפליקציה"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"אפשר להקיש כדי להציג כמה אפליקציות יחד"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"חזרה למסך מלא מתפריט האפליקציה"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"תפריט האפליקציה נמצא כאן"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"כדי לפתוח כמה אפליקציות יחד, צריך לעבור למצב תצוגה למחשב"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"אפשר לחזור למסך מלא בכל שלב מתפריט האפליקציה"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"רוצה לראות ולעשות יותר?"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"צריך לגרור אפליקציה אחרת כדי להשתמש במסך המפוצל"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"צריך להקיש הקשה כפולה מחוץ לאפליקציה כדי למקם אותה מחדש"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"הגדלה"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"הצמדה לשמאל"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"הצמדה לימין"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"הגדרות לפתיחה כברירת מחדל"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"בחירת האופן שבו קישורים לדפי אינטרנט אחרים ייפתחו באפליקציה הזו"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"באפליקציה"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"בדפדפן"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"אישור"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 91667c0..8aa8269 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"タップするとアプリメニューが開きます"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"タップすると複数のアプリが同時に表示されます"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"アプリメニューから全画面表示に戻ります"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"アプリメニューはここにあります"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"デスクトップ ビューに切り替えて複数のアプリを同時に開けます"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"アプリメニューからいつでも全画面表示に戻れます"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"表示を拡大して機能を強化"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"分割画面にするにはもう 1 つのアプリをドラッグしてください"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"位置を変えるにはアプリの外側をダブルタップしてください"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"左にスナップ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"右にスナップ"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"デフォルトの設定で開く"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"このアプリのウェブリンクを開く方法を選択"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"アプリ内"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ブラウザ内"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 2208348..6672599 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"შეეხეთ აპის მენიუს გასახსნელად"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"შეეხეთ რამდენიმე აპის ერთად საჩვენებლად"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"სრულეკრანიან რეჟიმზე დაბრუნდით აპის მენიუდან"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"აპის მენიუ შეგიძლიათ იხილოთ აქ"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"რამდენიმე აპის ერთდროულად გასახსნელად შედით დესკტოპის ხედში"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"სრულ ეკრანზე ნებისმიერ დროს შეგიძლიათ დაბრუნდეთ აპის მენიუდან"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"მეტის ნახვა და გაკეთება"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ორმაგად შეეხეთ აპის გარშემო სივრცეს, რათა ის სხვაგან გადაიტანოთ"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"მაქსიმალურად გაშლა"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"მარცხნივ გადატანა"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"მარჯვნივ გადატანა"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"პარამეტრების ნაგულისხმევად გახსნა"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ამ აპისთვის ვებ ბმულების გახსნის წესის არჩევა"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"აპში"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"თქვენს ბრაუზერში"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"კარგი"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 416a84c..56ae441 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Қолданба мәзірін ашу үшін түртіңіз"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Бірнеше қолданбаны қатар көрсету үшін түртіңіз"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Қолданба мәзірінен толық экран режиміне қайту"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Қолданба мәзірін осы жерден табуға болады."</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Бірнеше қолданбаны бірге ашу үшін компьютерлік нұсқаны қосыңыз."</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Қолданба мәзірінен кез келген уақытта толық экранға оралыңыз."</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлу үшін басқа қолданбаға өтіңіз."</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Қолданбаның орнын өзгерту үшін одан тыс жерді екі рет түртіңіз."</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Жаю"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солға тіркеу"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңға тіркеу"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Әдепкісінше ашу параметрлері"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Осы қолданбадағы веб-сілтемелерді ашу жолын таңдаңыз"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Қолданбада"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Браузерде"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайды"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index b074d65..460b867 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហា​ពាក់ព័ន្ធនឹង​កាមេរ៉ាឬ?\nចុចដើម្បី​ដោះស្រាយ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ចុច​ដើម្បីបើក​ម៉ឺនុយ​កម្មវិធី"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ចុច​ដើម្បីបង្ហាញ​កម្មវិធី​ច្រើនរួមគ្នា"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ត្រឡប់ទៅ​អេក្រង់​ពេញវិញ​ពីម៉ឺនុយ​កម្មវិធី"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"អាចរកឃើញម៉ឺនុយកម្មវិធីនៅទីនេះ"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ចូលទិដ្ឋភាព​លើកុំព្យូទ័រ ដើម្បីបើកកម្មវិធីច្រើនជាមួយគ្នា"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ត្រឡប់ទៅអេក្រង់ពេញវិញនៅពេលណាក៏បានពីម៉ឺនុយកម្មវិធី"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"មើលឃើញ និងធ្វើបានកាន់តែច្រើន"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"អូស​កម្មវិធី​មួយ​ទៀត​ចូល ដើម្បី​ប្រើ​មុខងារ​បំបែកអេក្រង់"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ចុចពីរដង​នៅ​ក្រៅ​កម្មវិធី ដើម្បី​ប្ដូរ​ទីតាំង​កម្មវិធី​នោះ"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ពង្រីក"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ផ្លាស់ទីទៅឆ្វេង"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ផ្លាស់ទីទៅស្ដាំ"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ការកំណត់បើកតាមលំនាំដើម"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ជ្រើសរើសរបៀបបើកតំណបណ្ដាញសម្រាប់កម្មវិធីនេះ"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"នៅក្នុងកម្មវិធី"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"នៅក្នុង​កម្មវិធីរុករកតាម​អ៊ីនធឺណិត​របស់អ្នក"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"យល់ព្រម"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 9c22241..2e2be46c 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ಆ್ಯಪ್‌ ಮೆನುವನ್ನು ತೆರೆಯಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ಅನೇಕ ಆ್ಯಪ್‌ಗಳನ್ನು ಒಟ್ಟಿಗೆ ತೋರಿಸಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ಆ್ಯಪ್‌ ಮೆನುವಿನಿಂದ ಫುಲ್‌ಸ್ಕ್ರೀನ್‌ಗೆ ಹಿಂತಿರುಗಿ"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ಆ್ಯಪ್ ಮೆನುವನ್ನು ಇಲ್ಲಿ ಕಾಣಬಹುದು"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ಹಲವು ಆ್ಯಪ್‌ಗಳನ್ನು ಒಟ್ಟಿಗೆ ತೆರೆಯಲು ಡೆಸ್ಕ್‌ಟಾಪ್ ವೀಕ್ಷಣೆಯನ್ನು ನಮೂದಿಸಿ"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ಆ್ಯಪ್ ಮೆನುವಿನಿಂದ ಯಾವಾಗ ಬೇಕಾದರೂ ಫುಲ್‌ಸ್ಕ್ರೀನ್‌ಗೆ ಹಿಂತಿರುಗಿ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ನೋಡಿ ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ನಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ಆ್ಯಪ್ ಒಂದರ ಸ್ಥಾನವನ್ನು ಬದಲಾಯಿಸಲು ಅದರ ಹೊರಗೆ ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ಎಡಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ಬಲಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ಡೀಫಾಲ್ಟ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಂದ ತೆರೆಯಿರಿ"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ಈ ಆ್ಯಪ್‌ಗೆ ವೆಬ್ ಲಿಂಕ್‌ಗಳನ್ನು ಹೇಗೆ ತೆರೆಯಬೇಕು ಎಂಬುದನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ಆ್ಯಪ್‌ನಲ್ಲಿ"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ನಿಮ್ಮ ಬ್ರೌಸರ್‌ನಲ್ಲಿ"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ಸರಿ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 58dd6f8..4bcc76d 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"탭하여 앱 메뉴 열기"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"탭하여 여러 앱을 함께 표시하기"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"앱 메뉴에서 전체 화면으로 돌아가기"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"앱 메뉴는 여기에서 찾을 수 있습니다."</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"데스크톱 뷰를 실행하여 여러 앱을 함께 열 수 있습니다."</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"언제든지 앱 메뉴에서 전체 화면으로 돌아갈 수 있습니다."</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"더 많은 정보를 보고 더 많은 작업을 처리하세요"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"화면 분할을 사용하려면 다른 앱을 드래그해 가져옵니다."</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"앱 위치를 조정하려면 앱 외부를 두 번 탭합니다."</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"최대화하기"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"왼쪽으로 맞추기"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"오른쪽으로 맞추기"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"기본값으로 열기 설정"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"이 앱에서 웹 링크를 여는 방법을 선택하세요"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"앱에서"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"브라우저에서"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"확인"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 75feede..6ae51a4 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Колдонмонун менюсун ачуу үчүн таптап коюңуз"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Бир нече колдонмону чогуу көрүү үчүн таптап коюңуз"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Колдонмонун менюсунан толук экранга кайтыңыз"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Колдонмонун менюсун ушул жерден таба аласыз"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Бир убакта бир нече колдонмону ачуу үчүн компьютердик версияга өтүңүз"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Каалаган убакта колдонмонун менюсунан толук экранга кайта аласыз"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Көрүп, көбүрөөк нерселерди жасаңыз"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлүү үчүн башка колдонмону сүйрөңүз"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Колдонмону жылдыруу үчүн сырт жагын эки жолу таптаңыз"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Чоңойтуу"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солго жылдыруу"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңго жылдыруу"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Демейки шартта ачуу параметрлери"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Колдонмодо шилтемелер кантип ачылышы керек экенин тандаңыз"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Колдонмодо"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Серепчиңизде"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайт"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 8f28504..f8a09da 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ແຕະເພື່ອເປີດເມນູແອັບ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ແຕະເພື່ອສະແດງແອັບຫຼາຍລາຍການພ້ອມກັນ"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ກັບຄືນໄປຫາໂໝດເຕັມຈໍຈາກເມນູແອັບ"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ສາມາດເບິ່ງເມນູແອັບໄດ້ບ່ອນນີ້"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ເຂົ້າສູ່ມຸມມອງເດັສທັອບເພື່ອເປີດຫຼາຍແອັບພ້ອມກັນ"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ກັບຄືນໄປຫາໂໝດເຕັມຈໍໄດ້ທຸກເວລາຈາກເມນູແອັບ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ເບິ່ງ ແລະ ເຮັດຫຼາຍຂຶ້ນ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ລາກໄປໄວ້ໃນແອັບອື່ນເພື່ອແບ່ງໜ້າຈໍ"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ແຕະສອງເທື່ອໃສ່ນອກແອັບໃດໜຶ່ງເພື່ອຈັດຕຳແໜ່ງຂອງມັນຄືນໃໝ່"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ແນບຊ້າຍ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ແນບຂວາ"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ເປີດຕາມການຕັ້ງຄ່າເລີ່ມຕົ້ນ"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ເລືອກວິທີເປີດລິ້ງເວັບສຳລັບແອັບນີ້"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ໃນແອັບ"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ໃນໂປຣແກຣມທ່ອງເວັບຂອງທ່ານ"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ຕົກລົງ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index b97b878..857e90e 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Palieskite, kad atidarytumėte programos meniu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Palieskite, kad būtų rodomos kelios programos kartu"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Grįžkite į viso ekrano režimą iš programos meniu"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Programos meniu rasite čia"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Įjunkite rodinio versiją staliniams kompiuteriams, kad vienu metu atidarytumėte kelias programas"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bet kada iš programos meniu grįžkite į viso ekrano režimą"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daugiau turinio ir funkcijų"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dukart palieskite už programos ribų, kad pakeistumėte jos poziciją"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Padidinti"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pritraukti kairėje"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pritraukti dešinėje"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atidaryti pagal numatytuosius nustatymus"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pasirinkite, kaip atidaryti šios programos žiniatinklio nuorodas"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Programoje"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Naršyklėje"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Gerai"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index de200d8..e56363e 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Lai atvērtu lietotnes izvēlni, pieskarieties."</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Lai parādītu vairākas lietotnes kopā, pieskarieties."</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Varat atgriezties pilnekrāna režīmā no lietotnes izvēlnes."</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Šeit ir pieejama lietotņu izvēlne"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Lai atvērtu vairākas lietotnes vienlaikus, pārejiet uz skatu datorā"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"No lietotnes izvēlnes varat jebkurā brīdī atgriezties pilnekrāna režīmā."</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Uzziniet un paveiciet vairāk"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lai izmantotu sadalītu ekrānu, ievelciet vēl vienu lietotni"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Lai pārvietotu lietotni, veiciet dubultskārienu ārpus lietotnes"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizēt"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Piestiprināt pa kreisi"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Piestiprināt pa labi"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atvērt pēc noklusējuma iestatījumiem"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Izvēlieties, kā atvērt šajā lietotnē norādītās saites"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Lietotnē"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pārlūkprogrammā"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Labi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 4922d04..1bf7a28 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Допрете за да го отворите менито со апликации"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Допрете за да се прикажат повеќе апликации заедно"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вратете се на цел екран од менито со апликации"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Менито со апликации може да го најдете овде"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Влезете во приказот на компјутер за да отворите повеќе апликации заедно"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратете се на цел екран од менито со апликации кога сакате"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Погледнете и направете повеќе"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Повлечете друга апликација за поделен екран"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Допрете двапати надвор од некоја апликација за да ја преместите"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Максимизирај"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Фотографирај лево"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Фотографирај десно"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отвори според стандардните поставки"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Изберете како да се отвораат линковите за апликацијава"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Во апликацијата"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Во прелистувачот"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Во ред"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 61277d6..3401f6d 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ആപ്പ് മെനു തുറക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ഒന്നിലധികം ആപ്പുകൾ ഒരുമിച്ച് കാണിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ആപ്പ് മെനുവിൽ നിന്ന് പൂർണ്ണസ്‌ക്രീനിലേക്ക് മടങ്ങുക"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ആപ്പ് മെനു ഇവിടെ കണ്ടെത്താനാകും"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ഒന്നിലധികം ആപ്പുകൾ ഒരുമിച്ച് തുറക്കാൻ ഡെസ്‌ക്‌‌ടോപ്പ് വ്യൂവിൽ പ്രവേശിക്കുക"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ആപ്പ് മെനുവിൽ നിന്ന് ഏതുസമയത്തും പൂർണ്ണ സ്‌ക്രീനിലേക്ക് മടങ്ങുക"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"കൂടുതൽ കാണുക, ചെയ്യുക"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"സ്‌ക്രീൻ വിഭജന മോഡിന്, മറ്റൊരു ആപ്പ് വലിച്ചിടുക"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ആപ്പിന്റെ സ്ഥാനം മാറ്റാൻ അതിന് പുറത്ത് ഡബിൾ ടാപ്പ് ചെയ്യുക"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"വലുതാക്കുക"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ഇടതുവശത്തേക്ക് സ്‌നാപ്പ് ചെയ്യുക"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"വലതുവശത്തേക്ക് സ്‌നാപ്പ് ചെയ്യുക"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ഡിഫോൾട്ട് ക്രമീകരണം ഉപയോഗിച്ച് തുറക്കുക"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ഈ ആപ്പിനായി വെബ് ലിങ്കുകൾ എങ്ങനെ തുറക്കണമെന്ന് തിരഞ്ഞെടുക്കൂ"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ആപ്പിൽ"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"നിങ്ങളുടെ ബ്രൗസറിൽ"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ശരി"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 2b313a2..87708d0 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Аппын цэсийг нээхийн тулд товшино уу"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Олон аппыг хамтад нь харуулахын товшино уу"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Аппын цэсээс бүтэн дэлгэц рүү буцна уу"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Аппын цэсийг эндээс олох боломжтой"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Олон аппыг хамтад нь нээхийн тулд дэлгэц дээр харагдах байдалд орно уу"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Аппын цэсээс бүтэн дэлгэц рүү хүссэн үедээ буцна уу"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Дэлгэц хуваах горимд ашиглахын тулд өөр аппыг чирнэ үү"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Аппыг дахин байрлуулахын тулд гадна талд нь хоёр товшино"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Зүүн тийш зэрэгцүүлэх"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Баруун тийш зэрэгцүүлэх"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Өгөгдмөл тохиргоогоор нээх"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Энэ аппад веб холбоосыг хэрхэн нээхийг сонгоно уу"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Аппад"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Хөтчидөө"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 9778dcd..1ea41e5 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"अ‍ॅप मेनू उघडण्यासाठी टॅप करा"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"एकाहून अधिक ॲप्स एकत्र दाखवण्यासाठी टॅप करा"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ॲप मेनूमधून फुलस्क्रीनवर परत या"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ॲप मेनू इथे आढळू शकतो"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एकाहून अधिक ॲप्स एकत्र उघडण्यासाठी डेस्कटॉप दृश्यात एंटर करा"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ॲप मेनूमधून कधीही फुल स्क्रीनवर परत या"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पहा आणि आणखी बरेच काही करा"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप ड्रॅग करा"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ॲपची स्थिती पुन्हा बदलण्यासाठी, त्याच्या बाहेर दोनदा टॅप करा"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"मोठे करा"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"डावीकडे स्नॅप करा"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"उजवीकडे स्नॅप करा"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"बाय डीफॉल्ट सेटिंग्ज उघडा"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"या अ‍ॅपसाठीच्या वेब लिंक कशा उघडाव्यात हे निवडा"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ॲपमध्ये"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तुमच्या ब्राउझरमध्ये"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ओके"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 0ee4396..ca248e1 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ketik untuk membuka menu apl"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Ketik untuk memaparkan berbilang apl serentak"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Kembali kepada skrin penuh daripada menu apl"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu apl boleh ditemukan di sini"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Masuki paparan desktop untuk membuka berbilang apl serentak"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali kepada skrin penuh pada bila-bila masa daripada menu apl"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Seret masuk apl lain untuk menggunakan skrin pisah"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketik dua kali di luar apl untuk menempatkan semula apl itu"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimumkan"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Autojajar ke kiri"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Autojajar ke kanan"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka tetapan secara lalai"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pilih cara membuka pautan web untuk apl ini"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Pada apl"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pada penyemak imbas"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 57d0950..3c4325bf 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"အက်ပ်မီနူးကိုဖွင့်ရန် တို့ပါ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"အက်ပ်များစွာကို အတူတကွပြရန် တို့ပါ"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"အက်ပ်မီနူးမှ ဖန်သားပြင်အပြည့်သို့ ပြန်သွားပါ"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"အက်ပ်မီနူးကို ဤနေရာတွင် တွေ့နိုင်သည်"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"အက်ပ်များစွာကို အတူတကွဖွင့်ရန်အတွက် ဒက်စ်တော့မြင်ကွင်းသို့ ဝင်ရောက်နိုင်သည်"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"အက်ပ်မီနူးမှ ဖန်သားပြင်အပြည့်သို့ အချိန်မရွေး ပြန်သွားနိုင်သည်"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ကြည့်ပြီး ပိုမိုလုပ်ဆောင်ပါ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းအတွက် အက်ပ်နောက်တစ်ခုကို ဖိဆွဲပါ"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"နေရာပြန်ချရန် အက်ပ်အပြင်ဘက်ကို နှစ်ချက်တို့ပါ"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ချဲ့ရန်"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ဘယ်တွင် ချဲ့ရန်"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ညာတွင် ချဲ့ရန်"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"မူရင်းဆက်တင်ဖြင့် ဖွင့်ရန်"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ဤအက်ပ်အတွက် ဝဘ်လင့်ခ်များ မည်သို့ဖွင့်မည်ကို ရွေးပါ"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"အက်ပ်တွင်"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"သင်၏ဘရောင်ဇာတွင်"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index eb505ec..4096bbf 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Trykk for å åpne appmenyen"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Trykk for å vise flere apper sammen"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Gå tilbake til fullskjerm fra appmenyen"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Her finner du appmenyen"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Gå til skrivebordsvisningen for å åpne flere apper samtidig"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Du kan gå tilbake til fullskjermmodusen når som helst fra appmenyen"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gjør mer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra inn en annen app for å bruke delt skjerm"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dobbelttrykk utenfor en app for å flytte den"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimer"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fest til venstre"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fest til høyre"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Innstillinger for åpning som standard"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Velg hvordan nettlinker skal åpnes for denne appen"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I nettleseren"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 47d66d5..2fc5e09 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"एपको मेनु खोल्न ट्याप गर्नुहोस्"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"एकभन्दा बढी एपहरू सँगै देखाउन ट्याप गर्नुहोस्"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"एपको मेनुबाट फुल स्क्रिनमा फर्कनुहोस्"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"एपको मेनु यहाँ भेट्टाउन सकिन्छ"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एकभन्दा बढी एपहरू सँगै देखाउन डेस्कटप भ्यू हाल्नुहोस्"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"जुनसुकै बेला एपको मेनुबाट फुल स्क्रिनमा फर्कनुहोस्"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"थप कुरा हेर्नुहोस् र गर्नुहोस्"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रिन मोड प्रयोग गर्न अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"तपाईं जुन एपको स्थिति मिलाउन चाहनुहुन्छ सोही एपको बाहिर डबल ट्याप गर्नुहोस्"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ठुलो बनाउनुहोस्"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बायाँतिर स्न्याप गर्नुहोस्"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दायाँतिर स्न्याप गर्नुहोस्"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफल्ट सेटिङअनुसार खोल्नुहोस्"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"यो एपका वेब लिंकहरू खोल्ने तरिका छनौट गर्नुहोस्"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"एपमा"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तपाईंको ब्राउजरमा"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठिक छ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index e3e3441..65fd8ea 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tik om het app-menu te openen"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tik om meerdere apps tegelijk te tonen"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Terug naar volledig scherm vanuit het app-menu"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Het app-menu vind je hier"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Ga naar de desktopweergave om meerdere apps tegelijk te openen"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ga wanneer je wilt terug naar volledig scherm vanuit het app-menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zie en doe meer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep een andere app hier naartoe om het scherm te splitsen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximaliseren"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links uitlijnen"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts uitlijnen"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Instellingen voor Standaard openen"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Kies hoe je weblinks voor deze app wilt openen"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In de app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In je browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index baf009e..1f96daa 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ଆପ ମେନୁ ଖୋଲିବାକୁ ଟାପ କରନ୍ତୁ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ଏକାଠି ଏକାଧିକ ଆପ୍ସ ଦେଖାଇବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ଆପ ମେନୁରୁ ପୂର୍ଣ୍ଣସ୍କ୍ରିନକୁ ଫେରନ୍ତୁ"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ଆପ ମେନୁ ଏଠାରେ ମିଳିପାରିବ"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ଏକାଠି ଏକାଧିକ ଆପ୍ସ ଖୋଲିବାକୁ ଡେସ୍କଟପ ଭ୍ୟୁରେ ପ୍ରବେଶ କରନ୍ତୁ"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ଆପ ମେନୁରୁ ଯେ କୌଣସି ସମୟରେ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଫେରନ୍ତୁ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ଦେଖନ୍ତୁ ଏବଂ ଆହୁରି ଅନେକ କିଛି କରନ୍ତୁ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ଏକ ଆପକୁ ରିପୋଜିସନ କରିବା ପାଇଁ ଏହାର ବାହାରେ ଦୁଇଥର-ଟାପ କରନ୍ତୁ"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ବାମରେ ସ୍ନାପ କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ଡାହାଣରେ ସ୍ନାପ କରନ୍ତୁ"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ଡିଫଲ୍ଟ ସେଟିଂସକୁ ଖୋଲନ୍ତୁ"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ଏହି ଆପ ପାଇଁ ୱେବ ଲିଙ୍କଗୁଡ଼ିକୁ କିପରି ଖୋଲିବେ, ତାହା ବାଛନ୍ତୁ"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ଆପରେ"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ଆପଣଙ୍କ ବ୍ରାଉଜରରେ"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ଠିକ ଅଛି"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 2c29c7f..f93f509 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ਐਪ ਮੀਨੂ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"ਕਈ ਐਪਾਂ ਇਕੱਠੀਆਂ ਦਿਖਾਉਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ਐਪ ਮੀਨੂ ਤੋਂ ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ਐਪ ਮੀਨੂ ਇੱਥੇ ਮਿਲ ਸਕਦਾ ਹੈ"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ਕਈ ਐਪਾਂ ਨੂੰ ਇਕੱਠੇ ਖੋਲ੍ਹਣ ਲਈ ਡੈਸਕਟਾਪ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ਐਪ ਮੀਨੂ ਤੋਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ਦੇਖੋ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਕਰੋ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੇ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ਕਿਸੇ ਐਪ ਦੀ ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਉਸ ਦੇ ਬਾਹਰ ਡਬਲ ਟੈਪ ਕਰੋ"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ਵੱਡਾ ਕਰੋ"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ਖੱਬੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ਸੱਜੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੈਟਿੰਗਾਂ ਮੁਤਾਬਕ ਖੋਲ੍ਹੋ"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ਇਸ ਐਪ ਲਈ ਵੈੱਬ ਲਿੰਕਾਂ ਨੂੰ ਖੋਲ੍ਹਣ ਦਾ ਤਰੀਕਾ ਚੁਣੋ"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ਐਪ ਵਿੱਚ"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ਤੁਹਾਡੇ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ਠੀਕ ਹੈ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 1e7b1811..1e76e82 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Kliknij, aby otworzyć menu aplikacji"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Kliknij, aby wyświetlić jednocześnie kilka aplikacji"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Wróć do trybu pełnoekranowego z menu aplikacji"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tu znajdziesz menu aplikacji"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Aby otworzyć kilka aplikacji jednocześnie, przejdź do widoku pulpitu"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z menu aplikacji w każdej chwili możesz wrócić do pełnego ekranu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobacz i zrób więcej"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Aby podzielić ekran, przeciągnij drugą aplikację"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kliknij dwukrotnie poza aplikacją, aby ją przenieść"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksymalizuj"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Przyciągnij do lewej"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Przyciągnij do prawej"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ustawienia domyślnego otwierania"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Wybierz, gdzie chcesz otwierać linki z tej aplikacji"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"W aplikacji"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"W przeglądarce"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 7d728a0..0bdb078 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toque para abrir o menu do app"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toque para mostrar vários apps juntos"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Volte para a tela cheia no menu do app"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Acesse a versão para computadores para abrir vários apps ao mesmo tempo"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para este app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 752fd6f..0e10da1 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toque para abrir o menu de apps"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toque para mostrar várias apps em conjunto"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Regresse ao ecrã inteiro a partir do menu de apps"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu da app pode ser encontrado aqui"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entre na vista de computador para abrir várias apps em conjunto"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresse ao ecrã inteiro em qualquer altura a partir do menu da app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outra app para usar o ecrã dividido"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de uma app para a reposicionar"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Encaixar à esquerda"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Encaixar à direita"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Definições de Abrir por predefinição"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para esta app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 7d728a0..0bdb078 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Toque para abrir o menu do app"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Toque para mostrar vários apps juntos"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Volte para a tela cheia no menu do app"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Acesse a versão para computadores para abrir vários apps ao mesmo tempo"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para este app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 3985d9b..c942736 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ai probleme cu camera foto?\nAtinge pentru a reîncadra"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a închide."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Atinge pentru a deschide meniul aplicației"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Atinge pentru a afișa mai multe aplicații împreună"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Revino la ecranul complet din meniul aplicației"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meniul aplicației poate fi găsit aici"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accesează afișarea pe desktop pentru a deschide mai multe aplicații simultan"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revino oricând la ecranul complet din meniul aplicației"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vezi și fă mai multe"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Atinge de două ori lângă o aplicație pentru a o repoziționa"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizează"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Trage la stânga"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Trage la dreapta"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Setări de deschidere în mod prestabilit"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Alege modul de deschidere a linkurilor web pentru aplicație"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"În aplicație"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"În browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 58a196b..e2c3938 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Нажмите, чтобы открыть меню приложения"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Нажмите, чтобы на экране размещались сразу несколько приложений"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вернуться из меню приложения в режим полного экрана"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Здесь вы найдете меню приложения"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Чтобы открыть сразу несколько приложений, перейдите в режим компьютера"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вернуться в полноэкранный режим можно из меню приложения"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Выполняйте несколько задач одновременно"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Чтобы переместить приложение, дважды нажмите рядом с ним."</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Развернуть"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Привязать слева"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Привязать справа"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Настройки, регулирующие, как по умолчанию открываются ссылки"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Выберите, где будут открываться ссылки из этого приложения"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложении"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузере"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 6682b01..83a09f5 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"යෙදුම් මෙනුව විවෘත කිරීමට තට්ටු කරන්න"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"යෙදුම් කිහිපයක් එකට පෙන්වීමට තට්ටු කරන්න"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"යෙදුම් මෙනුවෙන් පූර්ණ තිරය වෙත ආපසු යන්න"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"යෙදුම් මෙනුව මෙතැනින් සොයා ගත හැක"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"යෙදුම් කිහිපයක් එකට විවෘත කිරීමට ඩෙස්ක්ටොප් දසුනට ඇතුළු වන්න"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"යෙදුම් මෙනුවෙන් ඕනෑම වේලාවක පූර්ණ තිරය වෙත ආපසු යන්න"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"බලන්න සහ තවත් දේ කරන්න"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"බෙදුම් තිරය සඳහා වෙනත් යෙදුමකට අදින්න"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"යෙදුමක් නැවත ස්ථානගත කිරීමට පිටතින් දෙවරක් තට්ටු කරන්න"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"විහිදන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"වමට ස්නැප් කරන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"දකුණට ස්නැප් කරන්න"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"පෙරනිමි සැකසීම් මඟින් විවෘත කරන්න"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"මෙම යෙදුම සඳහා වෙබ් සබැඳි විවෘත කරන ආකාරය තෝරා ගන්න"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"යෙදුම තුළ"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ඔබේ බ්‍රව්සරය තුළ"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"හරි"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 96e54f1..1b3907e 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Klepnúť a otvoriť tak ponuku aplikácií"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Klepnúť a zobraziť tak viacero aplikácií naraz"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Prejsť späť na celú obrazovku z ponuky aplikácií"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ponuku aplikácie nájdete tu"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Prejdite do zobrazenia v počítači a otvorte viac aplikácií naraz"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z ponuky aplikácie sa môžete kedykoľvek vrátiť na celú obrazovku"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobrazte si a zvládnite toho viac"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Rozdelenú obrazovku môžete použiť presunutím do inej aplikácie"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikácie zmeníte jej pozíciu"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovať"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prichytiť vľavo"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prichytiť vpravo"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvárať podľa predvolených nastavení"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Vyberte, ako sa majú v tejto aplikácii otvárať webové odkazy"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikácii"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prehliadači"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 66c9b26..0a1b4a6 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Dotaknite se, če želite odpreti meni aplikacije"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Dotaknite se, če želite prikazati več aplikacij hkrati"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Nazaj v celozaslonski način iz menija aplikacije"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije najdete tukaj"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Preklopite v pogled za namizni računalnik, če želite odpreti več aplikacij hkrati"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"V meniju aplikacije se lahko kadar koli vrnete v celozaslonski način"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Oglejte si in naredite več"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvakrat se dotaknite zunaj aplikacije, če jo želite prestaviti."</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiraj"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pripni levo"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pripni desno"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Nastavitve privzetega odpiranja"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Izberite način odpiranja spletnih povezav za to aplikacijo"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaciji"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V brskalniku"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"V redu"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 6e49999..75120d2 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Trokit për të hapur menynë e aplikacionit"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Trokit për të shfaqur disa aplikacone bashkë"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Kthehu tek ekrani i plotë nga menyja e aplikacionit"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menyja e aplikacioneve mund të gjendet këtu"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Kalo te pamja e desktopit për të hapur disa aplikacione së bashku"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kthehu tek ekrani i plotë në çdo kohë nga menyja e aplikacioneve"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Trokit dy herë jashtë një aplikacioni për ta ripozicionuar"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizo"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Zhvendos majtas"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Zhvendos djathtas"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Hap sipas cilësimeve të parazgjedhura"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Zgjidh si do t\'i hapësh lidhjet e uebit për këtë aplikacion"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Në aplikacion"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Në shfletuesin tënd"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Në rregull"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index bd2fb8c..8b5c4df 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Додирните да бисте отворили мени апликације"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Додирните да бисте приказали више апликација заједно"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Вратите се из менија апликације на приказ преко целог екрана"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Мени апликације можете да пронађете овде"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Уђите у приказ за рачунаре да бисте истовремено отворили више апликација"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратите се на цео екран било када из менија апликације"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Видите и урадите више"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Превуците другу апликацију да бисте користили подељени екран"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Двапут додирните изван апликације да бисте променили њену позицију"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увећајте"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прикачите лево"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прикачите десно"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Подешавање Подразумевано отварај"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Одаберите начин отварања веб-линкова за ову апликацију"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У апликацији"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У прегледачу"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Потврди"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 2184ac6..e40b649 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Tryck för att öppna appmenyn"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Tryck för att visa flera appar tillsammans"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Återgå till helskärm från appmenyn"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenyn finns här"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Starta datorvyn för att öppna flera appar samtidigt"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Återgå till helskärm när som helst från appmenyn"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se och gör mer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra till en annan app för att dela upp skärmen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryck snabbt två gånger utanför en app för att flytta den"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Utöka"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fäst till vänster"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fäst till höger"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Inställningar för Öppna som standard"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Välj hur webblänkar ska öppnas för den här appen"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I webbläsaren"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 6068bf0..e63229c 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Gusa ili ufungue menyu ya programu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Gusa ili uonyeshe programu nyingi kwa pamoja"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Rudi kwenye skrini nzima katika menyu ya programu"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Unaweza kupata menyu ya programu hapa"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Tumia mwonekano wa kompyuta ili ufungue programu nyingi pamoja"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Rudi kwenye skrini nzima wakati wowote ukitumia menyu ya programu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Angalia na ufanye zaidi"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Buruta katika programu nyingine ili utumie skrini iliyogawanywa"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Gusa mara mbili nje ya programu ili uihamishe"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Panua"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Telezesha kushoto"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Telezesha kulia"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Fungua kwa mipangilio chaguomsingi"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Chagua jinsi ya kufungua viungo vya wavuti vya programu hii"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Kwenye programu"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kwenye kivinjari chako"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Sawa"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index a14abac..95972f1 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ஆப்ஸ் மெனுவைத் திறக்க தட்டவும்"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"பல ஆப்ஸை ஒன்றாகக் காட்ட தட்டவும்"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ஆப்ஸ் மெனுவில் இருந்து முழுத்திரைக்குச் செல்லும்"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ஆப்ஸ் மெனுவை இங்கே பார்க்கலாம்"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"பல ஆப்ஸை ஒன்றாகத் திறக்க டெஸ்க்டாப் காட்சிக்குச் செல்லலாம்"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ஆப்ஸ் மெனுவிலிருந்து எப்போது வேண்டுமானாலும் முழுத்திரைக்குத் திரும்பலாம்"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"பலவற்றைப் பார்த்தல் மற்றும் செய்தல்"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"திரைப் பிரிப்புக்கு மற்றொரு ஆப்ஸை இழுக்கலாம்"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ஆப்ஸை இடம் மாற்ற அதன் வெளியில் இருமுறை தட்டலாம்"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"பெரிதாக்கும்"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"இடதுபுறம் நகர்த்தும்"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"வலதுபுறம் நகர்த்தும்"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"இயல்பாக அமைப்புகளைத் திறக்கும்"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"இந்த ஆப்ஸில் வலை இணைப்புகளைத் திறக்கும் வழிமுறையைத் தேர்வுசெய்யுங்கள்"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ஆப்ஸில்"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"உங்கள் பிரவுசரில்"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"சரி"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 84e76a8..6223c83 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"యాప్ మెనూని తెరవడానికి ట్యాప్ చేయండి"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"పలు యాప్‌లను కలిపి చూడటానికి ట్యాప్ చేయండి"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"యాప్ మెనూ నుండి ఫుల్ స్క్రీన్‌కు తిరిగి రండి"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"యాప్ మెనూను ఇక్కడ పొందవచ్చు"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"పలు యాప్‌లను ఒకేసారి తెరవడానికి డెస్క్‌టాప్ వీక్షణకు ఎంటర్ అవ్వండి"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"యాప్ మెనూ నుండి ఏ సమయంలోనైనా ఫుల్ స్క్రీన్‌కు తిరిగి రండి"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"చూసి, మరిన్ని చేయండి"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"స్ప్లిట్ స్క్రీన్ కోసం మరొక యాప్‌లోకి లాగండి"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"యాప్ స్థానాన్ని మార్చడానికి దాని వెలుపల డబుల్-ట్యాప్ చేయండి"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"మ్యాగ్జిమైజ్ చేయండి"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ఎడమ వైపున స్నాప్ చేయండి"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"కుడి వైపున స్నాప్ చేయండి"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"ఆటోమేటిక్ సెట్టింగ్‌ల ద్వారా తెరవండి"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ఈ యాప్‌నకు సంబంధించిన వెబ్ లింక్‌లను ఎలా తెరవాలో ఎంచుకోండి"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"యాప్‌లో"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"మీ బ్రౌజర్‌లో"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"సరే"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 856893f..f74499c 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"แตะเพื่อเปิดเมนูแอป"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"แตะเพื่อแสดงแอปหลายรายการพร้อมกัน"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"กลับไปที่เต็มหน้าจอจากเมนูแอป"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ดูเมนูแอปที่นี่ได้"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"เข้าสู่มุมมองบนเดสก์ท็อปเพื่อเปิดหลายแอปพร้อมกัน"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"กลับไปที่โหมดเต็มหน้าจอได้ทุกเมื่อจากเมนูแอป"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"รับชมและทำสิ่งต่างๆ ได้มากขึ้น"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ลากไปไว้ในแอปอื่นเพื่อแยกหน้าจอ"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"แตะสองครั้งด้านนอกแอปเพื่อเปลี่ยนตำแหน่ง"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ขยายใหญ่สุด"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"จัดพอดีกับทางซ้าย"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"จัดพอดีกับทางขวา"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"เปิดตามการตั้งค่าเริ่มต้น"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"เลือกวิธีเปิดเว็บลิงก์สำหรับแอปนี้"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ในแอป"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ในเบราว์เซอร์"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ตกลง"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index dc92efd..7d984e0 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"I-tap para buksan ang menu ng app"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"I-tap para ipakita nang magkakasama ang maraming app"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Bumalik sa fullscreen mula sa menu ng app"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Makikita rito ang menu ng app"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Pumasok sa desktop view para magbukas ng maraming app nang sabay-sabay"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bumalik sa full screen anumang oras mula sa menu ng app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Tumingin at gumawa ng higit pa"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Mag-drag ng isa pang app para sa split screen"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Mag-double tap sa labas ng app para baguhin ang posisyon nito"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"I-maximize"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"I-snap pakaliwa"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"I-snap pakanan"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buksan sa pamamagitan ng mga default na setting"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Piliin kung paano magbukas ng web link para sa app na ito"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sa app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Sa iyong browser"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index e206cd6..ba186aa 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Uygulama menüsünü açmak için dokunun"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Birden fazla uygulamayı birlikte göstermek için dokunun"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Uygulama menüsünden tam ekrana dönün"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Uygulama menüsünü burada bulabilirsiniz"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Birden fazla uygulamayı birlikte açmak için masaüstü görünümüne geçin"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Uygulama menüsünden dilediğiniz zaman tam ekrana dönebilirsiniz"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daha fazlasını görün ve yapın"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran için başka bir uygulamayı sürükleyin"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Yeniden konumlandırmak için uygulamanın dışına iki kez dokunun"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ekranı kapla"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tuttur"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tuttur"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Varsayılan olarak açma ayarları"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu uygulama için web bağlantılarının nasıl açılacağını seçin"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Uygulamada"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Tarayıcınızda"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Tamam"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 90a3bc33b..756e64d 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Натисніть, щоб відкрити меню додатка"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Натисніть, щоб переглянути кілька додатків одночасно"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Повернутися з меню додатка в повноекранний режим"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Тут ви знайдете меню додатка"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Щоб відкрити кілька додатків одночасно, перейдіть у режим робочого стола"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"З меню додатка можна будь-коли повернутися в повноекранний режим"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Більше простору та можливостей"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Щоб перейти в режим розділення екрана, перетягніть сюди інший додаток"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Щоб перемістити додаток, двічі торкніться області поза ним"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Розгорнути"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Закріпити ліворуч"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Закріпити праворуч"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налаштування \"Відкривати за умовчанням\""</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Виберіть, як відкривати вебпосилання в цьому додатку"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У додатку"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У вебпереглядачі"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 2006b0b..8aaa306 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"ایپ مینو کھولنے کیلئے تھپتھپائیں"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"متعدد ایپس ایک ساتھ دکھانے کیلئے تھپتھپائیں"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"ایپ مینو سے مکمل اسکرین پر واپس جائیں"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ایپ کا مینو یہاں پایا جا سکتا ہے"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"متعدد ایپس کو ایک ساتھ کھولنے کے لیے ڈیسک ٹاپ منظر میں داخل ہوں"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ایپ مینو سے کسی بھی وقت فُل اسکرین پر واپس جائیں"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"دیکھیں اور بہت کچھ کریں"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسپلٹ اسکرین کے ليے دوسری ایپ میں گھسیٹیں"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"کسی ایپ کی پوزیشن تبدیل کرنے کے لیے اس ایپ کے باہر دو بار تھپتھپائیں"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بڑا کریں"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"دائیں منتقل کریں"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"بائیں منتقل کریں"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"بطور ڈیفالٹ ترتیبات کھولیں"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"اس ایپ کے لیے ویب لنکس کھولنے کا طریقہ منتخب کریں"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ایپ میں"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"آپ کے براؤزر میں"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ٹھیک ہے"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 4b163f7..4e4a58b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Ilova menyusini ochish uchun bosing"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Bir nechta ilovani birga chiqarish uchun bosing"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Ilova menyusidan butun ekranga qayting"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ilova menyusi shu yerda chiqadi"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Bir nechta ilovani birga ochish uchun kompyuter versiyasiga kiring"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ilova menyusi orqali istalganda butun ekranga qaytish mumkin"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Yana boshqa amallar"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Ekranni ikkiga ajratish uchun boshqa ilovani bu yerga torting"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Qayta joylash uchun ilova tashqarisiga ikki marta bosing"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Yoyish"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chapga tortish"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Oʻngga tortish"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Birlamchi sozlamalar asosida ochish"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu ilovalardagi veb havolalar qanday ochilishini tanlang"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ilovada"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerda"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index db86498..09a143a 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Nhấn để mở trình đơn ứng dụng"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Nhấn để hiển thị nhiều ứng dụng cùng lúc"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Quay lại chế độ toàn màn hình từ trình đơn ứng dụng"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Bạn có thể tìm thấy trình đơn ứng dụng tại đây"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Chuyển sang chế độ xem trên máy tính để bàn để mở nhiều ứng dụng cùng lúc"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Trở về chế độ toàn màn hình bất cứ lúc nào từ trình đơn ứng dụng"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Xem và làm được nhiều việc hơn"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Kéo một ứng dụng khác vào để chia đôi màn hình"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Nhấn đúp bên ngoài ứng dụng để đặt lại vị trí"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Phóng to tối đa"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Di chuyển nhanh sang trái"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Di chuyển nhanh sang phải"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Mở các chế độ cài đặt theo mặc định"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Chọn cách mở đường liên kết trang web cho ứng dụng này"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Trong ứng dụng"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Trên trình duyệt"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index ebf4b03..795febb 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"点按可打开应用菜单"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"点按可同时显示多个应用"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"从应用菜单可返回到全屏"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"您可以在此处找到应用菜单"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"进入桌面版视图可同时打开多个应用"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"随时从应用菜单返回全屏模式"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"查看和处理更多任务"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一个应用,即可使用分屏模式"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在某个应用外连续点按两次,即可调整它的位置"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"贴靠左侧"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"贴靠右侧"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"默认打开设置"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"选择如何打开此应用中的网页链接"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在此应用内"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在浏览器中"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"确定"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index f1d12fc..0c6ad61 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"輕按即可開啟應用程式選單"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"輕按即可同時顯示多個應用程式"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"從應用程式選單返回全螢幕"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可在這裡找到應用程式選單"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"進入桌面電腦檢視模式以同時開啟多個應用程式"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你可隨時從應用程式選單返回全螢幕"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一個應用程式即可分割螢幕"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在應用程式外輕按兩下即可調整位置"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"貼齊左邊"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"貼齊右邊"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"採用預設設定打開"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"選擇此應用程式開啟網絡連結的方式"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在應用程式內"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在瀏覽器中"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index b5c28d3..442a6fe 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"輕觸即可開啟應用程式選單"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"輕觸即可一次顯示多個應用程式"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"從應用程式選單返回全螢幕"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可以在這裡查看應用程式選單"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"進入電腦檢視畫面可以同時開啟多個應用程式"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你隨時可以從應用程式選單返回全螢幕模式"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖進另一個應用程式即可使用分割畫面模式"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在應用程式外輕觸兩下即可調整位置"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"靠左對齊"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"靠右對齊"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"開啟連結的預設設定"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"選擇如何開啟這個應用程式的網頁連結"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"使用應用程式"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"使用瀏覽器"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index f54d0ed..47613d4 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -97,9 +97,9 @@
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
-    <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Thepha ukuze uvule imenyu ye-app"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Thepha ukuze ubonise ama-app amaningi ndawonye"</string>
-    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Buyela esikrinini esigcwele ukusuka kumenyu ye-app"</string>
+    <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Imenyu ye-app ingatholakala lapha"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Faka ukubuka kwedeskithophu ukuze uvule ama-app amaningi ndawonye"</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Buyela esikrinini esigcwele noma nini ukusuka kumenyu ye-app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Bona futhi wenze okuningi"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Hudula kwenye i-app mayelana nokuhlukanisa isikrini"</string>
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Thepha kabili ngaphandle kwe-app ukuze uyimise kabusha"</string>
@@ -136,14 +136,9 @@
     <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Khulisa"</string>
     <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chofoza kwesobunxele"</string>
     <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Chofoza kwesokudla"</string>
-    <!-- no translation found for open_by_default_settings_text (2526548548598185500) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) -->
-    <skip />
-    <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) -->
-    <skip />
+    <string name="open_by_default_settings_text" msgid="2526548548598185500">"Vula amasethingi ngokuzenzakalela"</string>
+    <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Khetha indlela yokuvula amalinki ewebhu ale app"</string>
+    <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ku-app"</string>
+    <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kubhrawuza yakho"</string>
+    <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"KULUNGILE"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1f15651..df1e224 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -492,8 +492,12 @@
     <dimen name="desktop_mode_maximize_menu_buttons_outline_stroke">1dp</dimen>
     <!-- The radius of the inner fill of the maximize menu buttons. -->
     <dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
-    <!-- The padding between the outline and fill of the maximize menu buttons. -->
-    <dimen name="desktop_mode_maximize_menu_buttons_fill_padding">4dp</dimen>
+    <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
+    <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen>
+    <!-- The vertical padding between the outline and fill of the maximize menu restore button. -->
+    <dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen>
+    <!-- The horizontal padding between the outline and fill of the maximize menu restore button. -->
+    <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen>
 
     <!-- The corner radius of the maximize menu. -->
     <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 5ef8432..afac9f6 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -220,13 +220,13 @@
     <string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
 
     <!-- App handle education tooltip text for tooltip pointing to app handle -->
-    <string name="windowing_app_handle_education_tooltip">Tap to open the app menu</string>
+    <string name="windowing_app_handle_education_tooltip">The app menu can be found here</string>
 
     <!-- App handle education tooltip text for tooltip pointing to windowing image button -->
-    <string name="windowing_desktop_mode_image_button_education_tooltip">Tap to show multiple apps together</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip">Enter desktop view to open multiple apps together</string>
 
     <!-- App handle education tooltip text for tooltip pointing to app chip -->
-    <string name="windowing_desktop_mode_exit_education_tooltip">Return to fullscreen from the app menu</string>
+    <string name="windowing_desktop_mode_exit_education_tooltip">Return to full screen anytime from the app menu</string>
 
     <!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] -->
     <string name="letterbox_education_dialog_title">See and do more</string>
@@ -319,6 +319,8 @@
     <string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
     <!-- Accessibility text for the Maximize Menu's maximize button [CHAR LIMIT=NONE] -->
     <string name="desktop_mode_maximize_menu_maximize_button_text">Maximize</string>
+    <!-- Accessibility text for the Maximize Menu's restore button [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_maximize_menu_restore_button_text">Restore</string>
     <!-- Accessibility text for the Maximize Menu's snap left button [CHAR LIMIT=NONE] -->
     <string name="desktop_mode_maximize_menu_snap_left_button_text">Snap left</string>
     <!-- Accessibility text for the Maximize Menu's snap right button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index d061ae1..55cda78 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -43,7 +43,8 @@
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">52dp</item>
         <item name="android:gravity">start|center_vertical</item>
-        <item name="android:padding">16dp</item>
+        <item name="android:paddingStart">16dp</item>
+        <item name="android:paddingEnd">0dp</item>
         <item name="android:textSize">14sp</item>
         <item name="android:textFontWeight">500</item>
         <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java
index 26aae2d..02a7991 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java
@@ -26,5 +26,11 @@
     /**
      * Called when a transition changes the top, focused display.
      */
-    void onFocusedDisplayChanged(int displayId);
+    default void onFocusedDisplayChanged(int displayId) {}
+
+    /**
+     * Called when the per-app or system-wide focus state has changed for a task.
+     */
+    default void onFocusedTaskChanged(int taskId, boolean isFocusedOnDisplay,
+            boolean isFocusedGlobally) {}
 }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 647a555a..0150bcd 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.SystemProperties;
-import android.window.flags.DesktopModeFlags;
+import android.window.DesktopModeFlags;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
index 7f1e4a8..5a2a723 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
@@ -61,11 +61,30 @@
     @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
             SPLIT_POSITION_UNDEFINED,
             SPLIT_POSITION_TOP_OR_LEFT,
-            SPLIT_POSITION_BOTTOM_OR_RIGHT
+            SPLIT_POSITION_BOTTOM_OR_RIGHT,
     })
     public @interface SplitPosition {
     }
 
+    // These SPLIT_INDEX constants will be used in the same way as the above SPLIT_POSITION ints,
+    // but scalable to n apps. Eventually, SPLIT_POSITION can be deprecated and only the below
+    // will be used.
+    public static final int SPLIT_INDEX_UNDEFINED = -1;
+    public static final int SPLIT_INDEX_0 = 0;
+    public static final int SPLIT_INDEX_1 = 1;
+    public static final int SPLIT_INDEX_2 = 2;
+    public static final int SPLIT_INDEX_3 = 3;
+
+    @IntDef(prefix = {"SPLIT_INDEX_"}, value = {
+            SPLIT_INDEX_UNDEFINED,
+            SPLIT_INDEX_0,
+            SPLIT_INDEX_1,
+            SPLIT_INDEX_2,
+            SPLIT_INDEX_3
+    })
+    public @interface SplitIndex {
+    }
+
     /**
      * A snap target for two apps, where the split is 33-66. With FLAG_ENABLE_FLEXIBLE_SPLIT,
      * only used on tablets.
@@ -170,7 +189,7 @@
             SNAP_TO_NONE,
             SNAP_TO_START_AND_DISMISS,
             SNAP_TO_END_AND_DISMISS,
-            SNAP_TO_MINIMIZE
+            SNAP_TO_MINIMIZE,
     })
     public @interface SnapPosition {}
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
index 71bcb59..65132fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -22,7 +22,13 @@
 import android.content.Intent
 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.content.pm.PackageManager
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationUserState
 import android.net.Uri
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+
+private const val TAG = "AppToWebUtils"
 
 private val GenericBrowserIntent = Intent()
     .setAction(Intent.ACTION_VIEW)
@@ -58,4 +64,25 @@
     val component = intent.resolveActivity(packageManager) ?: return null
     intent.setComponent(component)
     return intent
-}
\ No newline at end of file
+}
+
+/**
+ * Returns the [DomainVerificationUserState] of the user associated with the given
+ * [DomainVerificationManager] and the given package.
+ */
+fun getDomainVerificationUserState(
+    manager: DomainVerificationManager,
+    packageName: String
+): DomainVerificationUserState? {
+    try {
+        return manager.getDomainVerificationUserState(packageName)
+    } catch (e: PackageManager.NameNotFoundException) {
+        ProtoLog.w(
+            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            "%s: Failed to get domain verification user state: %s",
+            TAG,
+            e.message!!
+        )
+        return null
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
index 4926cbd..a727b54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.TaskInfo
 import android.content.Context
+import android.content.pm.verify.domain.DomainVerificationManager
 import android.graphics.Bitmap
 import android.graphics.PixelFormat
 import android.view.LayoutInflater
@@ -30,6 +31,7 @@
 import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
 import android.view.WindowlessWindowManager
 import android.widget.ImageView
+import android.widget.RadioButton
 import android.widget.TextView
 import android.window.TaskConstants
 import com.android.wm.shell.R
@@ -58,8 +60,17 @@
     private lateinit var appIconView: ImageView
     private lateinit var appNameView: TextView
 
+    private lateinit var openInAppButton: RadioButton
+    private lateinit var openInBrowserButton: RadioButton
+
+    private val domainVerificationManager =
+        context.getSystemService(DomainVerificationManager::class.java)!!
+    private val packageName = taskInfo.baseActivity?.packageName!!
+
+
     init {
         createDialog()
+        initializeRadioButtons()
         bindAppInfo(appIconBitmap, appName)
     }
 
@@ -111,9 +122,30 @@
             closeMenu()
         }
 
+        dialog.setConfirmButtonClickListener {
+            setDefaultLinkHandlingSetting()
+            closeMenu()
+        }
+
         listener.onDialogCreated()
     }
 
+    private fun initializeRadioButtons() {
+        openInAppButton = dialog.requireViewById(R.id.open_in_app_button)
+        openInBrowserButton = dialog.requireViewById(R.id.open_in_browser_button)
+
+        val userState =
+            getDomainVerificationUserState(domainVerificationManager, packageName) ?: return
+        val openInApp = userState.isLinkHandlingAllowed
+        openInAppButton.isChecked = openInApp
+        openInBrowserButton.isChecked = !openInApp
+    }
+
+    private fun setDefaultLinkHandlingSetting() {
+        domainVerificationManager.setDomainVerificationLinkHandlingAllowed(
+            packageName, openInAppButton.isChecked)
+    }
+
     private fun closeMenu() {
         dialogContainer?.releaseView()
         dialogContainer = null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialogView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialogView.kt
index d03a38e..1b914f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialogView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialogView.kt
@@ -36,9 +36,6 @@
     private lateinit var backgroundDim: Drawable
 
     fun setDismissOnClickListener(callback: (View) -> Unit) {
-        val dismissButton = dialogContainer.requireViewById<Button>(
-            R.id.open_by_default_settings_dialog_dismiss_button)
-        dismissButton.setOnClickListener(callback)
         // Clicks on the background dim should also dismiss the dialog.
         setOnClickListener(callback)
         // We add a no-op on-click listener to the dialog container so that clicks on it won't
@@ -46,6 +43,13 @@
         dialogContainer.setOnClickListener { }
     }
 
+    fun setConfirmButtonClickListener(callback: (View) -> Unit) {
+        val dismissButton = dialogContainer.requireViewById<Button>(
+            R.id.open_by_default_settings_dialog_confirm_button
+        )
+        dismissButton.setOnClickListener(callback)
+    }
+
     override fun onFinishInflate() {
         super.onFinishInflate()
         dialogContainer = requireViewById(R.id.open_by_default_dialog_container)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index b90e6e2..19b51f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -355,11 +355,8 @@
                 int keyAction,
                 @BackEvent.SwipeEdge int swipeEdge
         ) {
-            mShellExecutor.execute(() -> onMotionEvent(
-                    /* touchX = */ touchX,
-                    /* touchY = */ touchY,
-                    /* keyAction = */ keyAction,
-                    /* swipeEdge = */ swipeEdge));
+            mShellExecutor.execute(
+                    () -> onMotionEvent(touchX, touchY, keyAction, swipeEdge));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e3fc5c2..e8e25e20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -572,6 +572,7 @@
      * @param expandedViewManager the bubble expanded view manager.
      * @param taskViewFactory the task view factory used to create the task view for the bubble.
      * @param positioner the bubble positioner.
+     * @param bubbleLogger log bubble metrics.
      * @param stackView the view the bubble is added to, iff showing as floating.
      * @param layerView the layer the bubble is added to, iff showing in the bubble bar.
      * @param iconFactory the icon factory used to create images for the bubble.
@@ -581,6 +582,7 @@
             BubbleExpandedViewManager expandedViewManager,
             BubbleTaskViewFactory taskViewFactory,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory iconFactory,
@@ -595,6 +597,7 @@
                     expandedViewManager,
                     taskViewFactory,
                     positioner,
+                    bubbleLogger,
                     stackView,
                     layerView,
                     iconFactory,
@@ -616,6 +619,7 @@
                     expandedViewManager,
                     taskViewFactory,
                     positioner,
+                    bubbleLogger,
                     stackView,
                     layerView,
                     iconFactory,
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 a8a8c2a..37e8ead 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
@@ -892,7 +892,7 @@
             registerBroadcastReceiver();
             if (isShowingAsBubbleBar()) {
                 mBubbleData.getOverflow().initializeForBubbleBar(
-                        mExpandedViewManager, mBubblePositioner);
+                        mExpandedViewManager, mBubblePositioner, mLogger);
             } else {
                 mBubbleData.getOverflow().initialize(
                         mExpandedViewManager, mStackView, mBubblePositioner);
@@ -1100,6 +1100,7 @@
                     mExpandedViewManager,
                     mBubbleTaskViewFactory,
                     mBubblePositioner,
+                    mLogger,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1111,6 +1112,7 @@
                     mExpandedViewManager,
                     mBubbleTaskViewFactory,
                     mBubblePositioner,
+                    mLogger,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1246,9 +1248,12 @@
      */
     public void dragBubbleToDismiss(String bubbleKey, long timestamp) {
         String selectedBubbleKey = mBubbleData.getSelectedBubbleKey();
-        if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) {
+        Bubble bubbleToDismiss = mBubbleData.getAnyBubbleWithkey(bubbleKey);
+        if (bubbleToDismiss != null) {
             mBubbleData.dismissBubbleWithKey(
                     bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp);
+            mLogger.log(bubbleToDismiss,
+                    BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE);
         }
         if (mBubbleData.hasBubbles()) {
             // We still have bubbles, if we dragged an individual bubble to dismiss we were expanded
@@ -1582,6 +1587,7 @@
                         mExpandedViewManager,
                         mBubbleTaskViewFactory,
                         mBubblePositioner,
+                        mLogger,
                         mStackView,
                         mLayerView,
                         mBubbleIconFactory,
@@ -1644,6 +1650,7 @@
                     mExpandedViewManager,
                     mBubbleTaskViewFactory,
                     mBubblePositioner,
+                    mLogger,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1724,6 +1731,7 @@
                 mExpandedViewManager,
                 mBubbleTaskViewFactory,
                 mBubblePositioner,
+                mLogger,
                 mStackView,
                 mLayerView,
                 mBubbleIconFactory,
@@ -1979,11 +1987,15 @@
 
         @Override
         public void addBubble(Bubble addedBubble) {
+            // Only log metrics event
+            mLogger.log(addedBubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED);
             // Nothing to do for adds, these are handled by launcher / in the bubble bar.
         }
 
         @Override
         public void updateBubble(Bubble updatedBubble) {
+            // Only log metrics event
+            mLogger.log(updatedBubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED);
             // Nothing to do for updates, these are handled by launcher / in the bubble bar.
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 709a7bd..4de9dfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -495,7 +495,7 @@
     /**
      * When this method is called it is expected that all info in the bubble has completed loading.
      * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
-     * BubbleTaskViewFactory, BubblePositioner, BubbleStackView,
+     * BubbleTaskViewFactory, BubblePositioner, BubbleLogger, BubbleStackView,
      * com.android.wm.shell.bubbles.bar.BubbleBarLayerView,
      * com.android.launcher3.icons.BubbleIconFactory, boolean)
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index c88a58b..6d757d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.bubbles;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.FrameworkStatsLog;
@@ -33,9 +32,10 @@
     /**
      * Bubble UI event.
      */
-    @VisibleForTesting
     public enum Event implements UiEventLogger.UiEventEnum {
 
+        // region bubble events
+
         @UiEvent(doc = "User dismissed the bubble via gesture, add bubble to overflow.")
         BUBBLE_OVERFLOW_ADD_USER_GESTURE(483),
 
@@ -64,7 +64,89 @@
         BUBBLE_OVERFLOW_SELECTED(600),
 
         @UiEvent(doc = "Restore bubble to overflow after phone reboot.")
-        BUBBLE_OVERFLOW_RECOVER(691);
+        BUBBLE_OVERFLOW_RECOVER(691),
+
+        // endregion
+
+        // region bubble bar events
+
+        @UiEvent(doc = "new bubble posted")
+        BUBBLE_BAR_BUBBLE_POSTED(1927),
+
+        @UiEvent(doc = "existing bubble updated")
+        BUBBLE_BAR_BUBBLE_UPDATED(1928),
+
+        @UiEvent(doc = "expanded a bubble from bubble bar")
+        BUBBLE_BAR_EXPANDED(1929),
+
+        @UiEvent(doc = "bubble bar collapsed")
+        BUBBLE_BAR_COLLAPSED(1930),
+
+        @UiEvent(doc = "dismissed single bubble from bubble bar by dragging it to dismiss target")
+        BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE(1931),
+
+        @UiEvent(doc = "dismissed single bubble from bubble bar by dragging the expanded view to "
+                + "dismiss target")
+        BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_EXP_VIEW(1932),
+
+        @UiEvent(doc = "dismiss bubble from app handle menu")
+        BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU(1933),
+
+        @UiEvent(doc = "bubble is dismissed due to app finishing the bubble activity")
+        BUBBLE_BAR_BUBBLE_ACTIVITY_FINISH(1934),
+
+        @UiEvent(doc = "dismissed the bubble bar by dragging it to dismiss target")
+        BUBBLE_BAR_DISMISSED_DRAG_BAR(1935),
+
+        @UiEvent(doc = "bubble bar moved to the left edge of the screen by dragging from the "
+                + "expanded view")
+        BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW(1936),
+
+        @UiEvent(doc = "bubble bar moved to the left edge of the screen by dragging from a single"
+                + " bubble")
+        BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE(1937),
+
+        @UiEvent(doc = "bubble bar moved to the left edge of the screen by dragging the bubble bar")
+        BUBBLE_BAR_MOVED_LEFT_DRAG_BAR(1938),
+
+        @UiEvent(doc = "bubble bar moved to the right edge of the screen by dragging from the "
+                + "expanded view")
+        BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW(1939),
+
+        @UiEvent(doc = "bubble bar moved to the right edge of the screen by dragging from a "
+                + "single bubble")
+        BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE(1940),
+
+        @UiEvent(doc = "bubble bar moved to the right edge of the screen by dragging the bubble "
+                + "bar")
+        BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR(1941),
+
+        @UiEvent(doc = "stop bubbling conversation from app handle menu")
+        BUBBLE_BAR_APP_MENU_OPT_OUT(1942),
+
+        @UiEvent(doc = "open app settings from app handle menu")
+        BUBBLE_BAR_APP_MENU_GO_TO_SETTINGS(1943),
+
+        @UiEvent(doc = "flyout shown for a bubble")
+        BUBBLE_BAR_FLYOUT(1944),
+
+        @UiEvent(doc = "notification for the bubble was canceled")
+        BUBBLE_BAR_BUBBLE_REMOVED_CANCELED(1945),
+
+        @UiEvent(doc = "user turned off bubbles from settings")
+        BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED(1946),
+
+        @UiEvent(doc = "bubble bar overflow opened")
+        BUBBLE_BAR_OVERFLOW_SELECTED(1947),
+
+        @UiEvent(doc = "max number of bubbles was reached in bubble bar, move bubble to overflow")
+        BUBBLE_BAR_OVERFLOW_ADD_AGED(1948),
+
+        @UiEvent(doc = "bubble promoted from overflow back to bubble bar")
+        BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR(1949),
+
+        // endregion
+        ;
 
         private final int mId;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 68c4657..c74412b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -73,17 +73,19 @@
 
     fun initializeForBubbleBar(
         expandedViewManager: BubbleExpandedViewManager,
-        positioner: BubblePositioner
+        positioner: BubblePositioner,
+        bubbleLogger: BubbleLogger,
     ) {
         createBubbleBarExpandedView()
             .initialize(
                 expandedViewManager,
                 positioner,
+                bubbleLogger,
                 /* isOverflow= */ true,
                 /* bubbleTaskView= */ null,
                 /* mainExecutor= */ null,
                 /* backgroundExecutor= */ null,
-                /* regionSamplingProvider= */ null
+                /* regionSamplingProvider= */ null,
             )
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 39fb2f49..96b6043 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -73,6 +73,7 @@
     private final WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
     private final WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
     private final WeakReference<BubblePositioner> mPositioner;
+    private final WeakReference<BubbleLogger> mBubbleLogger;
     private final WeakReference<BubbleStackView> mStackView;
     private final WeakReference<BubbleBarLayerView> mLayerView;
     private final BubbleIconFactory mIconFactory;
@@ -94,6 +95,7 @@
             BubbleExpandedViewManager expandedViewManager,
             BubbleTaskViewFactory taskViewFactory,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory factory,
@@ -106,6 +108,7 @@
         mExpandedViewManager = new WeakReference<>(expandedViewManager);
         mTaskViewFactory = new WeakReference<>(taskViewFactory);
         mPositioner = new WeakReference<>(positioner);
+        mBubbleLogger = new WeakReference<>(bubbleLogger);
         mStackView = new WeakReference<>(stackView);
         mLayerView = new WeakReference<>(layerView);
         mIconFactory = factory;
@@ -221,8 +224,9 @@
                 ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s",
                         mBubble.getKey());
                 viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(),
-                        mPositioner.get(), false /* isOverflow */, viewInfo.taskView,
-                        mMainExecutor, mBgExecutor, new RegionSamplingProvider() {
+                        mPositioner.get(), mBubbleLogger.get(), false /* isOverflow */,
+                        viewInfo.taskView, mMainExecutor, mBgExecutor,
+                        new RegionSamplingProvider() {
                             @Override
                             public RegionSamplingHelper createHelper(View sampledView,
                                     RegionSamplingHelper.SamplingCallback callback,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index e9a5933..c1da94c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -78,6 +78,7 @@
     private WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
     private WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
     private WeakReference<BubblePositioner> mPositioner;
+    private WeakReference<BubbleLogger>  mBubbleLogger;
     private WeakReference<BubbleStackView> mStackView;
     private WeakReference<BubbleBarLayerView> mLayerView;
     private BubbleIconFactory mIconFactory;
@@ -95,6 +96,7 @@
             BubbleExpandedViewManager expandedViewManager,
             BubbleTaskViewFactory taskViewFactory,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory factory,
@@ -107,6 +109,7 @@
         mExpandedViewManager = new WeakReference<>(expandedViewManager);
         mTaskViewFactory = new WeakReference<>(taskViewFactory);
         mPositioner = new WeakReference<>(positioner);
+        mBubbleLogger = new WeakReference<>(bubbleLogger);
         mStackView = new WeakReference<>(stackView);
         mLayerView = new WeakReference<>(layerView);
         mIconFactory = factory;
@@ -124,8 +127,9 @@
         }
         if (mLayerView.get() != null) {
             return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
-                    mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
-                    mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor);
+                    mTaskViewFactory.get(), mPositioner.get(), mBubbleLogger.get(),
+                    mLayerView.get(), mIconFactory, mBubble, mSkipInflation, mMainExecutor,
+                    mBackgroundExecutor);
         } else {
             return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
                     mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
@@ -187,6 +191,7 @@
                 BubbleExpandedViewManager expandedViewManager,
                 BubbleTaskViewFactory taskViewFactory,
                 BubblePositioner positioner,
+                BubbleLogger bubbleLogger,
                 BubbleBarLayerView layerView,
                 BubbleIconFactory iconFactory,
                 Bubble b,
@@ -200,9 +205,9 @@
                 LayoutInflater inflater = LayoutInflater.from(c);
                 info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
                         R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
-                info.bubbleBarExpandedView.initialize(
-                        expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView,
-                        mainExecutor, backgroundExecutor, new RegionSamplingProvider() {
+                info.bubbleBarExpandedView.initialize(expandedViewManager, positioner, bubbleLogger,
+                        false /* isOverflow */, bubbleTaskView, mainExecutor, backgroundExecutor,
+                        new RegionSamplingProvider() {
                             @Override
                             public RegionSamplingHelper createHelper(View sampledView,
                                     RegionSamplingHelper.SamplingCallback callback,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 2a90017..0ce651c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -39,6 +39,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
+import com.android.wm.shell.bubbles.BubbleLogger;
 import com.android.wm.shell.bubbles.BubbleOverflowContainerView;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleTaskView;
@@ -90,6 +91,7 @@
     private Bubble mBubble;
     private BubbleExpandedViewManager mManager;
     private BubblePositioner mPositioner;
+    private BubbleLogger mBubbleLogger;
     private boolean mIsOverflow;
     private BubbleTaskViewHelper mBubbleTaskViewHelper;
     private BubbleBarMenuViewController mMenuViewController;
@@ -178,6 +180,7 @@
     /** Initializes the view, must be called before doing anything else. */
     public void initialize(BubbleExpandedViewManager expandedViewManager,
             BubblePositioner positioner,
+            BubbleLogger bubbleLogger,
             boolean isOverflow,
             @Nullable BubbleTaskView bubbleTaskView,
             @Nullable Executor mainExecutor,
@@ -185,6 +188,7 @@
             @Nullable RegionSamplingProvider regionSamplingProvider) {
         mManager = expandedViewManager;
         mPositioner = positioner;
+        mBubbleLogger = bubbleLogger;
         mIsOverflow = isOverflow;
         mMainExecutor = mainExecutor;
         mBackgroundExecutor = backgroundExecutor;
@@ -248,6 +252,7 @@
             @Override
             public void onDismissBubble(Bubble bubble) {
                 mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
+                mBubbleLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU);
             }
 
             @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index 0300869..52b807a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -16,6 +16,7 @@
 package com.android.wm.shell.bubbles.bar;
 
 import android.annotation.ColorInt;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
@@ -41,6 +42,9 @@
  * Bubble bar expanded view menu
  */
 public class BubbleBarMenuView extends LinearLayout {
+
+    public static final Object DISMISS_ACTION_TAG = new Object();
+
     private ViewGroup mBubbleSectionView;
     private ViewGroup mActionsSectionView;
     private ImageView mBubbleIconView;
@@ -119,6 +123,9 @@
                     R.layout.bubble_bar_menu_item, mActionsSectionView, false);
             itemView.update(action.mIcon, action.mTitle, action.mTint);
             itemView.setOnClickListener(action.mOnClick);
+            if (action.mTag != null) {
+                itemView.setTag(action.mTag);
+            }
             mActionsSectionView.addView(itemView);
         }
     }
@@ -159,6 +166,8 @@
         private Icon mIcon;
         private @ColorInt int mTint;
         private String mTitle;
+        @Nullable
+        private Object mTag;
         private OnClickListener mOnClick;
 
         MenuAction(Icon icon, String title, OnClickListener onClick) {
@@ -171,5 +180,14 @@
             this.mTint = tint;
             this.mOnClick = onClick;
         }
+
+        MenuAction(Icon icon, String title, @ColorInt int tint, @Nullable Object tag,
+                OnClickListener onClick) {
+            this.mIcon = icon;
+            this.mTitle = title;
+            this.mTint = tint;
+            this.mTag = tag;
+            this.mOnClick = onClick;
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 5148107..5ed01b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -212,6 +212,7 @@
                 Icon.createWithResource(resources, R.drawable.ic_remove_no_shadow),
                 resources.getString(R.string.bubble_dismiss_text),
                 tintColor,
+                BubbleBarMenuView.DISMISS_ACTION_TAG,
                 view -> {
                     hideMenu(true /* animated */);
                     if (mListener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index b3491ba..b83b5f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -177,26 +177,84 @@
     }
 
     /**
+     * Calculates the transform to apply on a UNTRANSFORMED (config-at-end) Activity surface in
+     * order for it's hint-rect to occupy the same task-relative position/dimensions as it would
+     * have at the end of the transition (post-configuration).
+     *
+     * This is intended to be used in tandem with [calcStartTransform] below applied to the parent
+     * task. Applying both transforms simultaneously should result in the appearance of nothing
+     * having happened yet.
+     *
+     * Only the task should be animated (into it's identity state) and then WMCore will reset the
+     * activity transform in sync with its new configuration upon finish.
+     *
+     * Usage example:
+     *     calcEndTransform(pipActivity, pipTask, scale, pos);
+     *     t.setScale(pipActivity.getLeash(), scale.x, scale.y);
+     *     t.setPosition(pipActivity.getLeash(), pos.x, pos.y);
+     *
+     * @see calcStartTransform
+     */
+    @JvmStatic
+    fun calcEndTransform(pipActivity: TransitionInfo.Change, pipTask: TransitionInfo.Change,
+        outScale: PointF, outPos: PointF) {
+        val actStartBounds = pipActivity.startAbsBounds
+        val actEndBounds = pipActivity.endAbsBounds
+        val taskEndBounds = pipTask.endAbsBounds
+
+        var hintRect = pipTask.taskInfo?.pictureInPictureParams?.sourceRectHint
+        if (hintRect == null) {
+            hintRect = Rect(actStartBounds)
+            hintRect.offsetTo(0, 0)
+        }
+
+        // FA = final activity bounds (absolute)
+        // FT = final task bounds (absolute)
+        // SA = start activity bounds (absolute)
+        // H = source hint (relative to start activity bounds)
+        // We want to transform the activity so that when the task is at FT, H overlaps with FA
+
+        // This scales the activity such that the hint rect has the same dimensions
+        // as the final activity bounds.
+        val hintToEndScaleX = (actEndBounds.width().toFloat()) / (hintRect.width().toFloat())
+        val hintToEndScaleY = (actEndBounds.height().toFloat()) / (hintRect.height().toFloat())
+        // top-left needs to be (FA.tl - FT.tl) - H.tl * hintToEnd . H is relative to the
+        // activity; so, for example, if shrinking H to FA (hintToEnd < 1), then the tl of the
+        // shrunk SA is closer to H than expected, so we need to reduce how much we offset SA
+        // to get H.tl to match.
+        val startActPosInTaskEndX =
+            (actEndBounds.left - taskEndBounds.left) - hintRect.left * hintToEndScaleX
+        val startActPosInTaskEndY =
+            (actEndBounds.top - taskEndBounds.top) - hintRect.top * hintToEndScaleY
+        outScale.set(hintToEndScaleX, hintToEndScaleY)
+        outPos.set(startActPosInTaskEndX, startActPosInTaskEndY)
+    }
+
+    /**
      * Calculates the transform and crop to apply on a Task surface in order for the config-at-end
      * activity inside it (original-size activity transformed to match it's hint rect to the final
      * Task bounds) to occupy the same world-space position/dimensions as it had before the
      * transition.
      *
+     * Intended to be used in tandem with [calcEndTransform].
+     *
      * Usage example:
-     *     calcStartTransform(pipChange, scale, pos, crop);
-     *     t.setScale(pipChange.getLeash(), scale.x, scale.y);
-     *     t.setPosition(pipChange.getLeash(), pos.x, pos.y);
-     *     t.setCrop(pipChange.getLeash(), crop);
+     *     calcStartTransform(pipTask, scale, pos, crop);
+     *     t.setScale(pipTask.getLeash(), scale.x, scale.y);
+     *     t.setPosition(pipTask.getLeash(), pos.x, pos.y);
+     *     t.setCrop(pipTask.getLeash(), crop);
+     *
+     * @see calcEndTransform
      */
     @JvmStatic
-    fun calcStartTransform(pipChange: TransitionInfo.Change, outScale: PointF,
+    fun calcStartTransform(pipTask: TransitionInfo.Change, outScale: PointF,
         outPos: PointF, outCrop: Rect) {
-        val startBounds = pipChange.startAbsBounds
-        val taskEndBounds = pipChange.endAbsBounds
+        val startBounds = pipTask.startAbsBounds
+        val taskEndBounds = pipTask.endAbsBounds
         // For now, pip activity bounds always matches task bounds. If this ever changes, we'll
         // need to get the activity offset.
         val endBounds = taskEndBounds
-        var hintRect = pipChange.taskInfo?.pictureInPictureParams?.sourceRectHint
+        var hintRect = pipTask.taskInfo?.pictureInPictureParams?.sourceRectHint
         if (hintRect == null) {
             hintRect = Rect(startBounds)
             hintRect.offsetTo(0, 0)
@@ -226,8 +284,8 @@
                 + startBounds.left + hintRect.left)
         val endTaskPosForStartY = (-(endBounds.top - taskEndBounds.top) * endToHintScaleY
                 + startBounds.top + hintRect.top)
-        outScale[endToHintScaleX] = endToHintScaleY
-        outPos[endTaskPosForStartX] = endTaskPosForStartY
+        outScale.set(endToHintScaleX, endToHintScaleY)
+        outPos.set(endTaskPosForStartX, endTaskPosForStartY)
 
         // now need to set crop to reveal the non-hint stuff. Again, hintrect is relative, so
         // we must apply outsets to reveal the *activity* content which is *inside* the task
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 9f100fa..8592170 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -19,9 +19,11 @@
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_66_33;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_MINIMIZE;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NONE;
@@ -33,6 +35,9 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.wm.shell.Flags;
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+
 import java.util.ArrayList;
 
 /**
@@ -79,8 +84,10 @@
     private final int mTaskHeightInMinimizedMode;
     private final float mFixedRatio;
     /** Allows split ratios to calculated dynamically instead of using {@link #mFixedRatio}. */
-    private final boolean mAllowFlexibleSplitRatios;
-    private final boolean mIsHorizontalDivision;
+    private final boolean mCalculateRatiosBasedOnAvailableSpace;
+    /** Allows split ratios that go offscreen (a.k.a. "flexible split") */
+    private final boolean mAllowOffscreenRatios;
+    private final boolean mIsLeftRightSplit;
 
     /** The first target which is still splitting the screen */
     private final SnapTarget mFirstSplitTarget;
@@ -94,13 +101,13 @@
 
 
     public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
-        boolean isHorizontalDivision, Rect insets, int dockSide) {
-        this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
-            dockSide, false /* minimized */, true /* resizable */);
+            boolean isLeftRightSplit, Rect insets, int dockSide) {
+        this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets,
+                dockSide, false /* minimized */, true /* resizable */);
     }
 
     public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
-            boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode,
+            boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode,
             boolean isHomeResizable) {
         mMinFlingVelocityPxPerSecond =
                 MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
@@ -109,7 +116,7 @@
         mDividerSize = dividerSize;
         mDisplayWidth = displayWidth;
         mDisplayHeight = displayHeight;
-        mIsHorizontalDivision = isHorizontalDivision;
+        mIsLeftRightSplit = isLeftRightSplit;
         mInsets.set(insets);
         mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED :
                 res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode);
@@ -119,11 +126,14 @@
                 com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
         mMinimalSizeResizableTask = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.default_minimal_size_resizable_task);
-        mAllowFlexibleSplitRatios = res.getBoolean(
+        mCalculateRatiosBasedOnAvailableSpace = res.getBoolean(
                 com.android.internal.R.bool.config_flexibleSplitRatios);
+        // If this is a small screen or a foldable, use offscreen ratios
+        mAllowOffscreenRatios =
+                Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res);
         mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
                 com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
-        calculateTargets(isHorizontalDivision, dockSide);
+        calculateTargets(isLeftRightSplit, dockSide);
         mFirstSplitTarget = mTargets.get(1);
         mLastSplitTarget = mTargets.get(mTargets.size() - 2);
         mDismissStartTarget = mTargets.get(0);
@@ -208,18 +218,18 @@
     }
 
     private int getStartInset() {
-        if (mIsHorizontalDivision) {
-            return mInsets.top;
-        } else {
+        if (mIsLeftRightSplit) {
             return mInsets.left;
+        } else {
+            return mInsets.top;
         }
     }
 
     private int getEndInset() {
-        if (mIsHorizontalDivision) {
-            return mInsets.bottom;
-        } else {
+        if (mIsLeftRightSplit) {
             return mInsets.right;
+        } else {
+            return mInsets.bottom;
         }
     }
 
@@ -233,6 +243,11 @@
         return mFirstSplitTarget.position < position && position < mLastSplitTarget.position;
     }
 
+    /** Returns if we are currently on a device/screen that supports split apps going offscreen. */
+    public boolean areOffscreenRatiosSupported() {
+        return mAllowOffscreenRatios;
+    }
+
     private SnapTarget snap(int position, boolean hardDismiss) {
         if (shouldApplyFreeSnapMode(position)) {
             return new SnapTarget(position, SNAP_TO_NONE);
@@ -254,11 +269,11 @@
         return mTargets.get(minIndex);
     }
 
-    private void calculateTargets(boolean isHorizontalDivision, int dockedSide) {
+    private void calculateTargets(boolean isLeftRightSplit, int dockedSide) {
         mTargets.clear();
-        int dividerMax = isHorizontalDivision
-                ? mDisplayHeight
-                : mDisplayWidth;
+        int dividerMax = isLeftRightSplit
+                ? mDisplayWidth
+                : mDisplayHeight;
         int startPos = -mDividerSize;
         if (dockedSide == DOCKED_RIGHT) {
             startPos += mInsets.left;
@@ -266,57 +281,65 @@
         mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
         switch (mSnapMode) {
             case SNAP_MODE_16_9:
-                addRatio16_9Targets(isHorizontalDivision, dividerMax);
+                addRatio16_9Targets(isLeftRightSplit, dividerMax);
                 break;
             case SNAP_FIXED_RATIO:
-                addFixedDivisionTargets(isHorizontalDivision, dividerMax);
+                addFixedDivisionTargets(isLeftRightSplit, dividerMax);
                 break;
             case SNAP_ONLY_1_1:
-                addMiddleTarget(isHorizontalDivision);
+                addMiddleTarget(isLeftRightSplit);
                 break;
             case SNAP_MODE_MINIMIZED:
-                addMinimizedTarget(isHorizontalDivision, dockedSide);
+                addMinimizedTarget(isLeftRightSplit, dockedSide);
                 break;
         }
         mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
     }
 
-    private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
+    private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition,
             int bottomPosition, int dividerMax) {
-        maybeAddTarget(topPosition, topPosition - getStartInset(), SNAP_TO_2_33_66);
-        addMiddleTarget(isHorizontalDivision);
+        @PersistentSnapPosition int firstTarget =
+                mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66;
+        @PersistentSnapPosition int lastTarget =
+                mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33;
+        maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget);
+        addMiddleTarget(isLeftRightSplit);
         maybeAddTarget(bottomPosition,
-                dividerMax - getEndInset() - (bottomPosition + mDividerSize), SNAP_TO_2_66_33);
+                dividerMax - getEndInset() - (bottomPosition + mDividerSize), lastTarget);
     }
 
-    private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) {
-        int start = isHorizontalDivision ? mInsets.top : mInsets.left;
-        int end = isHorizontalDivision
-                ? mDisplayHeight - mInsets.bottom
-                : mDisplayWidth - mInsets.right;
+    private void addFixedDivisionTargets(boolean isLeftRightSplit, int dividerMax) {
+        int start = isLeftRightSplit ? mInsets.left : mInsets.top;
+        int end = isLeftRightSplit
+                ? mDisplayWidth - mInsets.right
+                : mDisplayHeight - mInsets.bottom;
         int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
-        if (mAllowFlexibleSplitRatios) {
+
+        if (mAllowOffscreenRatios) {
+            // TODO (b/349828130): This is a placeholder value, real measurements to come
+            size = (int) (0.3f * (end - start)) - mDividerSize / 2;
+        } else if (mCalculateRatiosBasedOnAvailableSpace) {
             size = Math.max(size, mMinimalSizeResizableTask);
         }
         int topPosition = start + size;
         int bottomPosition = end - size - mDividerSize;
-        addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
+        addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
     }
 
-    private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) {
-        int start = isHorizontalDivision ? mInsets.top : mInsets.left;
-        int end = isHorizontalDivision
-                ? mDisplayHeight - mInsets.bottom
-                : mDisplayWidth - mInsets.right;
-        int startOther = isHorizontalDivision ? mInsets.left : mInsets.top;
-        int endOther = isHorizontalDivision
+    private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) {
+        int start = isLeftRightSplit ? mInsets.left : mInsets.top;
+        int end = isLeftRightSplit
                 ? mDisplayWidth - mInsets.right
                 : mDisplayHeight - mInsets.bottom;
+        int startOther = isLeftRightSplit ? mInsets.top : mInsets.left;
+        int endOther = isLeftRightSplit
+                ? mDisplayHeight - mInsets.bottom
+                : mDisplayWidth - mInsets.right;
         float size = 9.0f / 16.0f * (endOther - startOther);
         int sizeInt = (int) Math.floor(size);
         int topPosition = start + sizeInt;
         int bottomPosition = end - sizeInt - mDividerSize;
-        addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
+        addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
     }
 
     /**
@@ -324,22 +347,22 @@
      * meets the minimal size requirement.
      */
     private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) {
-        if (smallerSize >= mMinimalSizeResizableTask) {
+        if (smallerSize >= mMinimalSizeResizableTask || mAllowOffscreenRatios) {
             mTargets.add(new SnapTarget(position, snapPosition));
         }
     }
 
-    private void addMiddleTarget(boolean isHorizontalDivision) {
-        int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+    private void addMiddleTarget(boolean isLeftRightSplit) {
+        int position = DockedDividerUtils.calculateMiddlePosition(isLeftRightSplit,
                 mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);
         mTargets.add(new SnapTarget(position, SNAP_TO_2_50_50));
     }
 
-    private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) {
+    private void addMinimizedTarget(boolean isLeftRightSplit, int dockedSide) {
         // In portrait offset the position by the statusbar height, in landscape add the statusbar
         // height as well to match portrait offset
         int position = mTaskHeightInMinimizedMode + mInsets.top;
-        if (!isHorizontalDivision) {
+        if (isLeftRightSplit) {
             if (dockedSide == DOCKED_LEFT) {
                 position += mInsets.left;
             } else if (dockedSide == DOCKED_RIGHT) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
index f25dfea..25157c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
@@ -97,12 +97,12 @@
         }
     }
 
-    public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets,
+    public static int calculateMiddlePosition(boolean isLeftRightSplit, Rect insets,
             int displayWidth, int displayHeight, int dividerSize) {
-        int start = isHorizontalDivision ? insets.top : insets.left;
-        int end = isHorizontalDivision
-                ? displayHeight - insets.bottom
-                : displayWidth - insets.right;
+        int start = isLeftRightSplit ? insets.left : insets.top;
+        int end = isLeftRightSplit
+                ? displayWidth - insets.right
+                : displayHeight - insets.bottom;
         return start + (end - start) / 2 - dividerSize / 2;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 83ffaf4..f73065ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -30,6 +30,8 @@
 import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
 import static com.android.wm.shell.shared.animation.Interpolators.LINEAR;
 import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -438,12 +440,31 @@
             dividerBounds.right = dividerBounds.left + mDividerWindowWidth;
             bounds1.right = position;
             bounds2.left = bounds1.right + mDividerSize;
+
+            // For flexible split, expand app offscreen as well
+            if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
+                if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
+                    bounds1.left = bounds1.right - bounds2.width();
+                } else {
+                    bounds2.right = bounds2.left + bounds1.width();
+                }
+            }
+
         } else {
             position += mRootBounds.top;
             dividerBounds.top = position - mDividerInsets;
             dividerBounds.bottom = dividerBounds.top + mDividerWindowWidth;
             bounds1.bottom = position;
             bounds2.top = bounds1.bottom + mDividerSize;
+
+            // For flexible split, expand app offscreen as well
+            if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
+                if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
+                    bounds1.top = bounds1.bottom - bounds2.width();
+                } else {
+                    bounds2.bottom = bounds2.top + bounds1.width();
+                }
+            }
         }
         DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */);
         DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */);
@@ -644,7 +665,7 @@
                 rootBounds.width(),
                 rootBounds.height(),
                 mDividerSize,
-                !mIsLeftRightSplit,
+                mIsLeftRightSplit,
                 insets,
                 mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
     }
@@ -669,6 +690,21 @@
                 });
     }
 
+    /**
+     * Moves the divider to the other side of the screen. Does nothing if the divider is in the
+     * center.
+     * TODO (b/349828130): Currently only supports the two-app case. For n-apps,
+     *  DividerSnapAlgorithm will need to be refactored, and this function will change as well.
+     */
+    public void flingDividerToOtherSide(@PersistentSnapPosition int currentSnapPosition) {
+        switch (currentSnapPosition) {
+            case SNAP_TO_2_10_90 ->
+                    snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getLastSplitTarget());
+            case SNAP_TO_2_90_10 ->
+                    snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getFirstSplitTarget());
+        }
+    }
+
     @VisibleForTesting
     void flingDividerPosition(int from, int to, int duration,
             @Nullable Runnable flingFinishedCallback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index bdbcb46..65bf389 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -18,6 +18,10 @@
 
 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_10_45_45;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_45_45_10;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -38,6 +42,8 @@
 
 /** Helper utility class for split screen components to use. */
 public class SplitScreenUtils {
+    private static final int LARGE_SCREEN_MIN_EDGE_DP = 600;
+
     /** Reverse the split position. */
     @SplitScreenConstants.SplitPosition
     public static int reverseSplitPosition(@SplitScreenConstants.SplitPosition int position) {
@@ -110,10 +116,9 @@
             Configuration config) {
         // Compare the max bounds sizes as on near-square devices, the insets may result in a
         // configuration in the other orientation
-        final boolean isLargeScreen = config.smallestScreenWidthDp >= 600;
         final Rect maxBounds = config.windowConfiguration.getMaxBounds();
         final boolean isLandscape = maxBounds.width() >= maxBounds.height();
-        return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen, isLandscape);
+        return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen(config), isLandscape);
     }
 
     /**
@@ -128,4 +133,41 @@
             return isLandscape;
         }
     }
+
+    /**
+     * Returns whether the current config is a large screen (tablet or unfolded foldable)
+     */
+    public static boolean isLargeScreen(Configuration config) {
+        return config.smallestScreenWidthDp >= LARGE_SCREEN_MIN_EDGE_DP;
+    }
+
+    /**
+     * Returns whether we should allow split ratios to go offscreen or not. If the device is a phone
+     * or a foldable (either screen), we allow it.
+     */
+    public static boolean allowOffscreenRatios(Resources res) {
+        return !isLargeScreen(res.getConfiguration())
+                ||
+                res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+    }
+
+    /**
+     * Within a particular split layout, we label the stages numerically: 0, 1, 2... from left to
+     * right (or top to bottom). This function takes in a stage index (0th, 1st, 2nd...) and a
+     * PersistentSnapPosition and returns if that particular stage is offscreen in that layout.
+     */
+    public static boolean isPartiallyOffscreen(int stageIndex,
+            @SplitScreenConstants.PersistentSnapPosition int snapPosition) {
+        switch(snapPosition) {
+            case SNAP_TO_2_10_90:
+            case SNAP_TO_3_10_45_45:
+                return stageIndex == 0;
+            case SNAP_TO_2_90_10:
+                return stageIndex == 1;
+            case SNAP_TO_3_45_45_10:
+                return stageIndex == 2;
+            default:
+                return false;
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 4d15605c..2128cbc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -27,7 +27,7 @@
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.window.flags.DesktopModeFlags;
+import android.window.DesktopModeFlags;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.window.flags.Flags;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 79c31e0..03a851b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,8 +16,9 @@
 
 package com.android.wm.shell.dagger;
 
-import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS;
-import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
 
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
@@ -71,10 +72,10 @@
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
 import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
 import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
 import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver;
-import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
 import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
 import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
 import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
@@ -92,9 +93,9 @@
 import com.android.wm.shell.freeform.FreeformTaskListener;
 import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
 import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
-import com.android.wm.shell.freeform.TaskChangeListener;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarterInitializer;
+import com.android.wm.shell.freeform.TaskChangeListener;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.pip.PipTransitionController;
@@ -111,6 +112,7 @@
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.taskview.TaskViewTransitions;
 import com.android.wm.shell.transition.DefaultMixedHandler;
+import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.HomeTransitionObserver;
 import com.android.wm.shell.transition.MixedTransitionHandler;
 import com.android.wm.shell.transition.Transitions;
@@ -263,7 +265,8 @@
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) {
+            Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler,
+            FocusTransitionObserver focusTransitionObserver) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return new DesktopModeWindowDecorViewModel(
                     context,
@@ -290,7 +293,8 @@
                     desktopTasksLimiter,
                     appHandleEducationController,
                     windowDecorCaptionHandleRepository,
-                    desktopActivityOrientationHandler);
+                    desktopActivityOrientationHandler,
+                    focusTransitionObserver);
         }
         return new CaptionWindowDecorViewModel(
                 context,
@@ -304,7 +308,8 @@
                 displayController,
                 rootTaskDisplayAreaOrganizer,
                 syncQueue,
-                transitions);
+                transitions,
+                focusTransitionObserver);
     }
 
     @WMSingleton
@@ -391,10 +396,11 @@
             Transitions transitions,
             Optional<DesktopFullImmersiveTransitionHandler> desktopImmersiveTransitionHandler,
             WindowDecorViewModel windowDecorViewModel,
-            Optional<TaskChangeListener> taskChangeListener) {
+            Optional<TaskChangeListener> taskChangeListener,
+            FocusTransitionObserver focusTransitionObserver) {
         return new FreeformTaskTransitionObserver(
                 context, shellInit, transitions, desktopImmersiveTransitionHandler,
-                windowDecorViewModel, taskChangeListener);
+                windowDecorViewModel, taskChangeListener, focusTransitionObserver);
     }
 
     @WMSingleton
@@ -591,9 +597,10 @@
             TransactionPool transactionPool,
             Transitions transitions,
             @ShellMainThread ShellExecutor executor,
+            @ShellMainThread Handler handler,
             ShellInit shellInit) {
         return new UnfoldTransitionHandler(shellInit, progressProvider.get(), animator,
-                unfoldAnimator, transactionPool, executor, transitions);
+                unfoldAnimator, transactionPool, executor, handler, transitions);
     }
 
     @WMSingleton
@@ -693,10 +700,16 @@
     static Optional<DesktopFullImmersiveTransitionHandler> provideDesktopImmersiveHandler(
             Context context,
             Transitions transitions,
-            @DynamicOverride DesktopRepository desktopRepository) {
+            @DynamicOverride DesktopRepository desktopRepository,
+            DisplayController displayController,
+            ShellTaskOrganizer shellTaskOrganizer) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.of(
-                    new DesktopFullImmersiveTransitionHandler(transitions, desktopRepository));
+                    new DesktopFullImmersiveTransitionHandler(
+                            transitions,
+                            desktopRepository,
+                            displayController,
+                            shellTaskOrganizer));
         }
         return Optional.empty();
     }
@@ -716,7 +729,8 @@
             Transitions transitions,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor) {
-        return Flags.enableDesktopWindowingTransitions()
+        return (Flags.enableDesktopWindowingTransitions() ||
+            ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
                 ? new SpringDragToDesktopTransitionHandler(context, transitions,
                         rootTaskDisplayAreaOrganizer, interactionJankMonitor)
                 : new DefaultDragToDesktopTransitionHandler(context, transitions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt
index f749aa1..320c003 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt
@@ -23,12 +23,17 @@
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.animation.DecelerateInterpolator
+import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
 import android.window.TransitionInfo
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import androidx.core.animation.addListener
+import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
-import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TransitionHandler
 import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
@@ -41,16 +46,29 @@
 class DesktopFullImmersiveTransitionHandler(
     private val transitions: Transitions,
     private val desktopRepository: DesktopRepository,
+    private val displayController: DisplayController,
+    private val shellTaskOrganizer: ShellTaskOrganizer,
     private val transactionSupplier: () -> SurfaceControl.Transaction,
 ) : TransitionHandler {
 
     constructor(
         transitions: Transitions,
         desktopRepository: DesktopRepository,
-    ) : this(transitions, desktopRepository, { SurfaceControl.Transaction() })
+        displayController: DisplayController,
+        shellTaskOrganizer: ShellTaskOrganizer,
+    ) : this(
+        transitions,
+        desktopRepository,
+        displayController,
+        shellTaskOrganizer,
+        { SurfaceControl.Transaction() }
+    )
 
     private var state: TransitionState? = null
 
+    @VisibleForTesting
+    val pendingExternalExitTransitions = mutableSetOf<ExternalPendingExit>()
+
     /** Whether there is an immersive transition that hasn't completed yet. */
     private val inProgress: Boolean
         get() = state != null
@@ -61,15 +79,15 @@
     var onTaskResizeAnimationListener: OnTaskResizeAnimationListener? = null
 
     /** Starts a transition to enter full immersive state inside the desktop. */
-    fun enterImmersive(taskInfo: RunningTaskInfo, wct: WindowContainerTransaction) {
+    fun moveTaskToImmersive(taskInfo: RunningTaskInfo) {
         if (inProgress) {
-            ProtoLog.v(
-                ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                "FullImmersive: cannot start entry because transition already in progress."
-            )
+            logV("Cannot start entry because transition already in progress.")
             return
         }
-
+        val wct = WindowContainerTransaction().apply {
+            setBounds(taskInfo.token, Rect())
+        }
+        logV("Moving task ${taskInfo.taskId} into immersive mode")
         val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this)
         state = TransitionState(
             transition = transition,
@@ -79,15 +97,16 @@
         )
     }
 
-    fun exitImmersive(taskInfo: RunningTaskInfo, wct: WindowContainerTransaction) {
+    fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo) {
         if (inProgress) {
-            ProtoLog.v(
-                ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
-                "$TAG: cannot start exit because transition already in progress."
-            )
+            logV("Cannot start exit because transition already in progress.")
             return
         }
 
+        val wct = WindowContainerTransaction().apply {
+            setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
+        }
+        logV("Moving task ${taskInfo.taskId} out of immersive mode")
         val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this)
         state = TransitionState(
             transition = transition,
@@ -97,6 +116,79 @@
         )
     }
 
+    /**
+     * Bring the immersive app of the given [displayId] out of immersive mode, if applicable.
+     *
+     * @param transition that will apply this transaction
+     * @param wct that will apply these changes
+     * @param displayId of the display that should exit immersive mode
+     */
+    fun exitImmersiveIfApplicable(
+        transition: IBinder,
+        wct: WindowContainerTransaction,
+        displayId: Int
+    ) {
+        if (!Flags.enableFullyImmersiveInDesktop()) return
+        exitImmersiveIfApplicable(wct, displayId)?.invoke(transition)
+    }
+
+    /**
+     * Bring the immersive app of the given [displayId] out of immersive mode, if applicable.
+     *
+     * @param wct that will apply these changes
+     * @param displayId of the display that should exit immersive mode
+     * @return a function to apply once the transition that will apply these changes is started
+     */
+    fun exitImmersiveIfApplicable(
+        wct: WindowContainerTransaction,
+        displayId: Int
+    ): ((IBinder) -> Unit)? {
+        if (!Flags.enableFullyImmersiveInDesktop()) return null
+        val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) ?: return null
+        val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return null
+        logV("Appending immersive exit for task: $immersiveTask in display: $displayId")
+        wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
+        return { transition -> addPendingImmersiveExit(immersiveTask, displayId, transition) }
+    }
+
+    /**
+     * Bring the given [taskInfo] out of immersive mode, if applicable.
+     *
+     * @param wct that will apply these changes
+     * @param taskInfo of the task that should exit immersive mode
+     * @return a function to apply once the transition that will apply these changes is started
+     */
+    fun exitImmersiveIfApplicable(
+        wct: WindowContainerTransaction,
+        taskInfo: RunningTaskInfo
+    ): ((IBinder) -> Unit)? {
+        if (!Flags.enableFullyImmersiveInDesktop()) return null
+        if (desktopRepository.isTaskInFullImmersiveState(taskInfo.taskId)) {
+            // A full immersive task is being minimized, make sure the immersive state is broken
+            // (i.e. resize back to max bounds).
+            wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
+            logV("Appending immersive exit for task: ${taskInfo.taskId}")
+            return { transition ->
+                addPendingImmersiveExit(
+                    taskId = taskInfo.taskId,
+                    displayId = taskInfo.displayId,
+                    transition = transition
+                )
+            }
+        }
+        return null
+    }
+
+    private fun addPendingImmersiveExit(taskId: Int, displayId: Int, transition: IBinder) {
+        pendingExternalExitTransitions.add(
+            ExternalPendingExit(
+                taskId = taskId,
+                displayId = displayId,
+                transition = transition
+            )
+        )
+    }
+
     override fun startAnimation(
         transition: IBinder,
         info: TransitionInfo,
@@ -190,15 +282,36 @@
      * Called when any transition in the system is ready to play. This is needed to update the
      * repository state before window decorations are drawn (which happens immediately after
      * |onTransitionReady|, before this transition actually animates) because drawing decorations
-     * depends in whether the task is in full immersive state or not.
+     * depends on whether the task is in full immersive state or not.
      */
-    fun onTransitionReady(transition: IBinder) {
-        val state = this.state ?: return
-        // TODO: b/369443668 - this assumes invoking the exit transition is the only way to exit
-        //  immersive, which isn't realistic. The app could crash, the user could dismiss it from
-        //  overview, etc. This (or its caller) should search all transitions to look for any
-        //  immersive task exiting that state to keep the repository properly updated.
-        if (transition == state.transition) {
+    fun onTransitionReady(transition: IBinder, info: TransitionInfo) {
+        // Check if this is a pending external exit transition.
+        val pendingExit = pendingExternalExitTransitions
+            .firstOrNull { pendingExit -> pendingExit.transition == transition }
+        if (pendingExit != null) {
+            pendingExternalExitTransitions.remove(pendingExit)
+            if (info.hasTaskChange(taskId = pendingExit.taskId)) {
+                if (desktopRepository.isTaskInFullImmersiveState(pendingExit.taskId)) {
+                    logV("Pending external exit for task ${pendingExit.taskId} verified")
+                    desktopRepository.setTaskInFullImmersiveState(
+                        displayId = pendingExit.displayId,
+                        taskId = pendingExit.taskId,
+                        immersive = false
+                    )
+                    if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
+                        desktopRepository.removeBoundsBeforeFullImmersive(pendingExit.taskId)
+                    }
+                }
+            }
+            return
+        }
+
+        // Check if this is a direct immersive enter/exit transition.
+        if (transition == state?.transition) {
+            val state = requireState()
+            val startBounds = info.changes.first { c -> c.taskInfo?.taskId == state.taskId }
+                .startAbsBounds
+            logV("Direct move for task ${state.taskId} in ${state.direction} direction verified")
             when (state.direction) {
                 Direction.ENTER -> {
                     desktopRepository.setTaskInFullImmersiveState(
@@ -206,6 +319,9 @@
                         taskId = state.taskId,
                         immersive = true
                     )
+                    if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
+                        desktopRepository.saveBoundsBeforeFullImmersive(state.taskId, startBounds)
+                    }
                 }
                 Direction.EXIT -> {
                     desktopRepository.setTaskInFullImmersiveState(
@@ -213,18 +329,54 @@
                         taskId = state.taskId,
                         immersive = false
                     )
+                    if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
+                        desktopRepository.removeBoundsBeforeFullImmersive(state.taskId)
+                    }
                 }
             }
+            return
         }
+
+        // Check if this is an untracked exit transition, like display rotation.
+        info.changes
+            .filter { c -> c.taskInfo != null }
+            .filter { c -> desktopRepository.isTaskInFullImmersiveState(c.taskInfo!!.taskId) }
+            .filter { c -> c.startRotation != c.endRotation }
+            .forEach { c ->
+                logV("Detected immersive exit due to rotation for task: ${c.taskInfo!!.taskId}")
+                desktopRepository.setTaskInFullImmersiveState(
+                    displayId = c.taskInfo!!.displayId,
+                    taskId = c.taskInfo!!.taskId,
+                    immersive = false
+                )
+            }
     }
 
     private fun clearState() {
         state = null
     }
 
+    private fun getExitDestinationBounds(taskInfo: RunningTaskInfo): Rect {
+        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
+            ?: error("Expected non-null display layout for displayId: ${taskInfo.displayId}")
+        return if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
+            desktopRepository.removeBoundsBeforeFullImmersive(taskInfo.taskId)
+                ?: if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
+                    calculateInitialBounds(displayLayout, taskInfo)
+                } else {
+                    calculateDefaultDesktopTaskBounds(displayLayout)
+                }
+        } else {
+            return calculateMaximizeBounds(displayLayout, taskInfo)
+        }
+    }
+
     private fun requireState(): TransitionState =
         state ?: error("Expected non-null transition state")
 
+    private fun TransitionInfo.hasTaskChange(taskId: Int): Boolean =
+        changes.any { c -> c.taskInfo?.taskId == taskId }
+
     /** The state of the currently running transition. */
     private data class TransitionState(
         val transition: IBinder,
@@ -233,12 +385,28 @@
         val direction: Direction
     )
 
+    /**
+     * Tracks state of a transition involving an immersive exit that is external to this class' own
+     * transitions. This usually means transitions that exit immersive mode as a side-effect and
+     * not the primary action (for example, minimizing the immersive task or launching a new task
+     * on top of the immersive task).
+     */
+    data class ExternalPendingExit(
+        val taskId: Int,
+        val displayId: Int,
+        val transition: IBinder,
+    )
+
     private enum class Direction {
         ENTER, EXIT
     }
 
+    private fun logV(msg: String, vararg arguments: Any?) {
+        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
     private companion object {
-        private const val TAG = "FullImmersiveHandler"
+        private const val TAG = "DesktopImmersive"
 
         private const val FULL_IMMERSIVE_ANIM_DURATION_MS = 336L
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 5a277316f..8ebe503 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -21,17 +21,39 @@
 import com.android.internal.util.FrameworkStatsLog
 import com.android.window.flags.Flags
 import com.android.wm.shell.EventLogTags
-import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import java.security.SecureRandom
+import java.util.Random
+import java.util.concurrent.atomic.AtomicInteger
+
 
 /** Event logger for logging desktop mode session events */
 class DesktopModeEventLogger {
+    private val random: Random = SecureRandom()
+
+    /** The session id for the current desktop mode session */
+    @VisibleForTesting
+    val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID)
+
+    private fun generateSessionId() = 1 + random.nextInt(1 shl 20)
+
     /**
-     * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for
-     * entering desktop mode
+     * Logs enter into desktop mode with [enterReason]
      */
-    fun logSessionEnter(sessionId: Int, enterReason: EnterReason) {
+    fun logSessionEnter(enterReason: EnterReason) {
+        val sessionId = generateSessionId()
+        val previousSessionId = currentSessionId.getAndSet(sessionId)
+        if (previousSessionId != NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: Existing desktop mode session id: %s found on desktop "
+                    + "mode enter",
+                previousSessionId
+            )
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging session enter, session: %s reason: %s",
             sessionId,
             enterReason.name
@@ -47,12 +69,20 @@
     }
 
     /**
-     * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for
-     * exiting desktop mode
+     * Logs exit from desktop mode session with [exitReason]
      */
-    fun logSessionExit(sessionId: Int, exitReason: ExitReason) {
+    fun logSessionExit(exitReason: ExitReason) {
+        val sessionId = currentSessionId.getAndSet(NO_SESSION_ID)
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging exit from desktop mode"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging session exit, session: %s reason: %s",
             sessionId,
             exitReason.name
@@ -68,12 +98,20 @@
     }
 
     /**
-     * Logs that the task with update [taskUpdate] was added in the desktop mode session having
-     * session id [sessionId]
+     * Logs that a task with [taskUpdate] was added in a desktop mode session
      */
-    fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) {
+    fun logTaskAdded(taskUpdate: TaskUpdate) {
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging task added"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task added, session: %s taskId: %s",
             sessionId,
             taskUpdate.instanceId
@@ -85,12 +123,20 @@
     }
 
     /**
-     * Logs that the task with update [taskUpdate] was removed in the desktop mode session having
-     * session id [sessionId]
+     * Logs that a task with [taskUpdate] was removed from a desktop mode session
      */
-    fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) {
+    fun logTaskRemoved(taskUpdate: TaskUpdate) {
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging task removed"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task remove, session: %s taskId: %s",
             sessionId,
             taskUpdate.instanceId
@@ -102,12 +148,20 @@
     }
 
     /**
-     * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session
-     * having session id [sessionId]
+     * Logs that a task with [taskUpdate] had it's info changed in a desktop mode session
      */
-    fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) {
+    fun logTaskInfoChanged(taskUpdate: TaskUpdate) {
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging task info changed"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task info changed, session: %s taskId: %s",
             sessionId,
             taskUpdate.instanceId
@@ -119,14 +173,23 @@
     }
 
     /**
-     * Logs that a task resize event is starting with [taskSizeUpdate] within a
-     * Desktop mode [sessionId].
+     * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode
+     * session.
      */
-    fun logTaskResizingStarted(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+    fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) {
         if (!Flags.enableResizingMetrics()) return
 
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging start of task resizing"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s",
             sessionId,
             taskSizeUpdate.instanceId
@@ -138,14 +201,22 @@
     }
 
     /**
-     * Logs that a task resize event is ending with [taskSizeUpdate] within a
-     * Desktop mode [sessionId].
+     * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session.
      */
-    fun logTaskResizingEnded(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+    fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) {
         if (!Flags.enableResizingMetrics()) return
 
+        val sessionId = currentSessionId.get()
+        if (sessionId == NO_SESSION_ID) {
+            ProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopModeLogger: No session id found for logging end of task resizing"
+            )
+            return
+        }
+
         ProtoLog.v(
-            ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+            WM_SHELL_DESKTOP_MODE,
             "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s",
             sessionId,
             taskSizeUpdate.instanceId
@@ -156,6 +227,21 @@
         )
     }
 
+    fun logTaskInfoStateInit() {
+        logTaskUpdate(
+            FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD,
+            /* session_id */ 0,
+            TaskUpdate(
+                visibleTaskCount = 0,
+                instanceId = 0,
+                uid = 0,
+                taskHeight = 0,
+                taskWidth = 0,
+                taskX = 0,
+                taskY = 0)
+        )
+    }
+
     private fun logTaskUpdate(taskEvent: Int, sessionId: Int, taskUpdate: TaskUpdate) {
         FrameworkStatsLog.write(
             DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
@@ -233,6 +319,7 @@
     }
 
     companion object {
+
         /**
          * Describes a task position and dimensions.
          *
@@ -450,5 +537,6 @@
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE
         private const val DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID =
             FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED
+        @VisibleForTesting const val NO_SESSION_ID = 0
     }
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index b8507e3..ed03982 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -35,8 +35,6 @@
 import androidx.core.util.isNotEmpty
 import androidx.core.util.plus
 import androidx.core.util.putAll
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.InstanceIdSequence
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
@@ -48,8 +46,8 @@
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 
@@ -65,8 +63,6 @@
     private val desktopModeEventLogger: DesktopModeEventLogger
 ) : Transitions.TransitionObserver {
 
-    private val idSequence: InstanceIdSequence by lazy { InstanceIdSequence(Int.MAX_VALUE) }
-
     init {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             shellInit.addInitCallback(this::onInit, this)
@@ -87,21 +83,14 @@
     // following enter reason could be Screen On
     private var wasPreviousTransitionExitByScreenOff: Boolean = false
 
-    // The instanceId for the current logging session
-    private var loggerInstanceId: InstanceId? = null
-
-    private val isSessionActive: Boolean
-        get() = loggerInstanceId != null
-
-    private fun setSessionInactive() {
-        loggerInstanceId = null
-    }
+    @VisibleForTesting var isSessionActive: Boolean = false
 
     fun onInit() {
         transitions.registerObserver(this)
         SystemProperties.set(
             VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
             VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY_DEFAULT_VALUE)
+        desktopModeEventLogger.logTaskInfoStateInit()
     }
 
     override fun onTransitionReady(
@@ -246,38 +235,32 @@
         ) {
             // Sessions is finishing, log task updates followed by an exit event
             identifyAndLogTaskUpdates(
-                loggerInstanceId!!.id,
                 preTransitionVisibleFreeformTasks,
                 postTransitionVisibleFreeformTasks
             )
 
             desktopModeEventLogger.logSessionExit(
-                loggerInstanceId!!.id,
                 getExitReason(transitionInfo)
             )
-
-            setSessionInactive()
+            isSessionActive = false
         } else if (
             postTransitionVisibleFreeformTasks.isNotEmpty() &&
                 preTransitionVisibleFreeformTasks.isEmpty() &&
                 !isSessionActive
         ) {
             // Session is starting, log enter event followed by task updates
-            loggerInstanceId = idSequence.newInstanceId()
+            isSessionActive = true
             desktopModeEventLogger.logSessionEnter(
-                loggerInstanceId!!.id,
                 getEnterReason(transitionInfo)
             )
 
             identifyAndLogTaskUpdates(
-                loggerInstanceId!!.id,
                 preTransitionVisibleFreeformTasks,
                 postTransitionVisibleFreeformTasks
             )
         } else if (isSessionActive) {
             // Session is neither starting, nor finishing, log task updates if there are any
             identifyAndLogTaskUpdates(
-                loggerInstanceId!!.id,
                 preTransitionVisibleFreeformTasks,
                 postTransitionVisibleFreeformTasks
             )
@@ -290,7 +273,6 @@
 
     /** Compare the old and new state of taskInfos and identify and log the changes */
     private fun identifyAndLogTaskUpdates(
-        sessionId: Int,
         preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
         postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>
     ) {
@@ -301,7 +283,7 @@
             when {
                 // new tasks added
                 previousTaskInfo == null -> {
-                    desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate)
+                    desktopModeEventLogger.logTaskAdded(currentTaskUpdate)
                     Trace.setCounter(
                         Trace.TRACE_TAG_WINDOW_MANAGER,
                         VISIBLE_TASKS_COUNTER_NAME,
@@ -314,14 +296,14 @@
                 // TODO(b/347935387): Log changes only once they are stable.
                 buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size())
                         != currentTaskUpdate ->
-                            desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate)
+                            desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate)
             }
         }
 
         // find old tasks that were removed
         preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
             if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
-                desktopModeEventLogger.logTaskRemoved(sessionId,
+                desktopModeEventLogger.logTaskRemoved(
                     buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size()))
                 Trace.setCounter(
                     Trace.TRACE_TAG_WINDOW_MANAGER,
@@ -416,13 +398,6 @@
         visibleFreeformTaskInfos.set(taskInfo.taskId, taskInfo)
     }
 
-    @VisibleForTesting fun getLoggerSessionId(): Int? = loggerInstanceId?.id
-
-    @VisibleForTesting
-    fun setLoggerSessionId(id: Int) {
-        loggerInstanceId = InstanceId.fakeInstanceId(id)
-    }
-
     private fun TransitionInfo.Change.requireTaskInfo(): RunningTaskInfo {
         return this.taskInfo ?: throw IllegalStateException("Expected TaskInfo in the Change")
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index bd61722..edcc877 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -37,6 +37,23 @@
     SystemProperties.getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25)
 
 /**
+ * Calculates the initial bounds to enter desktop, centered on the display.
+ */
+fun calculateDefaultDesktopTaskBounds(displayLayout: DisplayLayout): Rect {
+    // TODO(b/319819547): Account for app constraints so apps do not become letterboxed
+    val desiredWidth = (displayLayout.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
+    val desiredHeight = (displayLayout.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
+    val heightOffset = (displayLayout.height() - desiredHeight) / 2
+    val widthOffset = (displayLayout.width() - desiredWidth) / 2
+    return Rect(
+        widthOffset,
+        heightOffset,
+        desiredWidth + widthOffset,
+        desiredHeight + heightOffset
+    )
+}
+
+/**
  * Calculates the initial bounds required for an application to fill a scale of the display bounds
  * without any letterboxing. This is done by taking into account the applications fullscreen size,
  * aspect ratio, orientation and resizability to calculate an area this is compatible with the
@@ -123,6 +140,29 @@
 }
 
 /**
+ * Calculates the maximized bounds of a task given in the given [DisplayLayout], taking
+ * resizability into consideration.
+ */
+fun calculateMaximizeBounds(
+    displayLayout: DisplayLayout,
+    taskInfo: RunningTaskInfo,
+): Rect {
+    val stableBounds = Rect()
+    displayLayout.getStableBounds(stableBounds)
+    if (taskInfo.isResizeable) {
+        // if resizable then expand to entire stable bounds (full display minus insets)
+        return Rect(stableBounds)
+    } else {
+        // if non-resizable then calculate max bounds according to aspect ratio
+        val activityAspectRatio = calculateAspectRatio(taskInfo)
+        val newSize = maximizeSizeGivenAspectRatio(taskInfo,
+            Size(stableBounds.width(), stableBounds.height()), activityAspectRatio)
+        return centerInArea(
+            newSize, stableBounds, stableBounds.left, stableBounds.top)
+    }
+}
+
+/**
  * Calculates the largest size that can fit in a given area while maintaining a specific aspect
  * ratio.
  */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 52b92a8..61de077 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -378,11 +378,12 @@
 
         private static VisualIndicatorAnimator fadeBoundsIn(
                 @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) {
-            final Rect startBounds = getIndicatorBounds(displayLayout, type);
+            final Rect endBounds = getIndicatorBounds(displayLayout, type);
+            final Rect startBounds = getMinBounds(endBounds);
             view.getBackground().setBounds(startBounds);
 
             final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
-                    view, startBounds, getMaxBounds(startBounds));
+                    view, startBounds, endBounds);
             animator.setInterpolator(new DecelerateInterpolator());
             setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM);
             return animator;
@@ -390,8 +391,8 @@
 
         private static VisualIndicatorAnimator fadeBoundsOut(
                 @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) {
-            final Rect endBounds = getIndicatorBounds(displayLayout, type);
-            final Rect startBounds = getMaxBounds(endBounds);
+            final Rect startBounds = getIndicatorBounds(displayLayout, type);
+            final Rect endBounds = getMinBounds(startBounds);
             view.getBackground().setBounds(startBounds);
 
             final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
@@ -422,28 +423,35 @@
             return animator;
         }
 
+        /** Calculates the bounds the indicator should have when fully faded in. */
         private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type) {
-            final int padding = layout.stableInsets().top;
+            final Rect desktopStableBounds = new Rect();
+            layout.getStableBounds(desktopStableBounds);
+            final int padding = desktopStableBounds.top;
             switch (type) {
                 case TO_FULLSCREEN_INDICATOR:
-                    return new Rect(padding, padding,
-                            layout.width() - padding,
-                            layout.height() - padding);
+                    desktopStableBounds.top += padding;
+                    desktopStableBounds.bottom -= padding;
+                    desktopStableBounds.left += padding;
+                    desktopStableBounds.right -= padding;
+                    return desktopStableBounds;
                 case TO_DESKTOP_INDICATOR:
                     final float adjustmentPercentage = 1f
                             - DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
-                    return new Rect((int) (adjustmentPercentage * layout.width() / 2),
-                            (int) (adjustmentPercentage * layout.height() / 2),
-                            (int) (layout.width() - (adjustmentPercentage * layout.width() / 2)),
-                            (int) (layout.height() - (adjustmentPercentage * layout.height() / 2)));
+                    return new Rect((int) (adjustmentPercentage * desktopStableBounds.width() / 2),
+                            (int) (adjustmentPercentage * desktopStableBounds.height() / 2),
+                            (int) (desktopStableBounds.width()
+                                    - (adjustmentPercentage * desktopStableBounds.width() / 2)),
+                            (int) (desktopStableBounds.height()
+                                    - (adjustmentPercentage * desktopStableBounds.height() / 2)));
                 case TO_SPLIT_LEFT_INDICATOR:
                     return new Rect(padding, padding,
-                            layout.width() / 2 - padding,
-                            layout.height() - padding);
+                            desktopStableBounds.width() / 2 - padding,
+                            desktopStableBounds.height());
                 case TO_SPLIT_RIGHT_INDICATOR:
-                    return new Rect(layout.width() / 2 + padding, padding,
-                            layout.width() - padding,
-                            layout.height() - padding);
+                    return new Rect(desktopStableBounds.width() / 2 + padding, padding,
+                            desktopStableBounds.width() - padding,
+                            desktopStableBounds.height());
                 default:
                     throw new IllegalArgumentException("Invalid indicator type provided.");
             }
@@ -505,17 +513,18 @@
         }
 
         /**
-         * Return the max bounds of a visual indicator
+         * Return the minimum bounds of a visual indicator, to be used at the end of fading out
+         * and the start of fading in.
          */
-        private static Rect getMaxBounds(Rect startBounds) {
-            return new Rect((int) (startBounds.left
-                    - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
-                    (int) (startBounds.top
-                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())),
-                    (int) (startBounds.right
-                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
-                    (int) (startBounds.bottom
-                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())));
+        private static Rect getMinBounds(Rect maxBounds) {
+            return new Rect((int) (maxBounds.left
+                    + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())),
+                    (int) (maxBounds.top
+                            + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())),
+                    (int) (maxBounds.right
+                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())),
+                    (int) (maxBounds.bottom
+                            - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())));
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index c175133..eeb7ac8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -102,6 +102,9 @@
     /* Tracks last bounds of task before toggled to stable bounds. */
     private val boundsBeforeMaximizeByTaskId = SparseArray<Rect>()
 
+    /* Tracks last bounds of task before toggled to immersive state. */
+    private val boundsBeforeFullImmersiveByTaskId = SparseArray<Rect>()
+
     private var desktopGestureExclusionListener: Consumer<Region>? = null
     private var desktopGestureExclusionExecutor: Executor? = null
 
@@ -328,6 +331,10 @@
         return desktopTaskDataSequence().any { taskId == it.fullImmersiveTaskId }
     }
 
+    /** Returns the task that is currently in immersive mode in this display, or null. */
+    fun getTaskInFullImmersiveState(displayId: Int): Int? =
+        desktopTaskDataByDisplayId.getOrCreate(displayId).fullImmersiveTaskId
+
     private fun notifyVisibleTaskListeners(displayId: Int, visibleTasksCount: Int) {
         visibleTasksListeners.forEach { (listener, executor) ->
             executor.execute { listener.onTasksVisibilityChanged(displayId, visibleTasksCount) }
@@ -410,6 +417,7 @@
         logD("Removes freeform task: taskId=%d, displayId=%d", taskId, displayId)
         desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId)
         boundsBeforeMaximizeByTaskId.remove(taskId)
+        boundsBeforeFullImmersiveByTaskId.remove(taskId)
         logD("Remaining freeform tasks: %s",
             desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString())
         // Remove task from unminimized task if it is minimized.
@@ -468,6 +476,14 @@
     fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) =
         boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
 
+    /** Removes and returns the bounds saved before entering immersive with the given task. */
+    fun removeBoundsBeforeFullImmersive(taskId: Int): Rect? =
+        boundsBeforeFullImmersiveByTaskId.removeReturnOld(taskId)
+
+    /** Saves the bounds of the given task before entering immersive. */
+    fun saveBoundsBeforeFullImmersive(taskId: Int, bounds: Rect) =
+        boundsBeforeFullImmersiveByTaskId.set(taskId, Rect(bounds))
+
     private fun updatePersistentRepository(displayId: Int) {
         // Create a deep copy of the data
         desktopTaskDataByDisplayId[displayId]?.deepCopy()?.let { desktopTaskDataByDisplayIdCopy ->
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 fa8b6e6..29e302a 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
@@ -48,14 +48,14 @@
 import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.DesktopModeFlags
+import android.window.DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
 import android.window.RemoteTransition
 import android.window.TransitionInfo
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
-import android.window.flags.DesktopModeFlags
-import android.window.flags.DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE
-import android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
-import android.window.flags.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
 import androidx.annotation.BinderThread
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
@@ -82,6 +82,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
 import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
+import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
 import com.android.wm.shell.draganddrop.DragAndDropController
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.recents.RecentTasksController
@@ -91,6 +92,7 @@
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.shared.annotations.ExternalThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity
@@ -190,6 +192,7 @@
 
     private var recentsAnimationRunning = false
     private lateinit var splitScreenController: SplitScreenController
+    lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter
     // Launch cookie used to identify a drag and drop transition to fullscreen after it has begun.
     // Used to prevent handleRequest from moving the new fullscreen task to freeform.
     private var dragAndDropFullscreenCookie: Binder? = null
@@ -354,6 +357,8 @@
         // TODO(342378842): Instead of using default display, support multiple displays
         val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
             DEFAULT_DISPLAY, wct, taskId)
+        val runOnTransit = immersiveTransitionHandler
+            .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY)
         wct.startTask(
             taskId,
             ActivityOptions.makeBasic().apply {
@@ -363,6 +368,7 @@
         // TODO(343149901): Add DPI changes for task launch
         val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
         addPendingMinimizeTransition(transition, taskToMinimize)
+        runOnTransit?.invoke(transition)
         return true
     }
 
@@ -379,6 +385,7 @@
         }
         logV("moveRunningTaskToDesktop taskId=%d", task.taskId)
         exitSplitIfApplicable(wct, task)
+        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(wct, task.displayId)
         // Bring other apps to front first
         val taskToMinimize =
             bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
@@ -386,6 +393,7 @@
 
         val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
         addPendingMinimizeTransition(transition, taskToMinimize)
+        runOnTransit?.invoke(transition)
     }
 
     /**
@@ -422,8 +430,13 @@
         val taskToMinimize =
             bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
         addMoveToDesktopChanges(wct, taskInfo)
+        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(
+            wct, taskInfo.displayId)
         val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
-        transition?.let { addPendingMinimizeTransition(it, taskToMinimize) }
+        transition?.let {
+            addPendingMinimizeTransition(it, taskToMinimize)
+            runOnTransit?.invoke(transition)
+        }
     }
 
     /**
@@ -453,20 +466,36 @@
             removeWallpaperActivity(wct)
         }
         taskRepository.addClosingTask(displayId, taskId)
+        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+            doesAnyTaskRequireTaskbarRounding(
+                displayId,
+                taskId
+            )
+        )
     }
 
-    /**
-     * Perform clean up of the desktop wallpaper activity if the minimized window task is the last
-     * active task.
-     *
-     * @param wct transaction to modify if the last active task is minimized
-     * @param taskId task id of the window that's being minimized
-     */
-    fun onDesktopWindowMinimize(wct: WindowContainerTransaction, taskId: Int) {
+    fun minimizeTask(taskInfo: RunningTaskInfo) {
+        val taskId = taskInfo.taskId
+        val displayId = taskInfo.displayId
+        val wct = WindowContainerTransaction()
         if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
+            // Perform clean up of the desktop wallpaper activity if the minimized window task is
+            // the last active task.
             removeWallpaperActivity(wct)
         }
-        // Do not call taskRepository.minimizeTask because it will be called by DekstopTasksLimiter.
+        // Notify immersive handler as it might need to exit immersive state.
+        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(wct, taskInfo)
+
+        wct.reorder(taskInfo.token, false)
+        val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct)
+        desktopTasksLimiter.ifPresent {
+            it.addPendingMinimizeChange(
+                transition = transition,
+                displayId = displayId,
+                taskId = taskId
+            )
+        }
+        runOnTransit?.invoke(transition)
     }
 
     /** Move a task with given `taskId` to fullscreen */
@@ -493,7 +522,6 @@
         wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
 
         transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
-
     }
 
     private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
@@ -535,10 +563,20 @@
             )
     }
 
-    /** Move a task to the front */
-    fun moveTaskToFront(taskId: Int) {
+    /**
+     * Move a task to the front, using [remoteTransition].
+     *
+     * Note: beyond moving a task to the front, this method will minimize a task if we reach the
+     * Desktop task limit, so [remoteTransition] should also handle any such minimize change.
+     */
+    @JvmOverloads
+    fun moveTaskToFront(taskId: Int, remoteTransition: RemoteTransition? = null) {
         val task = shellTaskOrganizer.getRunningTaskInfo(taskId)
-        if (task == null) moveBackgroundTaskToFront(taskId) else moveTaskToFront(task)
+        if (task == null) {
+            moveBackgroundTaskToFront(taskId, remoteTransition)
+        } else {
+            moveTaskToFront(task, remoteTransition)
+        }
     }
 
     /**
@@ -546,32 +584,66 @@
      * desktop. If outside of desktop and want to launch a background task in desktop, use
      * [moveBackgroundTaskToDesktop] instead.
      */
-    private fun moveBackgroundTaskToFront(taskId: Int) {
+    private fun moveBackgroundTaskToFront(taskId: Int, remoteTransition: RemoteTransition?) {
         logV("moveBackgroundTaskToFront taskId=%s", taskId)
         val wct = WindowContainerTransaction()
         // TODO: b/342378842 - Instead of using default display, support multiple displays
-        val taskToMinimize: RunningTaskInfo? =
-            addAndGetMinimizeChangesIfNeeded(DEFAULT_DISPLAY, wct, taskId)
+        val runOnTransit = immersiveTransitionHandler
+            .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY)
         wct.startTask(
             taskId,
             ActivityOptions.makeBasic().apply {
                 launchWindowingMode = WINDOWING_MODE_FREEFORM
             }.toBundle(),
         )
-        val transition = transitions.startTransition(TRANSIT_OPEN, wct, null /* handler */)
-        addPendingMinimizeTransition(transition, taskToMinimize)
+        val transition = startLaunchTransition(TRANSIT_OPEN, wct, taskId, remoteTransition)
+        runOnTransit?.invoke(transition)
     }
 
-    /** Move a task to the front */
-    fun moveTaskToFront(taskInfo: RunningTaskInfo) {
+    /**
+     * Move a task to the front, using [remoteTransition].
+     *
+     * Note: beyond moving a task to the front, this method will minimize a task if we reach the
+     * Desktop task limit, so [remoteTransition] should also handle any such minimize change.
+     */
+    @JvmOverloads
+    fun moveTaskToFront(taskInfo: RunningTaskInfo, remoteTransition: RemoteTransition? = null) {
         logV("moveTaskToFront taskId=%s", taskInfo.taskId)
         val wct = WindowContainerTransaction()
         wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */)
-        val taskToMinimize =
-            addAndGetMinimizeChangesIfNeeded(taskInfo.displayId, wct, taskInfo.taskId)
+        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(
+            wct, taskInfo.displayId)
+        val transition =
+            startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition)
+        runOnTransit?.invoke(transition)
+    }
 
-        val transition = transitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */)
-        addPendingMinimizeTransition(transition, taskToMinimize)
+    private fun startLaunchTransition(
+        transitionType: Int,
+        wct: WindowContainerTransaction,
+        taskId: Int,
+        remoteTransition: RemoteTransition?,
+    ): IBinder {
+        val taskToMinimize: RunningTaskInfo? =
+            addAndGetMinimizeChangesIfNeeded(DEFAULT_DISPLAY, wct, taskId)
+        if (remoteTransition == null) {
+            val t = transitions.startTransition(transitionType, wct, null /* handler */)
+            addPendingMinimizeTransition(t, taskToMinimize)
+            return t
+        }
+        if (taskToMinimize == null) {
+            val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
+            val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
+            remoteTransitionHandler.setTransition(t)
+            return t
+        }
+        val remoteTransitionHandler =
+            DesktopWindowLimitRemoteHandler(
+                mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskToMinimize.taskId)
+        val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
+        remoteTransitionHandler.setTransition(t)
+        addPendingMinimizeTransition(t, taskToMinimize)
+        return t
     }
 
     /**
@@ -626,10 +698,10 @@
         }
 
         val wct = WindowContainerTransaction()
+        if (!task.isFreeform) addMoveToDesktopChanges(wct, task, displayId)
         wct.reparent(task.token, displayAreaInfo.token, true /* onTop */)
 
         transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
-
     }
 
     /** Moves a task in/out of full immersive state within the desktop. */
@@ -643,22 +715,12 @@
 
     private fun moveDesktopTaskToFullImmersive(taskInfo: RunningTaskInfo) {
         check(taskInfo.isFreeform) { "Task must already be in freeform" }
-        val wct = WindowContainerTransaction().apply {
-            setBounds(taskInfo.token, Rect())
-        }
-        immersiveTransitionHandler.enterImmersive(taskInfo, wct)
+        immersiveTransitionHandler.moveTaskToImmersive(taskInfo)
     }
 
     private fun exitDesktopTaskFromFullImmersive(taskInfo: RunningTaskInfo) {
         check(taskInfo.isFreeform) { "Task must already be in freeform" }
-        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
-        val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
-        val destinationBounds = getMaximizeBounds(taskInfo, stableBounds)
-
-        val wct = WindowContainerTransaction().apply {
-            setBounds(taskInfo.token, destinationBounds)
-        }
-        immersiveTransitionHandler.exitImmersive(taskInfo, wct)
+        immersiveTransitionHandler.moveTaskToNonImmersive(taskInfo)
     }
 
     /**
@@ -689,7 +751,7 @@
                 if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
                     destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo))
                 } else {
-                    destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
+                    destinationBounds.set(calculateDefaultDesktopTaskBounds(displayLayout))
                 }
             }
         } else {
@@ -697,7 +759,7 @@
             // and toggle to the stable bounds.
             taskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, currentTaskBounds)
 
-            destinationBounds.set(getMaximizeBounds(taskInfo, stableBounds))
+            destinationBounds.set(calculateMaximizeBounds(displayLayout, taskInfo))
         }
 
 
@@ -715,7 +777,6 @@
         val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
 
         toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
-
     }
 
     private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect {
@@ -827,7 +888,6 @@
         val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
 
         toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds)
-
     }
 
     @VisibleForTesting
@@ -860,20 +920,6 @@
         }
     }
 
-    private fun getDefaultDesktopTaskBounds(displayLayout: DisplayLayout): Rect {
-        // TODO(b/319819547): Account for app constraints so apps do not become letterboxed
-        val desiredWidth = (displayLayout.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
-        val desiredHeight = (displayLayout.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
-        val heightOffset = (displayLayout.height() - desiredHeight) / 2
-        val widthOffset = (displayLayout.width() - desiredWidth) / 2
-        return Rect(
-            widthOffset,
-            heightOffset,
-            desiredWidth + widthOffset,
-            desiredHeight + heightOffset
-        )
-    }
-
     private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
         val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect()
 
@@ -1164,7 +1210,13 @@
         val options = createNewWindowOptions(callingTask)
         if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
             wct.startTask(requestedTaskId, options.toBundle())
-            transitions.startTransition(TRANSIT_OPEN, wct, null)
+            val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+                callingTask.displayId, wct, requestedTaskId)
+            val runOnTransit = immersiveTransitionHandler
+                .exitImmersiveIfApplicable(wct, callingTask.displayId)
+            val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
+            addPendingMinimizeTransition(transition, taskToMinimize)
+            runOnTransit?.invoke(transition)
         } else {
             val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
             splitScreenController.startTask(requestedTaskId, splitPosition,
@@ -1198,7 +1250,8 @@
                     .determineNewInstancePosition(callingTaskInfo)
                 splitScreenController.startIntent(
                     launchIntent, context.userId, fillIn, splitPosition,
-                    options.toBundle(), null /* hideTaskToken */
+                    options.toBundle(), null /* hideTaskToken */,
+                    true /* forceLaunchNewTask */
                 )
             }
             WINDOWING_MODE_FREEFORM -> {
@@ -1222,10 +1275,23 @@
                 error("Invalid windowing mode: ${callingTask.windowingMode}")
             }
         }
+        val bounds = when (newTaskWindowingMode) {
+            WINDOWING_MODE_FREEFORM -> {
+                displayController.getDisplayLayout(callingTask.displayId)
+                    ?.let { getInitialBounds(it, callingTask, callingTask.displayId) }
+            }
+            WINDOWING_MODE_MULTI_WINDOW -> {
+                Rect()
+            }
+            else -> {
+                error("Invalid windowing mode: $newTaskWindowingMode")
+            }
+        }
         return ActivityOptions.makeBasic().apply {
             launchWindowingMode = newTaskWindowingMode
             pendingIntentBackgroundActivityStartMode =
                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+            launchBounds = bounds
         }
     }
 
@@ -1278,15 +1344,17 @@
             val displayLayout = displayController.getDisplayLayout(task.displayId)
             if (displayLayout != null) {
                 val initialBounds = Rect(task.configuration.windowConfiguration.bounds)
-                cascadeWindow(task, initialBounds, displayLayout)
+                cascadeWindow(initialBounds, displayLayout, task.displayId)
                 wct.setBounds(task.token, initialBounds)
             }
         }
         if (useDesktopOverrideDensity()) {
             wct.setDensityDpi(task.token, DESKTOP_DENSITY_OVERRIDE)
         }
-        // Desktop Mode is showing and we're launching a new Task - we might need to minimize
-        // a Task.
+        // Desktop Mode is showing and we're launching a new Task:
+        // 1) Exit immersive if needed.
+        immersiveTransitionHandler.exitImmersiveIfApplicable(transition, wct, task.displayId)
+        // 2) minimize a Task if needed.
         val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task.taskId)
         if (taskToMinimize != null) {
             addPendingMinimizeTransition(transition, taskToMinimize)
@@ -1316,6 +1384,9 @@
                 val taskToMinimize =
                     addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task.taskId)
                 addPendingMinimizeTransition(transition, taskToMinimize)
+                immersiveTransitionHandler.exitImmersiveIfApplicable(
+                    transition, wct, task.displayId
+                )
             }
         }
         return null
@@ -1361,13 +1432,19 @@
         return if (wct.isEmpty) null else wct
     }
 
+    /**
+     * Apply all changes required when task is first added to desktop. Uses the task's current
+     * display by default to apply initial bounds and placement relative to the display.
+     * Use a different [displayId] if the task should be moved to a different display.
+     */
     @VisibleForTesting
     fun addMoveToDesktopChanges(
         wct: WindowContainerTransaction,
-        taskInfo: RunningTaskInfo
+        taskInfo: RunningTaskInfo,
+        displayId: Int = taskInfo.displayId,
     ) {
-        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
-        val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
+        val displayLayout = displayController.getDisplayLayout(displayId) ?: return
+        val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)!!
         val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
         val targetWindowingMode =
             if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
@@ -1376,15 +1453,7 @@
             } else {
                 WINDOWING_MODE_FREEFORM
             }
-        val initialBounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
-            calculateInitialBounds(displayLayout, taskInfo)
-        } else {
-            getDefaultDesktopTaskBounds(displayLayout)
-        }
-
-        if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue()) {
-            cascadeWindow(taskInfo, initialBounds, displayLayout)
-        }
+        val initialBounds = getInitialBounds(displayLayout, taskInfo, displayId)
 
         if (canChangeTaskPosition(taskInfo)) {
             wct.setBounds(taskInfo.token, initialBounds)
@@ -1396,6 +1465,23 @@
         }
     }
 
+    private fun getInitialBounds(
+        displayLayout: DisplayLayout,
+        taskInfo: RunningTaskInfo,
+        displayId: Int,
+    ): Rect {
+        val bounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) {
+            calculateInitialBounds(displayLayout, taskInfo)
+        } else {
+            calculateDefaultDesktopTaskBounds(displayLayout)
+        }
+
+        if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) {
+            cascadeWindow(bounds, displayLayout, displayId)
+        }
+        return bounds
+    }
+
     private fun addMoveToFullscreenChanges(
         wct: WindowContainerTransaction,
         taskInfo: RunningTaskInfo
@@ -1420,11 +1506,11 @@
         }
     }
 
-    private fun cascadeWindow(task: TaskInfo, bounds: Rect, displayLayout: DisplayLayout) {
+    private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
         val stableBounds = Rect()
         displayLayout.getStableBoundsForDesktopMode(stableBounds)
 
-        val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(task.displayId)
+        val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
         activeTasks.firstOrNull()?.let { activeTask ->
             shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
                 cascadeWindow(context.resources, stableBounds,
@@ -1783,7 +1869,7 @@
         when (indicatorType) {
             IndicatorType.TO_DESKTOP_INDICATOR -> {
                 // Use default bounds, but with the top-center at the drop point.
-                newWindowBounds.set(getDefaultDesktopTaskBounds(displayLayout))
+                newWindowBounds.set(calculateDefaultDesktopTaskBounds(displayLayout))
                 newWindowBounds.offsetTo(
                     dragEvent.x.toInt() - (newWindowBounds.width() / 2),
                     dragEvent.y.toInt()
@@ -1970,9 +2056,9 @@
             }
         }
 
-        override fun showDesktopApp(taskId: Int) {
+        override fun showDesktopApp(taskId: Int, remoteTransition: RemoteTransition?) {
             executeRemoteCallWithTaskPermission(controller, "showDesktopApp") { c ->
-                c.moveTaskToFront(taskId)
+                c.moveTaskToFront(taskId, remoteTransition)
             }
         }
 
@@ -2023,6 +2109,12 @@
                 c.removeDesktop(displayId)
             }
         }
+
+        override fun moveToExternalDisplay(taskId: Int) {
+            executeRemoteCallWithTaskPermission(controller, "moveTaskToExternalDisplay") { c ->
+                c.moveToNextDisplay(taskId)
+            }
+        }
     }
 
     private fun logV(msg: String, vararg arguments: Any?) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 37bec21..d6b7212 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -24,7 +24,7 @@
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
-import android.window.flags.DesktopModeFlags
+import android.window.DesktopModeFlags
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
 import com.android.internal.jank.InteractionJankMonitor
@@ -39,7 +39,7 @@
  * Limits the number of tasks shown in Desktop Mode.
  *
  * This class should only be used if
- * [android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT]
+ * [android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT]
  * is enabled and [maxTasksLimit] is strictly greater than 0.
  */
 class DesktopTasksLimiter (
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 64ae35b..d1534da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -21,13 +21,15 @@
 import android.os.IBinder
 import android.view.SurfaceControl
 import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CLOSE
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
-import android.window.flags.DesktopModeFlags
-import android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+import android.window.DesktopModeFlags
+import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -36,8 +38,8 @@
 
 /**
  * A [Transitions.TransitionObserver] that observes shell transitions and updates the
- * [DesktopRepository] state TODO: b/332682201 This observes transitions related to desktop
- * mode and other transitions that originate both within and outside shell.
+ * [DesktopRepository] state TODO: b/332682201 This observes transitions related to desktop mode and
+ * other transitions that originate both within and outside shell.
  */
 class DesktopTasksTransitionObserver(
     private val context: Context,
@@ -47,6 +49,8 @@
     shellInit: ShellInit
 ) : Transitions.TransitionObserver {
 
+    private var transitionToCloseWallpaper: IBinder? = null
+
     init {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             shellInit.addInitCallback(::onInit, this)
@@ -70,24 +74,23 @@
             handleBackNavigation(info)
             removeTaskIfNeeded(info)
         }
+        removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
     }
 
     private fun removeTaskIfNeeded(info: TransitionInfo) {
         // Since we are no longer removing all the tasks [onTaskVanished], we need to remove them by
         // checking the transitions.
-        if (!TransitionUtil.isOpeningType(info.type)) return
+        if (!(TransitionUtil.isOpeningType(info.type) || info.type.isExitDesktopModeTransition())) {
+            return
+        }
         // Remove a task from the repository if the app is launched outside of desktop.
         for (change in info.changes) {
             val taskInfo = change.taskInfo
             if (taskInfo == null || taskInfo.taskId == -1) continue
 
-            if (desktopRepository.isActiveTask(taskInfo.taskId)
-                && taskInfo.windowingMode != WINDOWING_MODE_FREEFORM
-            ) {
-                desktopRepository.removeFreeformTask(
-                    taskInfo.displayId,
-                    taskInfo.taskId
-                )
+            if (desktopRepository.isActiveTask(taskInfo.taskId) &&
+                taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) {
+                desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
             }
         }
     }
@@ -104,14 +107,32 @@
 
                 if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) > 0 &&
                     change.mode == TRANSIT_TO_BACK &&
-                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
-                ) {
+                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
                     desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
                 }
             }
         }
     }
 
+    private fun removeWallpaperOnLastTaskClosingIfNeeded(
+        transition: IBinder,
+        info: TransitionInfo
+    ) {
+        for (change in info.changes) {
+            val taskInfo = change.taskInfo
+            if (taskInfo == null || taskInfo.taskId == -1) {
+                continue
+            }
+
+            if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 1 &&
+                change.mode == TRANSIT_CLOSE &&
+                taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
+                desktopRepository.wallpaperActivityToken != null) {
+                transitionToCloseWallpaper = transition
+            }
+        }
+    }
+
     override fun onTransitionStarting(transition: IBinder) {
         // TODO: b/332682201 Update repository state
     }
@@ -122,6 +143,16 @@
 
     override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
         // TODO: b/332682201 Update repository state
+        if (transitionToCloseWallpaper == transition) {
+            // TODO: b/362469671 - Handle merging the animation when desktop is also closing.
+            desktopRepository.wallpaperActivityToken?.let { wallpaperActivityToken ->
+                transitions.startTransition(
+                    TRANSIT_CLOSE,
+                    WindowContainerTransaction().removeTask(wallpaperActivityToken),
+                    null)
+            }
+            transitionToCloseWallpaper = null
+        }
     }
 
     private fun updateWallpaperToken(info: TransitionInfo) {
@@ -139,10 +170,9 @@
                             // task.
                             shellTaskOrganizer.applyTransaction(
                                 WindowContainerTransaction()
-                                    .setTaskTrimmableFromRecents(taskInfo.token, false)
-                            )
+                                    .setTaskTrimmableFromRecents(taskInfo.token, false))
                         }
-                        WindowManager.TRANSIT_CLOSE ->
+                        TRANSIT_CLOSE ->
                             desktopRepository.wallpaperActivityToken = null
                         else -> {}
                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 8e264b2..34c2f1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -648,7 +648,13 @@
             state.startAborted = true
             // The start-transition (DRAG_HOLD) is aborted, cancel its jank interaction.
             interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
-        } else if (state.cancelTransitionToken != transition) {
+        } else if (state.cancelTransitionToken == transition) {
+            state.draggedTaskChange?.leash?.let {
+                state.startTransitionFinishTransaction?.show(it)
+            }
+            state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+            clearState()
+        } else {
             // This transition being aborted is neither the start, nor the cancel transition, so
             // it must be the finish transition (DRAG_RELEASE); cancel its jank interaction.
             interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
@@ -863,7 +869,8 @@
 
     companion object {
         /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */
-        internal const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
     }
 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 1090a46..aac2361 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -35,8 +35,13 @@
     /** @deprecated this is no longer supported. */
     void hideStashedDesktopApps(int displayId);
 
-    /** Bring task with the given id to front */
-    oneway void showDesktopApp(int taskId);
+    /**
+    * Bring task with the given id to front, using the given remote transition.
+    *
+    * <p> Note: beyond moving a task to the front, this method will minimize a task if we reach the
+    * Desktop task limit, so {@code remoteTransition} should also handle any such minimize change.
+    */
+    oneway void showDesktopApp(int taskId, in @nullable RemoteTransition remoteTransition);
 
     /** Get count of visible desktop tasks on the given display */
     int getVisibleTaskCount(int displayId);
@@ -51,5 +56,8 @@
     void moveToDesktop(int taskId, in DesktopModeTransitionSource transitionSource);
 
     /** Remove desktop on the given display */
-    void removeDesktop(int displayId);
+    oneway void removeDesktop(int displayId);
+
+    /** Move a task with given `taskId` to external display */
+    void moveToExternalDisplay(int taskId);
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt
new file mode 100644
index 0000000..7554cbb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.minimize
+
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.RemoteTransition
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.transition.OneShotRemoteHandler
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionHandler
+
+/**
+ * Handles transitions that result in hitting the Desktop window limit, by performing some
+ * preparation work and then delegating to [remoteTransition].
+ *
+ * This transition handler prepares the leash of the minimizing change referenced by
+ * [taskIdToMinimize], and then delegates to [remoteTransition] to perform the actual transition.
+ */
+class DesktopWindowLimitRemoteHandler(
+    mainExecutor: ShellExecutor,
+    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+    remoteTransition: RemoteTransition,
+    private val taskIdToMinimize: Int,
+    ) : TransitionHandler {
+
+    private val oneShotRemoteHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
+    private var transition: IBinder? = null
+
+    /** Sets the transition that will be handled - this must be called before [startAnimation]. */
+    fun setTransition(transition: IBinder) {
+        this.transition = transition
+        oneShotRemoteHandler.setTransition(transition)
+    }
+
+    override fun handleRequest(
+        transition: IBinder,
+        request: TransitionRequestInfo
+    ): WindowContainerTransaction? {
+        this.transition = transition
+        return oneShotRemoteHandler.handleRequest(transition, request)
+    }
+
+    override fun startAnimation(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction,
+        finishCallback: Transitions.TransitionFinishCallback
+    ): Boolean {
+        if (transition != this.transition) return false
+        val minimizeChange = findMinimizeChange(info, taskIdToMinimize) ?: return false
+        // Reparent the minimize change back to the root task display area, so the minimizing window
+        // isn't shown in front of other windows. We do this here in Shell since Launcher doesn't
+        // have access to RootTaskDisplayAreaOrganizer.
+        applyMinimizeChangeReparenting(info, minimizeChange, startTransaction)
+        return oneShotRemoteHandler.startAnimation(
+            transition, info, startTransaction, finishTransaction, finishCallback)
+    }
+
+    private fun applyMinimizeChangeReparenting(
+        info: TransitionInfo,
+        minimizeChange: Change,
+        startTransaction: SurfaceControl.Transaction,
+    ) {
+        val taskInfo = minimizeChange.taskInfo ?: return
+        if (taskInfo.isFreeform && TransitionUtil.isOpeningMode(info.type)) {
+            rootTaskDisplayAreaOrganizer.reparentToDisplayArea(
+                taskInfo.displayId, minimizeChange.leash, startTransaction)
+        }
+    }
+
+    private fun findMinimizeChange(
+        info: TransitionInfo,
+        taskIdToMinimize: Int,
+    ): Change? =
+        info.changes.find { change ->
+            change.taskInfo?.taskId == taskIdToMinimize && change.mode == TRANSIT_TO_BACK }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index dfa2437..5c72cb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -23,6 +23,7 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_BOTTOM;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_RIGHT;
@@ -43,14 +44,18 @@
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
+import android.util.Log;
 import android.view.DragEvent;
 import android.view.SurfaceControl;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.window.WindowContainerToken;
 
@@ -67,14 +72,22 @@
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiConsumer;
 
 /**
  * Coordinates the visible drop targets for the current drag within a single display.
  */
 public class DragLayout extends LinearLayout
-        implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider {
+        implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider,
+        DragZoneAnimator{
 
+    static final boolean DEBUG_LAYOUT = false;
     // While dragging the status bar is hidden.
     private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS
             | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
@@ -108,13 +121,19 @@
     // The last position that was handled by the drag layout
     private final Point mLastPosition = new Point();
 
+    // Used with enableFlexibleSplit() flag
+    private List<SplitDragPolicy.Target> mTargets;
+    private Map<SplitDragPolicy.Target, DropZoneView> mTargetDropMap = new HashMap<>();
+    private FrameLayout mAnimatingRootLayout;
+    // Used with enableFlexibleSplit() flag
+
     @SuppressLint("WrongConstant")
     public DragLayout(Context context, SplitScreenController splitScreenController,
             IconProvider iconProvider) {
         super(context);
         mSplitScreenController = splitScreenController;
         mIconProvider = iconProvider;
-        mPolicy = new SplitDragPolicy(context, splitScreenController);
+        mPolicy = new SplitDragPolicy(context, splitScreenController, this);
         mStatusBarManager = context.getSystemService(StatusBarManager.class);
         mLastConfiguration.setTo(context.getResources().getConfiguration());
 
@@ -211,11 +230,26 @@
         boolean isLeftRightSplit = mSplitScreenController != null
                 && mSplitScreenController.isLeftRightSplit();
         if (isLeftRightSplit) {
-            mDropZoneView1.setBottomInset(mInsets.bottom);
-            mDropZoneView2.setBottomInset(mInsets.bottom);
+            if (enableFlexibleSplit()) {
+                mTargetDropMap.values().forEach(dzv -> dzv.setBottomInset(mInsets.bottom));
+            } else {
+                mDropZoneView1.setBottomInset(mInsets.bottom);
+                mDropZoneView2.setBottomInset(mInsets.bottom);
+            }
         } else {
-            mDropZoneView1.setBottomInset(0);
-            mDropZoneView2.setBottomInset(mInsets.bottom);
+            if (enableFlexibleSplit()) {
+                Collection<DropZoneView> dropViews = mTargetDropMap.values();
+                final DropZoneView[] bottomView = {null};
+                dropViews.forEach(dropZoneView -> {
+                    bottomView[0] = dropZoneView;
+                    dropZoneView.setBottomInset(0);
+                });
+                // TODO(b/349828130): necessary? maybe with UI polish
+                //  bottomView[0].setBottomInset(mInsets.bottom);
+            } else {
+                mDropZoneView1.setBottomInset(0);
+                mDropZoneView2.setBottomInset(mInsets.bottom);
+            }
         }
         return super.onApplyWindowInsets(insets);
     }
@@ -233,17 +267,31 @@
         final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0
                 || (diff & CONFIG_UI_MODE) != 0;
         if (themeChanged) {
-            mDropZoneView1.onThemeChange();
-            mDropZoneView2.onThemeChange();
+            if (enableFlexibleSplit()) {
+                mTargetDropMap.values().forEach(DropZoneView::onThemeChange);
+            } else {
+                mDropZoneView1.onThemeChange();
+                mDropZoneView2.onThemeChange();
+            }
         }
         mLastConfiguration.setTo(newConfig);
         requestLayout();
     }
 
     private void updateContainerMarginsForSingleTask() {
-        mDropZoneView1.setContainerMargin(
-                mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
-        mDropZoneView2.setContainerMargin(0, 0, 0, 0);
+        if (enableFlexibleSplit()) {
+            DropZoneView firstDropZone = mTargetDropMap.values().stream().findFirst().get();
+            mTargetDropMap.values().stream()
+                    .filter(dropZoneView -> dropZoneView != firstDropZone)
+                    .forEach(dropZoneView -> dropZoneView.setContainerMargin(0, 0, 0, 0));
+            firstDropZone.setContainerMargin(
+                    mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin
+            );
+        } else {
+            mDropZoneView1.setContainerMargin(
+                    mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin);
+            mDropZoneView2.setContainerMargin(0, 0, 0, 0);
+        }
     }
 
     private void updateContainerMargins(boolean isLeftRightSplit) {
@@ -306,19 +354,35 @@
                 }
             }
         } else {
-            // We're already in split so get taskInfo from the controller to populate icon / color.
-            ActivityManager.RunningTaskInfo topOrLeftTask =
-                    mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
-            ActivityManager.RunningTaskInfo bottomOrRightTask =
-                    mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
-            if (topOrLeftTask != null && bottomOrRightTask != null) {
-                Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo);
-                int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask);
-                Drawable bottomOrRightIcon = mIconProvider.getIcon(
-                        bottomOrRightTask.topActivityInfo);
-                int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask);
-                mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon);
-                mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon);
+            ActivityManager.RunningTaskInfo[] taskInfos = mSplitScreenController.getAllTaskInfos();
+            boolean anyTasksNull = Arrays.stream(taskInfos).anyMatch(Objects::isNull);
+            if (enableFlexibleSplit() && taskInfos != null && !anyTasksNull) {
+                int i = 0;
+                for (DropZoneView v : mTargetDropMap.values()) {
+                    if (i >= taskInfos.length) {
+                        // TODO(b/349828130) Support once we add 3 StageRoots
+                        continue;
+                    }
+                    ActivityManager.RunningTaskInfo task = taskInfos[i];
+                    v.setAppInfo(getResizingBackgroundColor(task),
+                            mIconProvider.getIcon(task.topActivityInfo));
+                    i++;
+                }
+            } else {
+                // We're already in split so get taskInfo from the controller to populate icon / color.
+                ActivityManager.RunningTaskInfo topOrLeftTask =
+                        mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+                ActivityManager.RunningTaskInfo bottomOrRightTask =
+                        mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+                if (topOrLeftTask != null && bottomOrRightTask != null) {
+                    Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo);
+                    int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask);
+                    Drawable bottomOrRightIcon = mIconProvider.getIcon(
+                            bottomOrRightTask.topActivityInfo);
+                    int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask);
+                    mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon);
+                    mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon);
+                }
             }
 
             // Update the dropzones to match existing split sizes
@@ -391,7 +455,14 @@
     @NonNull
     @Override
     public void addDraggingView(ViewGroup rootView) {
-        // TODO(b/349828130) We need to separate out view + logic here
+        if (enableFlexibleSplit()) {
+            removeAllViews();
+            mAnimatingRootLayout = new FrameLayout(getContext());
+            addView(mAnimatingRootLayout,
+                    new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+            ((LayoutParams) mAnimatingRootLayout.getLayoutParams()).weight = 1;
+        }
+
         rootView.addView(this, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
     }
 
@@ -409,6 +480,24 @@
             // Inset the draw region by a little bit
             target.drawRegion.inset(mDisplayMargin, mDisplayMargin);
         }
+
+        if (enableFlexibleSplit()) {
+            mTargets = targets;
+            mTargetDropMap.clear();
+            for (int i = 0; i < mTargets.size(); i++) {
+                DropZoneView v = new DropZoneView(getContext());
+                SplitDragPolicy.Target t = mTargets.get(i);
+                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(t.drawRegion.width(),
+                        t.drawRegion.height());
+                mAnimatingRootLayout.addView(v, params);
+                v.setTranslationX(t.drawRegion.left);
+                v.setTranslationY(t.drawRegion.top);
+                mTargetDropMap.put(t, v);
+                if (DEBUG_LAYOUT) {
+                    v.setDebugIndex(t.index);
+                }
+            }
+        }
     }
 
     /**
@@ -433,6 +522,9 @@
             if (target == null) {
                 // Animating to no target
                 animateSplitContainers(false, null /* animCompleteCallback */);
+                if (enableFlexibleSplit()) {
+                    animateHighlight(target);
+                }
             } else if (mCurrentTarget == null) {
                 if (mPolicy.getNumTargets() == 1) {
                     animateFullscreenContainer(true);
@@ -440,10 +532,14 @@
                     animateSplitContainers(true, null /* animCompleteCallback */);
                     animateHighlight(target);
                 }
-            } else if (mCurrentTarget.type != target.type) {
+            } else if (mCurrentTarget.type != target.type || enableFlexibleSplit()) {
                 // Switching between targets
-                mDropZoneView1.animateSwitch();
-                mDropZoneView2.animateSwitch();
+                if (enableFlexibleSplit()) {
+                    animateHighlight(target);
+                } else {
+                    mDropZoneView1.animateSwitch();
+                    mDropZoneView2.animateSwitch();
+                }
                 // Announce for accessibility.
                 switch (target.type) {
                     case TYPE_SPLIT_LEFT:
@@ -490,6 +586,9 @@
         mDropZoneView2.setForceIgnoreBottomMargin(false);
         updateContainerMargins(mIsLeftRightSplit);
         mCurrentTarget = null;
+        if (enableFlexibleSplit()) {
+            mAnimatingRootLayout.removeAllViews();
+        }
     }
 
     /**
@@ -566,9 +665,20 @@
         mStatusBarManager.disable(visible
                 ? HIDE_STATUS_BAR_FLAGS
                 : DISABLE_NONE);
-        mDropZoneView1.setShowingMargin(visible);
-        mDropZoneView2.setShowingMargin(visible);
-        Animator animator = mDropZoneView1.getAnimator();
+        Animator animator;
+        if (enableFlexibleSplit()) {
+            DropZoneView anyDropZoneView = null;
+            for (DropZoneView dz : mTargetDropMap.values()) {
+                dz.setShowingMargin(visible);
+                anyDropZoneView = dz;
+            }
+            animator = anyDropZoneView != null ? anyDropZoneView.getAnimator() : null;
+        } else {
+            mDropZoneView1.setShowingMargin(visible);
+            mDropZoneView2.setShowingMargin(visible);
+            animator = mDropZoneView1.getAnimator();
+        }
+
         if (animCompleteCallback != null) {
             if (animator != null) {
                 animator.addListener(new AnimatorListenerAdapter() {
@@ -584,7 +694,24 @@
         }
     }
 
+    @Override
+    public void animateDragTargets(
+            @NonNull List<? extends BiConsumer<SplitDragPolicy.Target, View>> viewsToAnimate) {
+        for (Map.Entry<SplitDragPolicy.Target, DropZoneView> entry : mTargetDropMap.entrySet()) {
+            viewsToAnimate.get(0).accept(entry.getKey(), entry.getValue());
+        }
+    }
+
     private void animateHighlight(SplitDragPolicy.Target target) {
+        if (enableFlexibleSplit()) {
+            for (Map.Entry<SplitDragPolicy.Target, DropZoneView> dzv : mTargetDropMap.entrySet()) {
+                // Highlight the view w/ the matching target, unhighlight the rest
+                dzv.getValue().setShowingHighlight(dzv.getKey() == target);
+            }
+            mPolicy.onHoveringOver(target);
+            return;
+        }
+
         if (target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP) {
             mDropZoneView1.setShowingHighlight(true);
             mDropZoneView2.setShowingHighlight(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt
new file mode 100644
index 0000000..240465d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.draganddrop
+
+import android.view.View
+import java.util.function.BiConsumer
+
+interface DragZoneAnimator {
+    /**
+     * Each consumer will be called for the corresponding DropZoneView.
+     * This must match the number of targets in [.mTargets] otherwise will
+     * throw an [IllegalStateException]
+     */
+    fun animateDragTargets(viewsToAnimate: List<BiConsumer<SplitDragPolicy.Target, View>>)
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt
index 122a105..2bbca48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt
@@ -47,7 +47,7 @@
     /**
      * Called when user is hovering Drag object over the given Target
      */
-    fun onHoveringOver(target: SplitDragPolicy.Target) {}
+    fun onHoveringOver(target: SplitDragPolicy.Target?) {}
     /**
      * Called when the user has dropped the provided target (need not be the same target as
      * [onHoveringOver])
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index f9749ec..e503b8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -20,12 +20,14 @@
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Path;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.Gravity;
@@ -33,6 +35,7 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 
@@ -83,6 +86,7 @@
     private int mTargetBackgroundColor;
     private ObjectAnimator mMarginAnimator;
     private float mMarginPercent;
+    private TextView mDebugIndex;
 
     // Renders a highlight or neutral transparent color
     private ColorDrawable mColorDrawable;
@@ -125,6 +129,22 @@
         mMarginView = new MarginView(context);
         addView(mMarginView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT));
+
+        if (DEBUG_LAYOUT) {
+            mDebugIndex = new TextView(context);
+            mDebugIndex.setVisibility(GONE);
+            mDebugIndex.setTextColor(Color.YELLOW);
+            addView(mDebugIndex, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP));
+
+            View borderView = new View(context);
+            addView(borderView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT));
+            GradientDrawable border = new GradientDrawable();
+            border.setShape(GradientDrawable.RECTANGLE);
+            border.setStroke(5, Color.RED);
+            borderView.setBackground(border);
+        }
     }
 
     public void onThemeChange() {
@@ -236,6 +256,16 @@
         }
     }
 
+    @SuppressLint("SetTextI18n")
+    public void setDebugIndex(int index) {
+        if (!DEBUG_LAYOUT) {
+            return;
+        }
+
+        mDebugIndex.setText("Index:\n" + index);
+        mDebugIndex.setVisibility(VISIBLE);
+    }
+
     private void animateBackground(int startColor, int endColor) {
         if (DEBUG_LAYOUT) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java
index 2a19d65..5d22c1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java
@@ -32,16 +32,22 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
+import static com.android.wm.shell.draganddrop.DragLayout.DEBUG_LAYOUT;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_FULLSCREEN;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_BOTTOM;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_RIGHT;
 import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_TOP;
 import static com.android.wm.shell.shared.draganddrop.DragAndDropConstants.EXTRA_DISALLOW_HIT_REGION;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
@@ -59,6 +65,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
+import android.view.View;
 import android.window.WindowContainerToken;
 
 import androidx.annotation.IntDef;
@@ -69,13 +76,23 @@
 import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
+import com.android.wm.shell.draganddrop.anim.DropTargetAnimSupplier;
+import com.android.wm.shell.draganddrop.anim.HoverAnimProps;
+import com.android.wm.shell.draganddrop.anim.TwoFiftyFiftyTargetAnimator;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
 import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import kotlin.Pair;
 
 /**
  * The policy for handling drag and drop operations to shell.
@@ -89,24 +106,42 @@
     private final Starter mFullscreenStarter;
     // Used for launching tasks into splitscreen
     private final Starter mSplitscreenStarter;
+    private final DragZoneAnimator mDragZoneAnimator;
     private final SplitScreenController mSplitScreen;
-    private final ArrayList<SplitDragPolicy.Target> mTargets = new ArrayList<>();
+    private ArrayList<SplitDragPolicy.Target> mTargets = new ArrayList<>();
     private final RectF mDisallowHitRegion = new RectF();
+    /**
+     * Maps a given SnapPosition to an array where each index of the array represents one
+     * of the targets that are being hovered over, in order (Left to Right, Top to Bottom).
+     * Ex: 4 drop targets when we're in 50/50 split
+     * 2_50_50 => [ [AnimPropsTarget1, AnimPropsTarget2, AnimPropsTarget3, AnimPropsTarget4],
+     *              ... // hovering over target 2,
+     *              ... // hovering over target 3,
+     *              ... // hovering over target 4
+     *            ]
+     */
+    private final Map<Integer, List<List<HoverAnimProps>>> mHoverAnimProps = new HashMap();
 
     private InstanceId mLoggerSessionId;
     private DragSession mSession;
+    @Nullable
+    private Target mCurrentHoverTarget;
+    /** This variable is a temporary placeholder, will be queried on drag start. */
+    private int mCurrentSnapPosition = -1;
 
-    public SplitDragPolicy(Context context, SplitScreenController splitScreen) {
-        this(context, splitScreen, new DefaultStarter(context));
+    public SplitDragPolicy(Context context, SplitScreenController splitScreen,
+            DragZoneAnimator dragZoneAnimator) {
+        this(context, splitScreen, new DefaultStarter(context), dragZoneAnimator);
     }
 
     @VisibleForTesting
     SplitDragPolicy(Context context, SplitScreenController splitScreen,
-            Starter fullscreenStarter) {
+            Starter fullscreenStarter, DragZoneAnimator dragZoneAnimator) {
         mContext = context;
         mSplitScreen = splitScreen;
         mFullscreenStarter = fullscreenStarter;
         mSplitscreenStarter = splitScreen;
+        mDragZoneAnimator = dragZoneAnimator;
     }
 
     /**
@@ -164,58 +199,123 @@
                 || (mSession.runningTaskActType == ACTIVITY_TYPE_STANDARD
                         && mSession.runningTaskWinMode == WINDOWING_MODE_FULLSCREEN);
         if (allowSplit) {
-            // Already split, allow replacing existing split task
-            final Rect topOrLeftBounds = new Rect();
-            final Rect bottomOrRightBounds = new Rect();
-            mSplitScreen.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
-            topOrLeftBounds.intersect(displayRegion);
-            bottomOrRightBounds.intersect(displayRegion);
+            if (enableFlexibleSplit()) {
+                // TODO(b/349828130) get this from split screen controller, expose the SnapTarget object
+                //  entirely and then pull out the SnapPosition
+                @SplitScreenConstants.SnapPosition int snapPosition = SNAP_TO_2_50_50;
+                final Rect startHitRegion = new Rect();
+                final Rect endHitRegion = new Rect();
+                if (!inSplitScreen) {
+                    // Currently in fullscreen, split in half
+                    final Rect startBounds = new Rect();
+                    final Rect endBounds = new Rect();
+                    mSplitScreen.getStageBounds(startBounds, endBounds);
+                    startBounds.intersect(displayRegion);
+                    endBounds.intersect(displayRegion);
 
-            if (isLeftRightSplit) {
-                final Rect leftHitRegion = new Rect();
-                final Rect rightHitRegion = new Rect();
+                    if (isLeftRightSplit) {
+                        displayRegion.splitVertically(startHitRegion, endHitRegion);
+                    } else {
+                        displayRegion.splitHorizontally(startHitRegion, endHitRegion);
+                    }
 
-                // If we have existing split regions use those bounds, otherwise split it 50/50
-                if (inSplitScreen) {
-                    // The bounds of the existing split will have a divider bar, the hit region
-                    // should include that space. Find the center of the divider bar:
-                    float centerX = topOrLeftBounds.right + (dividerWidth / 2);
-                    // Now set the hit regions using that center.
-                    leftHitRegion.set(displayRegion);
-                    leftHitRegion.right = (int) centerX;
-                    rightHitRegion.set(displayRegion);
-                    rightHitRegion.left = (int) centerX;
+                    mTargets.add(new Target(TYPE_SPLIT_LEFT, startHitRegion, startBounds, -1));
+                    mTargets.add(new Target(TYPE_SPLIT_RIGHT, endHitRegion, endBounds, -1));
                 } else {
-                    displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+                    // TODO(b/349828130), move this into init function and/or the insets updating
+                    //  callback
+                    DropTargetAnimSupplier supplier = null;
+                    switch (snapPosition) {
+                        case SNAP_TO_2_50_50:
+                            supplier = new TwoFiftyFiftyTargetAnimator();
+                        break;
+                        case SplitScreenConstants.SNAP_TO_2_33_66:
+                            break;
+                        case SplitScreenConstants.SNAP_TO_2_66_33:
+                            break;
+                        case SplitScreenConstants.SNAP_TO_END_AND_DISMISS:
+                            break;
+                        case SplitScreenConstants.SNAP_TO_MINIMIZE:
+                            break;
+                        case SplitScreenConstants.SNAP_TO_NONE:
+                            break;
+                        case SplitScreenConstants.SNAP_TO_START_AND_DISMISS:
+                            break;
+                        default:
+                    }
+
+                    Pair<List<Target>, List<List<HoverAnimProps>>> targetsAnims =
+                            supplier.getTargets(mSession.displayLayout,
+                                    insets, isLeftRightSplit, mContext.getResources());
+                    mTargets = new ArrayList<>(targetsAnims.getFirst());
+                    mHoverAnimProps.put(SNAP_TO_2_50_50, targetsAnims.getSecond());
+                    assert(mTargets.size() == targetsAnims.getSecond().size());
+                    if (DEBUG_LAYOUT) {
+                        for (List<HoverAnimProps> props : targetsAnims.getSecond()) {
+                            StringBuilder sb = new StringBuilder();
+                            for (HoverAnimProps hap : props) {
+                                sb.append(hap).append("\n");
+                            }
+                            sb.append("\n");
+                            Log.d(TAG, sb.toString());
+                        }
+                    }
                 }
-
-                mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds));
-                mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds));
-
             } else {
-                final Rect topHitRegion = new Rect();
-                final Rect bottomHitRegion = new Rect();
+                // Already split, allow replacing existing split task
+                final Rect topOrLeftBounds = new Rect();
+                final Rect bottomOrRightBounds = new Rect();
+                mSplitScreen.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
+                topOrLeftBounds.intersect(displayRegion);
+                bottomOrRightBounds.intersect(displayRegion);
 
-                // If we have existing split regions use those bounds, otherwise split it 50/50
-                if (inSplitScreen) {
-                    // The bounds of the existing split will have a divider bar, the hit region
-                    // should include that space. Find the center of the divider bar:
-                    float centerX = topOrLeftBounds.bottom + (dividerWidth / 2);
-                    // Now set the hit regions using that center.
-                    topHitRegion.set(displayRegion);
-                    topHitRegion.bottom = (int) centerX;
-                    bottomHitRegion.set(displayRegion);
-                    bottomHitRegion.top = (int) centerX;
+                if (isLeftRightSplit) {
+                    final Rect leftHitRegion = new Rect();
+                    final Rect rightHitRegion = new Rect();
+
+                    // If we have existing split regions use those bounds, otherwise split it 50/50
+                    if (inSplitScreen) {
+                        // The bounds of the existing split will have a divider bar, the hit region
+                        // should include that space. Find the center of the divider bar:
+                        float centerX = topOrLeftBounds.right + (dividerWidth / 2);
+                        // Now set the hit regions using that center.
+                        leftHitRegion.set(displayRegion);
+                        leftHitRegion.right = (int) centerX;
+                        rightHitRegion.set(displayRegion);
+                        rightHitRegion.left = (int) centerX;
+                    } else {
+                        displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+                    }
+
+                    mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds, -1));
+                    mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds,
+                            -1));
                 } else {
-                    displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
-                }
+                    final Rect topHitRegion = new Rect();
+                    final Rect bottomHitRegion = new Rect();
 
-                mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds));
-                mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds));
+                    // If we have existing split regions use those bounds, otherwise split it 50/50
+                    if (inSplitScreen) {
+                        // The bounds of the existing split will have a divider bar, the hit region
+                        // should include that space. Find the center of the divider bar:
+                        float centerX = topOrLeftBounds.bottom + (dividerWidth / 2);
+                        // Now set the hit regions using that center.
+                        topHitRegion.set(displayRegion);
+                        topHitRegion.bottom = (int) centerX;
+                        bottomHitRegion.set(displayRegion);
+                        bottomHitRegion.top = (int) centerX;
+                    } else {
+                        displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
+                    }
+
+                    mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds, -1));
+                    mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds,
+                            -1));
+                }
             }
         } else {
             // Split-screen not allowed, so only show the fullscreen target
-            mTargets.add(new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
+            mTargets.add(new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion, -1));
         }
         return mTargets;
     }
@@ -230,6 +330,22 @@
         }
         for (int i = mTargets.size() - 1; i >= 0; i--) {
             SplitDragPolicy.Target t = mTargets.get(i);
+            if (enableFlexibleSplit() && mCurrentHoverTarget != null) {
+                // If we're in flexible split, the targets themselves animate, so we have to rely
+                // on the view's animated position for subsequent drag coordinates which we also
+                // cache in HoverAnimProps.
+                List<List<HoverAnimProps>> hoverAnimPropTargets =
+                        mHoverAnimProps.get(mCurrentSnapPosition);
+                for (HoverAnimProps animProps :
+                        hoverAnimPropTargets.get(mCurrentHoverTarget.index)) {
+                    if (animProps.getHoverRect() != null &&
+                            animProps.getHoverRect().contains(x, y)) {
+                        return animProps.getTarget();
+                    }
+                }
+
+            }
+
             if (t.hitRegion.contains(x, y)) {
                 return t;
             }
@@ -266,6 +382,10 @@
         } else {
             launchIntent(mSession, starter, position, hideTaskToken);
         }
+
+        if (enableFlexibleSplit()) {
+            reset();
+        }
     }
 
     /**
@@ -335,6 +455,82 @@
                 null /* fillIntent */, position, opts, hideTaskToken);
     }
 
+    @Override
+    public void onHoveringOver(Target hoverTarget) {
+        final boolean isLeftRightSplit = mSplitScreen != null && mSplitScreen.isLeftRightSplit();
+        final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
+        if (!inSplitScreen) {
+            // no need to animate for entering 50/50 split
+            return;
+        }
+
+        mCurrentHoverTarget = hoverTarget;
+        if (hoverTarget == null) {
+            // Reset to default state
+            BiConsumer<Target, View> biConsumer = new BiConsumer<Target, View>() {
+                @Override
+                public void accept(Target target, View view) {
+                    // take into account left/right split
+                    Animator transX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+                            target.drawRegion.left);
+                    Animator transY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+                            target.drawRegion.top);
+                    Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, 1);
+                    Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, 1);
+                    AnimatorSet as = new AnimatorSet();
+                    as.play(transX);
+                    as.play(transY);
+                    as.play(scaleX);
+                    as.play(scaleY);
+
+                    as.start();
+                }
+            };
+            mDragZoneAnimator.animateDragTargets(List.of(biConsumer));
+            return;
+        }
+
+        // TODO(b/349828130) get this from split controller
+        @SplitScreenConstants.SnapPosition int snapPosition = SNAP_TO_2_50_50;
+        mCurrentSnapPosition = SNAP_TO_2_50_50;
+        List<BiConsumer<Target, View>> animatingConsumers = new ArrayList<>();
+        final List<List<HoverAnimProps>> hoverAnimProps = mHoverAnimProps.get(snapPosition);
+        List<HoverAnimProps> animProps = hoverAnimProps.get(hoverTarget.index);
+        // Expand start and push out the rest to the end
+        BiConsumer<Target, View> biConsumer = new BiConsumer<>() {
+            @Override
+            public void accept(Target target, View view) {
+                if (animProps.isEmpty() || animProps.size() < (target.index + 1)) {
+                    return;
+                }
+                HoverAnimProps singleAnimProp = animProps.get(target.index);
+                Animator transX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+                        singleAnimProp.getTransX());
+                Animator transY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+                        singleAnimProp.getTransY());
+                Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X,
+                        singleAnimProp.getScaleX());
+                Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y,
+                        singleAnimProp.getScaleY());
+                AnimatorSet as = new AnimatorSet();
+                as.play(transX);
+                as.play(transY);
+                as.play(scaleX);
+                as.play(scaleY);
+                as.start();
+            }
+        };
+        animatingConsumers.add(biConsumer);
+        mDragZoneAnimator.animateDragTargets(animatingConsumers);
+    }
+
+    private void reset() {
+        mCurrentHoverTarget = null;
+        mCurrentSnapPosition = -1;
+    }
+
+
+
     /**
      * Interface for actually committing the task launches.
      */
@@ -425,7 +621,7 @@
      */
     public static class Target {
         static final int TYPE_FULLSCREEN = 0;
-        static final int TYPE_SPLIT_LEFT = 1;
+        public static final int TYPE_SPLIT_LEFT = 1;
         static final int TYPE_SPLIT_TOP = 2;
         static final int TYPE_SPLIT_RIGHT = 3;
         static final int TYPE_SPLIT_BOTTOM = 4;
@@ -445,16 +641,23 @@
         final Rect hitRegion;
         // The approximate visual region for where the task will start
         final Rect drawRegion;
+        int index;
 
-        public Target(@Type int t, Rect hit, Rect draw) {
+        /**
+         * @param index 0-indexed, represents which position of drop target this object represents,
+         *              0 to N for left to right, top to bottom
+         */
+        public Target(@Type int t, Rect hit, Rect draw, int index) {
             type = t;
             hitRegion = hit;
             drawRegion = draw;
+            this.index = index;
         }
 
         @Override
         public String toString() {
-            return "Target {type=" + type + " hit=" + hitRegion + " draw=" + drawRegion + "}";
+            return "Target {type=" + type + " hit=" + hitRegion + " draw=" + drawRegion
+                    + " index=" + index + "}";
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/DropTargetAnimSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/DropTargetAnimSupplier.kt
new file mode 100644
index 0000000..bb34613
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/DropTargetAnimSupplier.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.draganddrop.anim
+
+import android.content.res.Resources
+import android.graphics.Insets
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.draganddrop.SplitDragPolicy
+
+/**
+ * When the user is dragging an icon from Taskbar to add an app into split
+ * screen, we have a set of rules by which we draw and move colored drop
+ * targets around the screen. The rules are provided through this interface.
+ *
+ * Each possible screen layout should have an implementation of this interface.
+ * E.g.
+ * - 50:50 two-app split
+ * - 10:45:45 three-app split
+ * - single app, no split
+ *     = three implementations of this interface.
+ */
+interface DropTargetAnimSupplier {
+    /**
+     * Returns a Pair of lists.
+     * First list (length n): Where to draw the n colored drop zones.
+     * Second list (length n): How to animate the drop zones as user hovers around.
+     *
+     * Ex: First list => [A, B, C] // 3 views will be created representing these 3 targets
+     * Second list => [
+     *      [A (scaleX=4), B (translateX=20), C (translateX=20)], // hovering over A
+     *      [A (translateX=20), B (scaleX=4), C (translateX=20)], // hovering over B
+     *      [A (translateX=20), B (translateX=20), C (scaleX=4)], // hovering over C
+     *  ]
+     *
+     *  All indexes assume 0 to N => left to right when [isLeftRightSplit] is true and top to bottom
+     *  when [isLeftRightSplit] is false. Indexing is left to right even in RtL mode.
+     *
+     *  All lists should have the SAME number of elements, even if no animations are to be run for
+     *  a given target while in a hover state.
+     *  It's not that we don't trust you, but we _really_ don't trust you, so this will throw an
+     *  exception if lengths are different. Don't ruin it for everyone else...
+     *  or do. Idk, you're an adult.
+     */
+    fun getTargets(displayLayout: DisplayLayout, insets: Insets, isLeftRightSplit: Boolean,
+                   resources: Resources) :
+            Pair<List<SplitDragPolicy.Target>, List<List<HoverAnimProps>>>
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/HoverAnimProps.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/HoverAnimProps.kt
new file mode 100644
index 0000000..d61caeb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/HoverAnimProps.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.draganddrop.anim
+
+import android.graphics.Rect
+import com.android.wm.shell.draganddrop.SplitDragPolicy
+
+/**
+ * Contains the animation props to represent a single state of drop targets.
+ * When the user is dragging, we'd be going between different HoverAnimProps
+ */
+data class HoverAnimProps(
+    var target: SplitDragPolicy.Target,
+    val transX: Float,
+    val transY: Float,
+    val scaleX: Float,
+    val scaleY: Float,
+    /**
+     * Pass in null to indicate this target cannot be hovered over for this given animation/
+     * state
+     *
+     * TODO: There's some way we can probably use the existing translation/scaling values
+     * to take [.target]'s hitRect and scale that so we don't have to take in a separate
+     * hoverRect in the CTOR. Have to make sure the pivots match since view's pivot in the
+     * center of the view and rect's pivot at 0, 0 if unspecified.
+     * The two may also not be correlated, but worth investigating
+     *
+     */
+    var hoverRect: Rect?
+) {
+
+    override fun toString(): String {
+        return ("targetId: " + target
+                + " translationX: " + transX
+                + " translationY: " + transY
+                + " scaleX: " + scaleX
+                + " scaleY: " + scaleY
+                + " hoverRect: " + hoverRect)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt
new file mode 100644
index 0000000..9f532f5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.draganddrop.anim
+
+import android.content.res.Resources
+import android.graphics.Insets
+import android.graphics.Rect
+import com.android.wm.shell.R
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.draganddrop.SplitDragPolicy.Target
+
+/**
+ * Represents Drop Zone targets and animations for when the system is currently in a 2 app 50/50
+ * split.
+ * SnapPosition = 2_50_50
+ *
+ * NOTE: Naming convention for many variables is done as "hXtYZ"
+ * This means that variable is a transformation on the Z property for target index Y while the user
+ * is hovering over target index X
+ * Ex: h1t2scaleX=2 => User is hovering over target index 1, target index 2 should scaleX by 2
+ *
+ * TODO(b/349828130): Everything in this class is temporary, none of this is up to spec.
+ */
+class TwoFiftyFiftyTargetAnimator : DropTargetAnimSupplier {
+    /**
+     * TODO: Could we transpose all the horizontal rects by 90 degrees and have that suffice for
+     *  top bottom split?? Hmmm... Doubt it.
+     */
+    override fun getTargets(
+        displayLayout: DisplayLayout,
+        insets: Insets,
+        isLeftRightSplit: Boolean,
+        resources: Resources
+    ): Pair<List<Target>, List<List<HoverAnimProps>>> {
+        val targets : ArrayList<Target> = ArrayList()
+        val w: Int = displayLayout.width()
+        val h: Int = displayLayout.height()
+        val iw = w - insets.left - insets.right
+        val ih = h - insets.top - insets.bottom
+        val l = insets.left
+        val t = insets.top
+        val displayRegion = Rect(l, t, l + iw, t + ih)
+        val fullscreenDrawRegion = Rect(displayRegion)
+        val dividerWidth: Float = resources.getDimensionPixelSize(
+            R.dimen.split_divider_bar_width
+        ).toFloat()
+
+        val farStartBounds = Rect()
+        farStartBounds.set(displayRegion)
+        val startBounds = Rect()
+        startBounds.set(displayRegion)
+        val endBounds = Rect()
+        endBounds.set(displayRegion)
+        val farEndBounds = Rect()
+        farEndBounds.set(displayRegion)
+        val endsPercent = 0.10f
+        val visibleStagePercent = 0.45f
+        val halfDividerWidth = dividerWidth.toInt() / 2
+        val endsWidth = Math.round(displayRegion.width() * endsPercent)
+        val stageWidth = Math.round(displayRegion.width() * visibleStagePercent)
+
+
+        // Place the farStart and farEnds outside of the display, and then
+        // animate them in once the hover starts
+        // | = divider; || = display boundary
+        // farStart || start | end || farEnd
+        farStartBounds.left = -endsWidth
+        farStartBounds.right = 0
+        startBounds.left = farStartBounds.right + dividerWidth.toInt()
+        startBounds.right = startBounds.left + stageWidth
+        endBounds.left = startBounds.right + dividerWidth.toInt()
+        endBounds.right = endBounds.left + stageWidth
+        farEndBounds.left = fullscreenDrawRegion.right
+        farEndBounds.right = farEndBounds.left + endsWidth
+
+
+        // For the hit rect, trim the divider space we've added between the
+        // rects
+        targets.add(
+            Target(
+                Target.TYPE_SPLIT_LEFT,
+                Rect(
+                    farStartBounds.left, farStartBounds.top,
+                    farStartBounds.right + halfDividerWidth,
+                    farStartBounds.bottom
+                ),
+                farStartBounds, 0
+            )
+        )
+        targets.add(
+            Target(
+                Target.TYPE_SPLIT_LEFT,
+                Rect(
+                    startBounds.left - halfDividerWidth,
+                    startBounds.top,
+                    startBounds.right + halfDividerWidth,
+                    startBounds.bottom
+                ),
+                startBounds, 1
+            )
+        )
+        targets.add(
+            Target(
+                Target.TYPE_SPLIT_LEFT,
+                Rect(
+                    endBounds.left - halfDividerWidth,
+                    endBounds.top, endBounds.right, endBounds.bottom
+                ),
+                endBounds, 2
+            )
+        )
+        targets.add(
+            Target(
+                Target.TYPE_SPLIT_LEFT,
+                Rect(
+                    farEndBounds.left - halfDividerWidth,
+                    farEndBounds.top, farEndBounds.right, farEndBounds.bottom
+                ),
+                farEndBounds, 3
+            )
+        )
+
+
+        // Hovering over target 0,
+        // * increase scaleX of target 0
+        // * decrease scaleX of target 1, 2
+        // * ensure target 3 offscreen
+
+        // bring target 0 in from offscreen and expand
+        val h0t0ScaleX = stageWidth.toFloat() / endsWidth
+        val h0t0TransX: Float = stageWidth / h0t0ScaleX + dividerWidth
+        val h0t0HoverProps = HoverAnimProps(
+            targets.get(0),
+            h0t0TransX, farStartBounds.top.toFloat(), h0t0ScaleX, 1f,
+            Rect(
+                0, 0, (stageWidth + dividerWidth).toInt(),
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 1 over to the middle/end
+        val h0t1TransX = stageWidth.toFloat()
+        val h0t1ScaleX = 1f
+        val h0t1HoverProps = HoverAnimProps(
+            targets.get(1),
+            h0t1TransX, startBounds.top.toFloat(), h0t1ScaleX, 1f,
+            Rect(
+                stageWidth, 0, (stageWidth + h0t1TransX).toInt(),
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 2 to the very end
+        val h0t2TransX = endBounds.left + stageWidth / 2f
+        val h0t2ScaleX = endsWidth.toFloat() / stageWidth
+        val h0t2HoverProps = HoverAnimProps(
+            targets.get(2),
+            h0t2TransX, endBounds.top.toFloat(), h0t2ScaleX, 1f,
+            Rect(
+                displayRegion.right as Int - endsWidth, 0,
+                displayRegion.right as Int,
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 3 off-screen
+        val h0t3TransX = farEndBounds.right.toFloat()
+        val h0t3ScaleX = 1f
+        val h0t3HoverProps = HoverAnimProps(
+            targets.get(3),
+            h0t3TransX, farEndBounds.top.toFloat(), h0t3ScaleX, 1f,
+            null
+        )
+        val animPropsForHoverTarget0 =
+            listOf(h0t0HoverProps, h0t1HoverProps, h0t2HoverProps, h0t3HoverProps)
+
+
+        // Hovering over target 1,
+        // * Bring in target 0 from offscreen start
+        // * Shift over target 1
+        // * Slightly lower scale of target 2
+        // * Ensure target 4 offscreen
+        // bring target 0 in from offscreen
+        val h1t0TransX = 0f
+        val h1t0ScaleX = 1f
+        val h1t0HoverProps = HoverAnimProps(
+            targets.get(0),
+            h1t0TransX, farStartBounds.top.toFloat(), h1t0ScaleX, 1f,
+            Rect(
+                0, 0, (farStartBounds.width() + dividerWidth).toInt(),
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 1 over a tiny bit by same amount and make it smaller
+        val h1t1TransX: Float = endsWidth + dividerWidth
+        val h1t1ScaleX = 1f
+        val h1t1HoverProps = HoverAnimProps(
+            targets.get(1),
+            h1t1TransX, startBounds.top.toFloat(), h1t1ScaleX, 1f,
+            Rect(
+                h1t1TransX.toInt(), 0, (h1t1TransX + stageWidth).toInt(),
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 2 to the very end
+        val h1t2TransX = (endBounds.left + farStartBounds.width()).toFloat()
+        val h1t2ScaleX = h1t1ScaleX
+        val h1t2HoverProps = HoverAnimProps(
+            targets.get(2),
+            h1t2TransX, endBounds.top.toFloat(), h1t2ScaleX, 1f,
+            Rect(
+                endBounds.left + farStartBounds.width(),
+                0,
+                (endBounds.left + farStartBounds.width() + stageWidth),
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 3 off-screen, default laid out is off-screen
+        val h1t3TransX = farEndBounds.right.toFloat()
+        val h1t3ScaleX = 1f
+        val h1t3HoverProps = HoverAnimProps(
+            targets.get(3),
+            h1t3TransX, farEndBounds.top.toFloat(), h1t3ScaleX, 1f,
+            null
+        )
+        val animPropsForHoverTarget1 =
+            listOf(h1t0HoverProps, h1t1HoverProps, h1t2HoverProps, h1t3HoverProps)
+
+
+        // Hovering over target 2,
+        // * Ensure Target 0 offscreen
+        // * Ensure target 1 back to start, slightly smaller scale
+        // * Slightly lower scale of target 2
+        // * Bring target 4 on screen
+        // reset target 0
+        val h2t0TransX = farStartBounds.left.toFloat()
+        val h2t0ScaleX = 1f
+        val h2t0HoverProps = HoverAnimProps(
+            targets.get(0),
+            h2t0TransX, farStartBounds.top.toFloat(), h2t0ScaleX, 1f,
+            null
+        )
+
+
+        // move target 1 over a tiny bit by same amount and make it smaller
+        val h2t1TransX = startBounds.left.toFloat()
+        val h2t1ScaleX = 1f
+        val h2t1HoverProps = HoverAnimProps(
+            targets.get(1),
+            h2t1TransX, startBounds.top.toFloat(), h2t1ScaleX, 1f,
+            Rect(
+                startBounds.left, 0,
+                (startBounds.left + stageWidth),
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 2 to the very end
+        val h2t2TransX = endBounds.left.toFloat()
+        val h2t2ScaleX = h2t1ScaleX
+        val h2t2HoverProps = HoverAnimProps(
+            targets.get(2),
+            h2t2TransX, endBounds.top.toFloat(), h2t2ScaleX, 1f,
+            Rect(
+                (startBounds.right + dividerWidth).toInt(),
+                0,
+                endBounds.left + stageWidth,
+                farStartBounds.bottom
+            )
+        )
+
+
+        // bring target 3 on-screen
+        val h2t3TransX = (farEndBounds.left - farEndBounds.width()).toFloat()
+        val h2t3ScaleX = 1f
+        val h2t3HoverProps = HoverAnimProps(
+            targets.get(3),
+            h2t3TransX, farEndBounds.top.toFloat(), h2t3ScaleX, 1f,
+            Rect(
+                endBounds.right,
+                0,
+                displayRegion.right,
+                farStartBounds.bottom
+            )
+        )
+        val animPropsForHoverTarget2 =
+            listOf(h2t0HoverProps, h2t1HoverProps, h2t2HoverProps, h2t3HoverProps)
+
+
+        // Hovering over target 3,
+        // * Ensure Target 0 offscreen
+        // * Ensure target 1 back to start, slightly smaller scale
+        // * Slightly lower scale of target 2
+        // * Bring target 4 on screen and scale up
+        // reset target 0
+        val h3t0TransX = farStartBounds.left.toFloat()
+        val h3t0ScaleX = 1f
+        val h3t0HoverProps = HoverAnimProps(
+            targets.get(0),
+            h3t0TransX, farStartBounds.top.toFloat(), h3t0ScaleX, 1f,
+            null
+        )
+
+
+        // move target 1 over a tiny bit by same amount and make it smaller
+        val h3t1ScaleX = endsWidth.toFloat() / stageWidth
+        val h3t1TransX = 0 - (stageWidth / (1 / h3t1ScaleX))
+        val h3t1HoverProps = HoverAnimProps(
+            targets.get(1),
+            h3t1TransX, startBounds.top.toFloat(), h3t1ScaleX, 1f,
+            Rect(
+                0, 0,
+                endsWidth,
+                farStartBounds.bottom
+            )
+        )
+
+
+        // move target 2 towards the start
+        val h3t2TransX: Float = endsWidth + dividerWidth
+        val h3t2ScaleX = 1f
+        val h3t2HoverProps = HoverAnimProps(
+            targets.get(2),
+            h3t2TransX, endBounds.top.toFloat(), h3t2ScaleX, 1f,
+            Rect(
+                endsWidth, 0,
+                (endsWidth + stageWidth + dividerWidth).toInt(),
+                farStartBounds.bottom
+            )
+        )
+
+
+        // bring target 3 on-screen and expand
+        val h3t3ScaleX = stageWidth.toFloat() / endsWidth
+        val h3t3TransX = endBounds.right - stageWidth / 2f
+        val h3t3HoverProps = HoverAnimProps(
+            targets.get(3),
+            h3t3TransX, farEndBounds.top.toFloat(), h3t3ScaleX, 1f,
+            Rect(
+                displayRegion.right - stageWidth, 0,
+                displayRegion.right,
+                farStartBounds.bottom
+            )
+        )
+        val animPropsForHoverTarget3 =
+            listOf(h3t0HoverProps, h3t1HoverProps, h3t2HoverProps, h3t3HoverProps)
+
+        return Pair(targets, listOf(animPropsForHoverTarget0, animPropsForHoverTarget1,
+            animPropsForHoverTarget2, animPropsForHoverTarget3))
+
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 92e645d..a16446ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -24,7 +24,7 @@
 import android.content.Context;
 import android.util.SparseArray;
 import android.view.SurfaceControl;
-import android.window.flags.DesktopModeFlags;
+import android.window.DesktopModeFlags;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -126,6 +126,7 @@
                         || repository.isClosingTask(taskInfo.taskId)) {
                     // A task that's vanishing should be removed:
                     // - If it's closed by the X button which means it's marked as a closing task.
+                    repository.removeClosingTask(taskInfo.taskId);
                     repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
                 } else {
                     repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId, false);
@@ -150,8 +151,6 @@
             mDesktopRepository.ifPresent(repository -> {
                 if (taskInfo.isVisible) {
                     repository.addActiveTask(taskInfo.displayId, taskInfo.taskId);
-                } else if (repository.isClosingTask(taskInfo.taskId)) {
-                    repository.removeClosingTask(taskInfo.taskId);
                 }
                 repository.updateTaskVisibility(taskInfo.displayId, taskInfo.taskId,
                         taskInfo.isVisible);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index c9eccc3..771573d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -30,6 +30,7 @@
 import com.android.window.flags.Flags;
 import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
@@ -50,6 +51,7 @@
     private final Optional<DesktopFullImmersiveTransitionHandler> mImmersiveTransitionHandler;
     private final WindowDecorViewModel mWindowDecorViewModel;
     private final Optional<TaskChangeListener> mTaskChangeListener;
+    private final FocusTransitionObserver mFocusTransitionObserver;
 
     private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo =
             new HashMap<>();
@@ -60,11 +62,13 @@
             Transitions transitions,
             Optional<DesktopFullImmersiveTransitionHandler> immersiveTransitionHandler,
             WindowDecorViewModel windowDecorViewModel,
-            Optional<TaskChangeListener> taskChangeListener) {
+            Optional<TaskChangeListener> taskChangeListener,
+            FocusTransitionObserver focusTransitionObserver) {
         mTransitions = transitions;
         mImmersiveTransitionHandler = immersiveTransitionHandler;
         mWindowDecorViewModel = windowDecorViewModel;
         mTaskChangeListener = taskChangeListener;
+        mFocusTransitionObserver = focusTransitionObserver;
         if (FreeformComponents.isFreeformEnabled(context)) {
             shellInit.addInitCallback(this::onInit, this);
         }
@@ -85,8 +89,11 @@
             // TODO(b/367268953): Remove when DesktopTaskListener is introduced and the repository
             //  is updated from there **before** the |mWindowDecorViewModel| methods are invoked.
             //  Otherwise window decoration relayout won't run with the immersive state up to date.
-            mImmersiveTransitionHandler.ifPresent(h -> h.onTransitionReady(transition));
+            mImmersiveTransitionHandler.ifPresent(h -> h.onTransitionReady(transition, info));
         }
+        // Update focus state first to ensure the correct state can be queried from listeners.
+        // TODO(371503964): Remove this once the unified task repository is ready.
+        mFocusTransitionObserver.updateFocusState(info);
 
         final ArrayList<ActivityManager.RunningTaskInfo> taskInfoList = new ArrayList<>();
         final ArrayList<WindowContainerToken> taskParents = new ArrayList<>();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index cbb08b8..6da3995 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -463,7 +463,7 @@
         // so update fixed rotation state to default.
         mFixedRotationState = FIXED_ROTATION_UNDEFINED;
 
-        if (transition != mExitTransition) {
+        if (transition != mExitTransition && transition != mMoveToBackTransition) {
             return;
         }
         // This means an expand happened before enter-pip finished and we are now "merging" a
@@ -477,8 +477,10 @@
             cancelled = true;
         }
 
-        // Unset exitTransition AFTER cancel so that finishResize knows we are merging.
+        // Unset both exitTransition and moveToBackTransition AFTER cancel so that
+        // finishResize knows we are merging.
         mExitTransition = null;
+        mMoveToBackTransition = null;
         if (!cancelled) return;
         final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
         if (taskInfo != null) {
@@ -515,7 +517,8 @@
         // means we're expecting the exit transition will be "merged" into another transition
         // (likely a remote like launcher), so don't fire the finish-callback here -- wait until
         // the exit transition is merged.
-        if ((mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null) {
+        if ((mExitTransition == null || mMoveToBackTransition == null || isAnimatingLocally())
+                && mFinishCallback != null) {
             final SurfaceControl leash = mPipOrganizer.getSurfaceControl();
             final boolean hasValidLeash = leash != null && leash.isValid();
             WindowContainerTransaction wct = null;
@@ -665,17 +668,6 @@
         return null;
     }
 
-    @Nullable
-    private TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) {
-        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-            final TransitionInfo.Change change = info.getChanges().get(i);
-            if (change.getEndFixedRotation() != ROTATION_UNDEFINED) {
-                return change;
-            }
-        }
-        return null;
-    }
-
     private void startExitAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 9b81581..94b344f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.pip;
 
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.TRANSIT_PIP;
 
@@ -346,6 +347,21 @@
         return false;
     }
 
+    /**
+     * Gets a change amongst the transition targets that is in a different final orientation than
+     * the display, signalling a potential fixed rotation transition.
+     */
+    @Nullable
+    public TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if (change.getEndFixedRotation() != ROTATION_UNDEFINED) {
+                return change;
+            }
+        }
+        return null;
+    }
+
     /** End the currently-playing PiP animation. */
     public void end() {
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
new file mode 100644
index 0000000..5381a62
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.animation;
+
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.animation.Animator;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.shared.animation.Interpolators;
+
+/**
+ * Animator that handles bounds animations for entering PIP.
+ */
+public class PipEnterAnimator extends ValueAnimator
+        implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
+    @NonNull private final SurfaceControl mLeash;
+    private final SurfaceControl.Transaction mStartTransaction;
+    private final SurfaceControl.Transaction mFinishTransaction;
+
+    // Bounds updated by the evaluator as animator is running.
+    private final Rect mAnimatedRect = new Rect();
+
+    private final RectEvaluator mRectEvaluator;
+    private final Rect mEndBounds = new Rect();
+    @Nullable private final Rect mSourceRectHint;
+    private final @Surface.Rotation int mRotation;
+    @Nullable private Runnable mAnimationStartCallback;
+    @Nullable private Runnable mAnimationEndCallback;
+
+    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+            mSurfaceControlTransactionFactory;
+
+    // Internal state representing initial transform - cached to avoid recalculation.
+    private final PointF mInitScale = new PointF();
+    private final PointF mInitPos = new PointF();
+    private final Rect mInitCrop = new Rect();
+    private final PointF mInitActivityScale = new PointF();
+    private final PointF mInitActivityPos = new PointF();
+
+    Matrix mTransformTensor = new Matrix();
+    final float[] mMatrixTmp = new float[9];
+
+    public PipEnterAnimator(Context context,
+            @NonNull SurfaceControl leash,
+            SurfaceControl.Transaction startTransaction,
+            SurfaceControl.Transaction finishTransaction,
+            @NonNull Rect endBounds,
+            @Nullable Rect sourceRectHint,
+            @Surface.Rotation int rotation) {
+        mLeash = leash;
+        mStartTransaction = startTransaction;
+        mFinishTransaction = finishTransaction;
+        mRectEvaluator = new RectEvaluator(mAnimatedRect);
+        mEndBounds.set(endBounds);
+        mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
+        mRotation = rotation;
+        mSurfaceControlTransactionFactory =
+                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+
+        final int enterAnimationDuration = context.getResources()
+                .getInteger(R.integer.config_pipEnterAnimationDuration);
+        setDuration(enterAnimationDuration);
+        setFloatValues(0f, 1f);
+        setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        addListener(this);
+        addUpdateListener(this);
+    }
+
+    public void setAnimationStartCallback(@NonNull Runnable runnable) {
+        mAnimationStartCallback = runnable;
+    }
+
+    public void setAnimationEndCallback(@NonNull Runnable runnable) {
+        mAnimationEndCallback = runnable;
+    }
+
+    @Override
+    public void onAnimationStart(@NonNull Animator animation) {
+        if (mAnimationStartCallback != null) {
+            mAnimationStartCallback.run();
+        }
+        if (mStartTransaction != null) {
+            onEnterAnimationUpdate(0f /* fraction */, mStartTransaction);
+            mStartTransaction.apply();
+        }
+    }
+
+    @Override
+    public void onAnimationEnd(@NonNull Animator animation) {
+        if (mFinishTransaction != null) {
+            onEnterAnimationUpdate(1f /* fraction */, mFinishTransaction);
+        }
+        if (mAnimationEndCallback != null) {
+            mAnimationEndCallback.run();
+        }
+    }
+
+    @Override
+    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+        final float fraction = getAnimatedFraction();
+        onEnterAnimationUpdate(fraction, tx);
+        tx.apply();
+    }
+
+    /**
+     * Updates the transaction to reflect the state of PiP leash at a certain fraction during enter.
+     *
+     * @param fraction the fraction of the animator going from 0f to 1f.
+     * @param tx the transaction to modify the transform of.
+     */
+    public void onEnterAnimationUpdate(float fraction, SurfaceControl.Transaction tx) {
+        onEnterAnimationUpdate(mInitScale, mInitPos, mInitCrop, fraction, tx);
+    }
+
+    private void onEnterAnimationUpdate(PointF initScale, PointF initPos, Rect initCrop,
+            float fraction, SurfaceControl.Transaction tx) {
+        float scaleX = 1 + (initScale.x - 1) * (1 - fraction);
+        float scaleY = 1 + (initScale.y - 1) * (1 - fraction);
+        float posX = initPos.x + (mEndBounds.left - initPos.x) * fraction;
+        float posY = initPos.y + (mEndBounds.top - initPos.y) * fraction;
+
+        int normalizedRotation = mRotation;
+        if (normalizedRotation == ROTATION_270) {
+            normalizedRotation = -ROTATION_90;
+        }
+        float degrees = -normalizedRotation * 90f * fraction;
+
+        Rect endCrop = new Rect(mEndBounds);
+        endCrop.offsetTo(0, 0);
+        mRectEvaluator.evaluate(fraction, initCrop, endCrop);
+        tx.setCrop(mLeash, mAnimatedRect);
+
+        mTransformTensor.setScale(scaleX, scaleY);
+        mTransformTensor.postTranslate(posX, posY);
+        mTransformTensor.postRotate(degrees);
+        tx.setMatrix(mLeash, mTransformTensor, mMatrixTmp);
+    }
+
+    // no-ops
+
+    @Override
+    public void onAnimationCancel(@NonNull Animator animation) {}
+
+    @Override
+    public void onAnimationRepeat(@NonNull Animator animation) {}
+
+    /**
+     * Caches the initial transform relevant values for the bounds enter animation.
+     *
+     * Since enter PiP makes use of a config-at-end transition, initial transform needs to be
+     * calculated differently from generic transitions.
+     * @param pipChange PiP change received as a transition target.
+     */
+    public void setEnterStartState(@NonNull TransitionInfo.Change pipChange,
+            @NonNull TransitionInfo.Change pipActivityChange) {
+        PipUtils.calcEndTransform(pipActivityChange, pipChange, mInitActivityScale,
+                mInitActivityPos);
+        if (mStartTransaction != null && pipActivityChange.getLeash() != null) {
+            mStartTransaction.setCrop(pipActivityChange.getLeash(), null);
+            mStartTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x,
+                    mInitActivityScale.y);
+            mStartTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x,
+                    mInitActivityPos.y);
+            mFinishTransaction.setCrop(pipActivityChange.getLeash(), null);
+            mFinishTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x,
+                    mInitActivityScale.y);
+            mFinishTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x,
+                    mInitActivityPos.y);
+        }
+        PipUtils.calcStartTransform(pipChange, mInitScale, mInitPos, mInitCrop);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
similarity index 77%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
index 8ebdc96..a93ef12 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
@@ -19,7 +19,6 @@
 import android.animation.Animator;
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
-import android.annotation.IntDef;
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.Surface;
@@ -30,35 +29,22 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 /**
- * Animator that handles bounds animations for entering / exiting PIP.
+ * Animator that handles bounds animations for exit-via-expanding PIP.
  */
-public class PipEnterExitAnimator extends ValueAnimator
+public class PipExpandAnimator extends ValueAnimator
         implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
-    @IntDef(prefix = {"BOUNDS_"}, value = {
-            BOUNDS_ENTER,
-            BOUNDS_EXIT
-    })
-
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BOUNDS {}
-
-    public static final int BOUNDS_ENTER = 0;
-    public static final int BOUNDS_EXIT = 1;
-
-    @NonNull private final SurfaceControl mLeash;
+    @NonNull
+    private final SurfaceControl mLeash;
     private final SurfaceControl.Transaction mStartTransaction;
     private final SurfaceControl.Transaction mFinishTransaction;
-    private final int mEnterExitAnimationDuration;
-    private final @BOUNDS int mDirection;
     private final @Surface.Rotation int mRotation;
 
     // optional callbacks for tracking animation start and end
-    @Nullable private Runnable mAnimationStartCallback;
+    @Nullable
+    private Runnable mAnimationStartCallback;
     @Nullable private Runnable mAnimationEndCallback;
 
     private final Rect mBaseBounds = new Rect();
@@ -78,7 +64,7 @@
     private final RectEvaluator mInsetEvaluator;
     private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
 
-    public PipEnterExitAnimator(Context context,
+    public PipExpandAnimator(Context context,
             @NonNull SurfaceControl leash,
             SurfaceControl.Transaction startTransaction,
             SurfaceControl.Transaction finishTransaction,
@@ -86,7 +72,6 @@
             @NonNull Rect startBounds,
             @NonNull Rect endBounds,
             @Nullable Rect sourceRectHint,
-            @BOUNDS int direction,
             @Surface.Rotation int rotation) {
         mLeash = leash;
         mStartTransaction = startTransaction;
@@ -98,7 +83,6 @@
         mRectEvaluator = new RectEvaluator(mAnimatedRect);
         mInsetEvaluator = new RectEvaluator(new Rect());
         mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
-        mDirection = direction;
         mRotation = rotation;
 
         mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
@@ -113,12 +97,14 @@
 
         mSurfaceControlTransactionFactory =
                 new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
-        mEnterExitAnimationDuration = context.getResources()
+
+        final int enterAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipEnterAnimationDuration);
+        setDuration(enterAnimationDuration);
 
         setObjectValues(startBounds, endBounds);
-        setDuration(mEnterExitAnimationDuration);
         setEvaluator(mRectEvaluator);
+        setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         addListener(this);
         addUpdateListener(this);
     }
@@ -147,9 +133,10 @@
             // finishTransaction might override some state (eg. corner radii) so we want to
             // manually set the state to the end of the animation
             mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint,
-                            mBaseBounds, mAnimatedRect, getInsets(1f), isInPipDirection(), 1f)
-                    .round(mFinishTransaction, mLeash, isInPipDirection())
-                    .shadow(mFinishTransaction, mLeash, isInPipDirection());
+                            mBaseBounds, mAnimatedRect, getInsets(1f),
+                            false /* isInPipDirection */, 1f)
+                    .round(mFinishTransaction, mLeash, false /* applyCornerRadius */)
+                    .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */);
         }
         if (mAnimationEndCallback != null) {
             mAnimationEndCallback.run();
@@ -160,32 +147,23 @@
     public void onAnimationUpdate(@NonNull ValueAnimator animation) {
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         final float fraction = getAnimatedFraction();
-        Rect insets = getInsets(fraction);
 
         // TODO (b/350801661): implement fixed rotation
 
+        Rect insets = getInsets(fraction);
         mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
-                mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction)
-                .round(tx, mLeash, isInPipDirection())
-                .shadow(tx, mLeash, isInPipDirection());
+                        mBaseBounds, mAnimatedRect, insets, false /* isInPipDirection */, fraction)
+                .round(tx, mLeash, false /* applyCornerRadius */)
+                .shadow(tx, mLeash, false /* applyCornerRadius */);
         tx.apply();
     }
 
     private Rect getInsets(float fraction) {
-        Rect startInsets = isInPipDirection() ? mZeroInsets : mSourceRectHintInsets;
-        Rect endInsets = isInPipDirection() ? mSourceRectHintInsets : mZeroInsets;
-
+        final Rect startInsets = mSourceRectHintInsets;
+        final Rect endInsets = mZeroInsets;
         return mInsetEvaluator.evaluate(fraction, startInsets, endInsets);
     }
 
-    private boolean isInPipDirection() {
-        return mDirection == BOUNDS_ENTER;
-    }
-
-    private boolean isOutPipDirection() {
-        return mDirection == BOUNDS_EXIT;
-    }
-
     // no-ops
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 73be8db..0427294 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -292,23 +292,34 @@
         setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
 
         if (!mPipTransitionState.isInPip()) {
+            // Skip the PiP-relevant updates if we aren't in a valid PiP state.
+            if (mPipTransitionState.isInFixedRotation()) {
+                ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                        "Fixed rotation flag shouldn't be set while in an invalid PiP state");
+            }
             return;
         }
 
         mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
 
-        // Update the caches to reflect the new display layout in the movement bounds;
-        // temporarily update bounds to be at the top left for the movement bounds calculation.
-        Rect toBounds = new Rect(0, 0,
-                (int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale),
-                (int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale));
-        mPipBoundsState.setBounds(toBounds);
-        mPipTouchHandler.updateMovementBounds();
-
-        // The policy is to keep PiP snap fraction invariant.
-        mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
-        mPipBoundsState.setBounds(toBounds);
-        t.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+        if (mPipTransitionState.isInFixedRotation()) {
+            // Do not change the bounds when in fixed rotation, but do update the movement bounds
+            // based on the current bounds state and potentially new display layout.
+            mPipTouchHandler.updateMovementBounds();
+            mPipTransitionState.setInFixedRotation(false);
+        } else {
+            Rect toBounds = new Rect(0, 0,
+                    (int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale),
+                    (int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale));
+            // Update the caches to reflect the new display layout in the movement bounds;
+            // temporarily update bounds to be at the top left for the movement bounds calculation.
+            mPipBoundsState.setBounds(toBounds);
+            mPipTouchHandler.updateMovementBounds();
+            // The policy is to keep PiP snap fraction invariant.
+            mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
+            mPipBoundsState.setBounds(toBounds);
+        }
+        t.setBounds(mPipTransitionState.mPipTaskToken, mPipBoundsState.getBounds());
     }
 
     private void setDisplayLayout(DisplayLayout layout) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 268c3a2..810eff8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -350,7 +350,7 @@
         }
         cancelPhysicsAnimation();
         mMenuController.hideMenu(ANIM_TYPE_DISMISS, false /* resize */);
-        // mPipTaskOrganizer.removePip();
+        mPipScheduler.removePipAfterAnimation();
     }
 
     /** Sets the movement bounds to use to constrain PIP position animations. */
@@ -776,6 +776,10 @@
                 cancelPhysicsAnimation();
                 settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
                 break;
+            case PipTransitionState.CHANGED_PIP_BOUNDS:
+                // Check whether changed bounds imply we need to update stash state too.
+                stashEndActionIfNeeded();
+                break;
         }
     }
 
@@ -829,9 +833,6 @@
         mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
         mSpringingToTouch = false;
         mDismissalPending = false;
-
-        // Check whether new bounds after fling imply we need to update stash state too.
-        stashEndActionIfNeeded();
     }
 
     private void stashEndActionIfNeeded() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index d4f190e..fbbf6f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -19,81 +19,40 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsState;
-import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Scheduler for Shell initiated PiP transitions and animations.
  */
 public class PipScheduler {
     private static final String TAG = PipScheduler.class.getSimpleName();
-    private static final String BROADCAST_FILTER = PipScheduler.class.getCanonicalName();
 
     private final Context mContext;
     private final PipBoundsState mPipBoundsState;
     private final ShellExecutor mMainExecutor;
     private final PipTransitionState mPipTransitionState;
-    private PipSchedulerReceiver mSchedulerReceiver;
     private PipTransitionController mPipTransitionController;
+    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+            mSurfaceControlTransactionFactory;
 
     @Nullable private Runnable mUpdateMovementBoundsRunnable;
 
-    /**
-     * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell.
-     * This is used for a broadcast receiver to resolve intents. This should be removed once
-     * there is an equivalent of PipTouchHandler and PipResizeGestureHandler for PiP2.
-     */
-    private static final int PIP_EXIT_VIA_EXPAND_CODE = 0;
-    private static final int PIP_DOUBLE_TAP = 1;
-
-    @IntDef(value = {
-            PIP_EXIT_VIA_EXPAND_CODE,
-            PIP_DOUBLE_TAP
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface PipUserJourneyCode {}
-
-    /**
-     * A temporary broadcast receiver to initiate PiP CUJs.
-     */
-    private class PipSchedulerReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            int userJourneyCode = intent.getIntExtra("cuj_code_extra", 0);
-            switch (userJourneyCode) {
-                case PIP_EXIT_VIA_EXPAND_CODE:
-                    scheduleExitPipViaExpand();
-                    break;
-                case PIP_DOUBLE_TAP:
-                    scheduleDoubleTapToResize();
-                    break;
-                default:
-                    throw new IllegalStateException("unexpected CUJ code=" + userJourneyCode);
-            }
-        }
-    }
-
     public PipScheduler(Context context,
             PipBoundsState pipBoundsState,
             ShellExecutor mainExecutor,
@@ -103,12 +62,8 @@
         mMainExecutor = mainExecutor;
         mPipTransitionState = pipTransitionState;
 
-        if (PipUtils.isPip2ExperimentEnabled()) {
-            // temporary broadcast receiver to initiate exit PiP via expand
-            mSchedulerReceiver = new PipSchedulerReceiver();
-            ContextCompat.registerReceiver(mContext, mSchedulerReceiver,
-                    new IntentFilter(BROADCAST_FILTER), ContextCompat.RECEIVER_EXPORTED);
-        }
+        mSurfaceControlTransactionFactory =
+                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
     }
 
     ShellExecutor getMainExecutor() {
@@ -133,6 +88,18 @@
         return wct;
     }
 
+    @Nullable
+    private WindowContainerTransaction getRemovePipTransaction() {
+        if (mPipTransitionState.mPipTaskToken == null) {
+            return null;
+        }
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setBounds(mPipTransitionState.mPipTaskToken, null);
+        wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED);
+        wct.reorder(mPipTransitionState.mPipTaskToken, false);
+        return wct;
+    }
+
     /**
      * Schedules exit PiP via expand transition.
      */
@@ -146,10 +113,26 @@
         }
     }
 
-    /**
-     * Schedules resize PiP via double tap.
-     */
-    public void scheduleDoubleTapToResize() {}
+    // TODO: Optimize this by running the animation as part of the transition
+    /** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */
+    public void removePipAfterAnimation() {
+        SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+        PipAlphaAnimator animator = new PipAlphaAnimator(mContext,
+                mPipTransitionState.mPinnedTaskLeash, tx, PipAlphaAnimator.FADE_OUT);
+        animator.setAnimationEndCallback(this::scheduleRemovePipImmediately);
+        animator.start();
+    }
+
+    /** Schedules remove PiP transition. */
+    private void scheduleRemovePipImmediately() {
+        WindowContainerTransaction wct = getRemovePipTransaction();
+        if (wct != null) {
+            mMainExecutor.execute(() -> {
+                mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
+                        null /* destinationBounds */);
+            });
+        }
+    }
 
     /**
      * Animates resizing of the pinned stack given the duration.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index c58de2c..373ec80 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -90,9 +90,10 @@
         if (mPictureInPictureParams.equals(params)) {
             return;
         }
-        if (PipUtils.remoteActionsChanged(params.getActions(), mPictureInPictureParams.getActions())
+        if (params != null && (PipUtils.remoteActionsChanged(params.getActions(),
+                mPictureInPictureParams.getActions())
                 || !PipUtils.remoteActionsMatch(params.getCloseAction(),
-                mPictureInPictureParams.getCloseAction())) {
+                mPictureInPictureParams.getCloseAction()))) {
             for (PipParamsChangedCallback listener : mPipParamsChangedListeners) {
                 listener.onActionsChanged(params.getActions(), params.getCloseAction());
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 029f001..19d293e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -406,12 +406,9 @@
         // We need to remove the callback even if the shelf is visible, in case it the delayed
         // callback hasn't been executed yet to avoid the wrong final state.
         mMainExecutor.removeCallbacks(mMoveOnShelVisibilityChanged);
-        if (shelfVisible) {
-            mMoveOnShelVisibilityChanged.run();
-        } else {
-            // Postpone moving in response to hide of Launcher in case there's another change
-            mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY);
-        }
+
+        // Postpone moving in response to hide of Launcher in case there's another change
+        mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY);
     }
 
     /**
@@ -582,7 +579,6 @@
             return true;
         }
 
-        /*
         if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
                 && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
             // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
@@ -599,6 +595,7 @@
             return true;
         }
 
+        /*
         // Ignore the motion event When the entry animation is waiting to be started
         if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) {
             ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE,
@@ -1016,21 +1013,6 @@
             return true;
         }
 
-        private void stashEndAction() {
-            if (mPipBoundsState.getBounds().left < 0
-                    && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT) {
-                mPipUiEventLogger.log(
-                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_LEFT);
-                mPipBoundsState.setStashed(STASH_TYPE_LEFT);
-            } else if (mPipBoundsState.getBounds().left >= 0
-                    && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) {
-                mPipUiEventLogger.log(
-                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_RIGHT);
-                mPipBoundsState.setStashed(STASH_TYPE_RIGHT);
-            }
-            mMenuController.hideMenu();
-        }
-
         private void flingEndAction() {
             if (mShouldHideMenuAfterFling) {
                 // If the menu is not visible, then we can still be showing the activity for the
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 62a60fa..b286211 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -16,7 +16,9 @@
 
 package com.android.wm.shell.pip2.phone;
 
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OPEN;
@@ -25,6 +27,7 @@
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP;
 
 import android.animation.Animator;
@@ -34,7 +37,7 @@
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.content.Context;
-import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -56,7 +59,8 @@
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
-import com.android.wm.shell.pip2.animation.PipEnterExitAnimator;
+import com.android.wm.shell.pip2.animation.PipEnterAnimator;
+import com.android.wm.shell.pip2.animation.PipExpandAnimator;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.shared.pip.PipContentOverlay;
 import com.android.wm.shell.sysui.ShellInit;
@@ -218,6 +222,7 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        mFinishCallback = finishCallback;
         if (transition == mEnterTransition || info.getType() == TRANSIT_PIP) {
             mEnterTransition = null;
             // If we are in swipe PiP to Home transition we are ENTERING_PIP as a jumpcut transition
@@ -258,6 +263,7 @@
         if (isRemovePipTransition(info)) {
             return removePipImmediately(info, startTransaction, finishTransaction, finishCallback);
         }
+        mFinishCallback = null;
         return false;
     }
 
@@ -297,7 +303,6 @@
             mBoundsChangeDuration = BOUNDS_CHANGE_JUMPCUT_DURATION;
         }
 
-        mFinishCallback = finishCallback;
         mPipTransitionState.setState(PipTransitionState.CHANGING_PIP_BOUNDS, extra);
         return true;
     }
@@ -310,6 +315,14 @@
         if (pipChange == null) {
             return false;
         }
+
+        // We expect the PiP activity as a separate change in a config-at-end transition.
+        TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
+                pipChange.getTaskInfo().getToken());
+        if (pipActivityChange == null) {
+            return false;
+        }
+
         SurfaceControl pipLeash = pipChange.getLeash();
         Preconditions.checkNotNull(pipLeash, "Leash is null for swipe-up transition.");
 
@@ -327,29 +340,28 @@
                             (destinationBounds.width() - overlaySize) / 2f,
                             (destinationBounds.height() - overlaySize) / 2f);
         }
-        startTransaction.merge(finishTransaction);
 
         final int startRotation = pipChange.getStartRotation();
         final int endRotation = mPipDisplayLayoutState.getRotation();
-        if (endRotation != startRotation) {
-            boolean isClockwise = (endRotation - startRotation) == -ROTATION_270;
-
-            // Display bounds were already updated to represent the final orientation,
-            // so we just need to readjust the origin, and perform rotation about (0, 0).
-            Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
-            int originTranslateX = isClockwise ? 0 : -displayBounds.width();
-            int originTranslateY = isClockwise ? -displayBounds.height() : 0;
-
-            Matrix transformTensor = new Matrix();
-            final float[] matrixTmp = new float[9];
-            transformTensor.setTranslate(originTranslateX + destinationBounds.left,
-                    originTranslateY + destinationBounds.top);
-            final float degrees = (endRotation - startRotation) * 90f;
-            transformTensor.postRotate(degrees);
-            startTransaction.setMatrix(pipLeash, transformTensor, matrixTmp);
+        final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
+                : startRotation - endRotation;
+        if (delta != ROTATION_0) {
+            mPipTransitionState.setInFixedRotation(true);
+            handleBoundsTypeFixedRotation(pipChange, pipActivityChange, endRotation);
         }
+
+        Rect sourceRectHint = null;
+        if (pipChange.getTaskInfo() != null
+                && pipChange.getTaskInfo().pictureInPictureParams != null) {
+            sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+        }
+
+        startTransaction.merge(finishTransaction);
+        PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
+                startTransaction, finishTransaction, destinationBounds, sourceRectHint, delta);
+        animator.setEnterStartState(pipChange, pipActivityChange);
+        animator.onEnterAnimationUpdate(1.0f /* fraction */, startTransaction);
         startTransaction.apply();
-        finishCallback.onTransitionFinished(null /* finishWct */);
         finishInner();
         return true;
     }
@@ -386,15 +398,13 @@
             return false;
         }
 
-        WindowContainerToken pipTaskToken = pipChange.getContainer();
-        if (pipTaskToken == null) {
+        // We expect the PiP activity as a separate change in a config-at-end transition.
+        TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
+                pipChange.getTaskInfo().getToken());
+        if (pipActivityChange == null) {
             return false;
         }
 
-        WindowContainerTransaction finishWct = new WindowContainerTransaction();
-        SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
-
-        Rect startBounds = pipChange.getStartAbsBounds();
         Rect endBounds = pipChange.getEndAbsBounds();
         SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
         Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
@@ -405,33 +415,74 @@
             sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
         }
 
-        // For opening type transitions, if there is a non-pip change of mode TO_FRONT/OPEN,
+        // For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
         // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
         // by the Transitions framework to simplify Task opening transitions.
         if (TransitionUtil.isOpeningType(info.getType())) {
             for (TransitionInfo.Change change : info.getChanges()) {
-                if (change.getLeash() == null || change == pipChange) continue;
+                if (change.getLeash() == null) continue;
                 if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
                     startTransaction.setAlpha(change.getLeash(), 1f);
                 }
             }
         }
 
-        PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
-                startTransaction, finishTransaction, startBounds, startBounds, endBounds,
-                sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0);
+        final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+        int startRotation = pipChange.getStartRotation();
+        int endRotation = fixedRotationChange != null
+                ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED;
+        final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
+                : startRotation - endRotation;
 
-        tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
-                this::finishInner);
-        finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
+        if (delta != ROTATION_0) {
+            mPipTransitionState.setInFixedRotation(true);
+            handleBoundsTypeFixedRotation(pipChange, pipActivityChange,
+                    fixedRotationChange.getEndFixedRotation());
+        }
 
-        animator.setAnimationEndCallback(() ->
-                finishCallback.onTransitionFinished(finishWct));
-
+        PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
+                startTransaction, finishTransaction, endBounds, sourceRectHint, delta);
+        animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange,
+                pipActivityChange));
+        animator.setAnimationEndCallback(this::finishInner);
         animator.start();
         return true;
     }
 
+    private void handleBoundsTypeFixedRotation(TransitionInfo.Change pipTaskChange,
+            TransitionInfo.Change pipActivityChange, int endRotation) {
+        final Rect endBounds = pipTaskChange.getEndAbsBounds();
+        final Rect endActivityBounds = pipActivityChange.getEndAbsBounds();
+        int startRotation = pipTaskChange.getStartRotation();
+
+        // Cache the task to activity offset to potentially restore later.
+        Point activityEndOffset = new Point(endActivityBounds.left - endBounds.left,
+                endActivityBounds.top - endBounds.top);
+
+        // If we are running a fixed rotation bounds enter PiP animation,
+        // then update the display layout rotation, and recalculate the end rotation bounds.
+        // Update the endBounds in place, so that the PiP change is up-to-date.
+        mPipDisplayLayoutState.rotateTo(endRotation);
+        float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
+                mPipBoundsAlgorithm.getEntryDestinationBounds());
+        mPipBoundsAlgorithm.applySnapFraction(endBounds, snapFraction);
+        mPipBoundsState.setBounds(endBounds);
+
+        // Display bounds were already updated to represent the final orientation,
+        // so we just need to readjust the origin, and perform rotation about (0, 0).
+        boolean isClockwise = (endRotation - startRotation) == -ROTATION_270;
+        Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
+        int originTranslateX = isClockwise ? 0 : -displayBounds.width();
+        int originTranslateY = isClockwise ? -displayBounds.height() : 0;
+        endBounds.offset(originTranslateX, originTranslateY);
+
+        // Update the activity end bounds in place as well, as this is used for transform
+        // calculation later.
+        endActivityBounds.offsetTo(endBounds.left + activityEndOffset.x,
+                endBounds.top + activityEndOffset.y);
+    }
+
+
     private boolean startAlphaTypeEnterAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
@@ -452,11 +503,8 @@
 
         PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
                 PipAlphaAnimator.FADE_IN);
-        animator.setAnimationEndCallback(() -> {
-            finishCallback.onTransitionFinished(null);
-            // This should update the pip transition state accordingly after we stop playing.
-            finishInner();
-        });
+        // This should update the pip transition state accordingly after we stop playing.
+        animator.setAnimationEndCallback(this::finishInner);
 
         animator.start();
         return true;
@@ -510,9 +558,9 @@
             sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
         }
 
-        PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
+        PipExpandAnimator animator = new PipExpandAnimator(mContext, pipLeash,
                 startTransaction, finishTransaction, endBounds, startBounds, endBounds,
-                sourceRectHint, PipEnterExitAnimator.BOUNDS_EXIT, Surface.ROTATION_0);
+                sourceRectHint, Surface.ROTATION_0);
 
         animator.setAnimationEndCallback(() -> {
             mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
@@ -549,6 +597,19 @@
     }
 
     @Nullable
+    private TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
+            @NonNull WindowContainerToken parent) {
+        for (TransitionInfo.Change change : info.getChanges()) {
+            if (change.getTaskInfo() == null
+                    && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
+                    && change.getParent() != null && change.getParent().equals(parent)) {
+                return change;
+            }
+        }
+        return null;
+    }
+
+    @Nullable
     private TransitionInfo.Change getChangeByToken(TransitionInfo info,
             WindowContainerToken token) {
         for (TransitionInfo.Change change : info.getChanges()) {
@@ -622,8 +683,11 @@
                 && pipChange.getMode() == TRANSIT_TO_BACK;
         boolean isPipClosed = info.getType() == TRANSIT_CLOSE
                 && pipChange.getMode() == TRANSIT_CLOSE;
-        // PiP is being removed if the pinned task is either moved to back or closed.
-        return isPipMovedToBack || isPipClosed;
+        // If PiP is dismissed by user (i.e. via dismiss button in PiP menu)
+        boolean isPipDismissed = info.getType() == TRANSIT_REMOVE_PIP
+                && pipChange.getMode() == TRANSIT_TO_BACK;
+        // PiP is being removed if the pinned task is either moved to back, closed, or dismissed.
+        return isPipMovedToBack || isPipClosed || isPipDismissed;
     }
 
     //
@@ -631,6 +695,7 @@
     //
 
     private void finishInner() {
+        finishTransition(null /* tx */);
         if (mPipTransitionState.getSwipePipToHomeOverlay() != null) {
             startOverlayFadeoutAnimation();
         } else if (mPipTransitionState.getState() == PipTransitionState.ENTERING_PIP) {
@@ -652,6 +717,7 @@
         }
         if (mFinishCallback != null) {
             mFinishCallback.onTransitionFinished(wct);
+            mFinishCallback = null;
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index a132796f..ccdd66b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -155,6 +155,8 @@
     @Nullable
     private Runnable mOnIdlePipTransitionStateRunnable;
 
+    private boolean mInFixedRotation = false;
+
     /**
      * An interface to track state updates as we progress through PiP transitions.
      */
@@ -256,7 +258,7 @@
 
     private void maybeRunOnIdlePipTransitionStateCallback() {
         if (mOnIdlePipTransitionStateRunnable != null && isPipStateIdle()) {
-            mOnIdlePipTransitionStateRunnable.run();
+            mMainHandler.post(mOnIdlePipTransitionStateRunnable);
             mOnIdlePipTransitionStateRunnable = null;
         }
     }
@@ -303,6 +305,23 @@
     }
 
     /**
+     * @return true if either in swipe or button-nav fixed rotation.
+     */
+    public boolean isInFixedRotation() {
+        return mInFixedRotation;
+    }
+
+    /**
+     * Sets the fixed rotation flag.
+     */
+    public void setInFixedRotation(boolean inFixedRotation) {
+        mInFixedRotation = inFixedRotation;
+        if (!inFixedRotation) {
+            maybeRunOnIdlePipTransitionStateCallback();
+        }
+    }
+
+    /**
      * @return true if in swipe PiP to home. Note that this is true until overlay fades if used too.
      */
     public boolean isInSwipePipToHomeTransition() {
@@ -351,7 +370,7 @@
 
     public boolean isPipStateIdle() {
         // This needs to be a valid in-PiP state that isn't a transient state.
-        return mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS;
+        return (mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS) && !isInFixedRotation();
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index a0b7a29..6086801 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -38,8 +38,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.window.DesktopModeFlags;
 import android.window.WindowContainerToken;
-import android.window.flags.DesktopModeFlags;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.NonNull;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
index 0cbbb71..1af99f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -21,7 +21,7 @@
 import android.util.ArrayMap
 import android.view.SurfaceControl
 import android.window.TransitionInfo
-import android.window.flags.DesktopModeFlags
+import android.window.DesktopModeFlags
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 59aa792..cf2c3da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -55,11 +55,6 @@
     oneway void unregisterSplitSelectListener(in ISplitSelectListener listener) = 21;
 
     /**
-     * Removes a task from the side stage.
-     */
-    oneway void removeFromSideStage(int taskId) = 4;
-
-    /**
      * Removes the split-screen stages and leaving indicated task to top. Passing INVALID_TASK_ID
      * to indicate leaving no top task after leaving split-screen.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 87b661d..a23b576 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -322,6 +322,22 @@
         return mTaskOrganizer.getRunningTaskInfo(taskId);
     }
 
+    /**
+     * @return an Array of RunningTaskInfo's ordered by leftToRight or topTopBottom
+     */
+    @Nullable
+    public ActivityManager.RunningTaskInfo[] getAllTaskInfos() {
+        // TODO(b/349828130) Add the third stage task info and not rely on positions
+        ActivityManager.RunningTaskInfo topLeftTask = getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+        ActivityManager.RunningTaskInfo bottomRightTask =
+                getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+        if (topLeftTask != null && bottomRightTask != null) {
+            return new ActivityManager.RunningTaskInfo[]{topLeftTask, bottomRightTask};
+        }
+
+        return null;
+    }
+
     /** Check task is under split or not by taskId. */
     public boolean isTaskInSplitScreen(int taskId) {
         return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
@@ -378,10 +394,6 @@
         return mStageCoordinator.moveToStage(task, stagePosition, wct);
     }
 
-    public boolean removeFromSideStage(int taskId) {
-        return mStageCoordinator.removeFromSideStage(taskId);
-    }
-
     public void setSideStagePosition(@SplitPosition int sideStagePosition) {
         mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
     }
@@ -760,15 +772,25 @@
                 instanceId);
     }
 
-    /**
-     * Starts the given intent into split.
-     * @param hideTaskToken If non-null, a task matching this token will be moved to back in the
-     *                      same window container transaction as the starting of the intent.
-     */
     @Override
     public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
             @SplitPosition int position, @Nullable Bundle options,
             @Nullable WindowContainerToken hideTaskToken) {
+        startIntent(intent, userId1, fillInIntent, position, options, hideTaskToken,
+                false /* forceLaunchNewTask */);
+    }
+
+    /**
+     * Starts the given intent into split.
+     *
+     * @param hideTaskToken If non-null, a task matching this token will be moved to back in the
+     *                      same window container transaction as the starting of the intent.
+     * @param forceLaunchNewTask If true, this method will skip the check for a background task
+     *                           matching the intent and launch a new task.
+     */
+    public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
+            @SplitPosition int position, @Nullable Bundle options,
+            @Nullable WindowContainerToken hideTaskToken, boolean forceLaunchNewTask) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                 "startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1,
                 fillInIntent, position);
@@ -786,8 +808,9 @@
         // To prevent accumulating large number of instances in the background, reuse task
         // in the background. If we don't explicitly reuse, new may be created even if the app
         // isn't multi-instance because WM won't automatically remove/reuse the previous instance
-        final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
-                .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1,
+        final ActivityManager.RecentTaskInfo taskInfo = forceLaunchNewTask ? null :
+                mRecentTasksOptional
+                        .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1,
                         hideTaskToken))
                 .orElse(null);
         if (taskInfo != null) {
@@ -1177,12 +1200,6 @@
         }
 
         @Override
-        public void removeFromSideStage(int taskId) {
-            executeRemoteCallWithTaskPermission(mController, "removeFromSideStage",
-                    (controller) -> controller.removeFromSideStage(taskId));
-        }
-
-        @Override
         public void startTask(int taskId, int position, @Nullable Bundle options) {
             executeRemoteCallWithTaskPermission(mController, "startTask",
                     (controller) -> controller.startTask(taskId, position, options,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
index e1b474d..a016a84 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
@@ -40,8 +40,6 @@
         switch (args[0]) {
             case "moveToSideStage":
                 return runMoveToSideStage(args, pw);
-            case "removeFromSideStage":
-                return runRemoveFromSideStage(args, pw);
             case "setSideStagePosition":
                 return runSetSideStagePosition(args, pw);
             case "switchSplitPosition":
@@ -67,17 +65,6 @@
         return true;
     }
 
-    private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
-        if (args.length < 2) {
-            // First argument is the action name.
-            pw.println("Error: task id should be provided as arguments");
-            return false;
-        }
-        final int taskId = new Integer(args[1]);
-        mController.removeFromSideStage(taskId);
-        return true;
-    }
-
     private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
         if (args.length < 2) {
             // First argument is the action name.
@@ -109,8 +96,6 @@
     public void printShellCommandHelp(PrintWriter pw, String prefix) {
         pw.println(prefix + "moveToSideStage <taskId> <SideStagePosition>");
         pw.println(prefix + "  Move a task with given id in split-screen mode.");
-        pw.println(prefix + "removeFromSideStage <taskId>");
-        pw.println(prefix + "  Remove a task with given id in split-screen mode.");
         pw.println(prefix + "setSideStagePosition <SideStagePosition>");
         pw.println(prefix + "  Sets the position of the side-stage.");
         pw.println(prefix + "switchSplitPosition");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e527c02..3e76403 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -35,12 +35,19 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
 
 import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
+import static com.android.wm.shell.common.split.SplitScreenUtils.isPartiallyOffscreen;
 import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
 import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
+import static com.android.wm.shell.shared.TransitionUtil.isOrderOnly;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -117,6 +124,7 @@
 import com.android.internal.policy.FoldLockSettingsObserver;
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
@@ -160,19 +168,19 @@
  * - The {@link SplitLayout} divider is only visible if multiple {@link StageTaskListener}s are
  * visible
  * - Both stages are put under a single-top root task.
- * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
- * {@link #onStageHasChildrenChanged(StageListenerImpl).}
  */
 public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
         DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler,
-        ShellTaskOrganizer.TaskListener {
+        ShellTaskOrganizer.TaskListener, StageTaskListener.StageListenerCallbacks {
 
     private static final String TAG = StageCoordinator.class.getSimpleName();
 
+    // The duration in ms to prevent launch-adjacent from working after split screen is first
+    // entered
+    private static final int DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS = 1000;
+
     private final StageTaskListener mMainStage;
-    private final StageListenerImpl mMainStageListener = new StageListenerImpl();
     private final StageTaskListener mSideStage;
-    private final StageListenerImpl mSideStageListener = new StageListenerImpl();
     @SplitPosition
     private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
 
@@ -235,6 +243,10 @@
     private SplitScreen.SplitInvocationListener mSplitInvocationListener;
     private Executor mSplitInvocationListenerExecutor;
 
+    // Re-enables launch-adjacent handling on the split root task.  This needs to be a member
+    // because we will be posting and removing it from the handler.
+    private final Runnable mReEnableLaunchAdjacentOnRoot = () -> setLaunchAdjacentDisabled(false);
+
     /**
      * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
      * CompatUI layouts. CompatUI is handled separately by MainStage and SideStage.
@@ -328,7 +340,7 @@
                 mContext,
                 mTaskOrganizer,
                 mDisplayId,
-                mMainStageListener,
+                this /*stageListenerCallbacks*/,
                 mSyncQueue,
                 iconProvider,
                 mWindowDecorViewModel);
@@ -336,7 +348,7 @@
                 mContext,
                 mTaskOrganizer,
                 mDisplayId,
-                mSideStageListener,
+                this /*stageListenerCallbacks*/,
                 mSyncQueue,
                 iconProvider,
                 mWindowDecorViewModel);
@@ -411,7 +423,7 @@
     }
 
     public boolean isSplitScreenVisible() {
-        return mSideStageListener.mVisible && mMainStageListener.mVisible;
+        return mSideStage.mVisible && mMainStage.mVisible;
     }
 
     private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) {
@@ -499,21 +511,6 @@
         return true;
     }
 
-    boolean removeFromSideStage(int taskId) {
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
-
-
-        // MainStage will be deactivated in onStageHasChildrenChanged() if the other stages
-        // no longer have children.
-
-        final boolean result = mSideStage.removeTask(taskId,
-                isSplitActive() ? mMainStage.mRootTaskInfo.token : null,
-                wct);
-        mTaskOrganizer.applyTransaction(wct);
-        return result;
-    }
-
     SplitscreenEventLogger getLogger() {
         return mLogger;
     }
@@ -1111,7 +1108,7 @@
         mSideStagePosition = sideStagePosition;
         sendOnStagePositionChanged();
 
-        if (mSideStageListener.mVisible && updateBounds) {
+        if (mSideStage.mVisible && updateBounds) {
             if (wct == null) {
                 // onLayoutChanged builds/applies a wct with the contents of updateWindowBounds.
                 onLayoutSizeChanged(mSplitLayout);
@@ -1332,8 +1329,8 @@
 
     private void clearRequestIfPresented() {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
-        if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
-                && mMainStageListener.mVisible && mSideStageListener.mHasChildren) {
+        if (mSideStage.mVisible && mSideStage.mHasChildren
+                && mMainStage.mVisible && mSideStage.mHasChildren) {
             mSplitRequest = null;
         }
     }
@@ -1586,11 +1583,12 @@
         }
     }
 
-    private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId,
+    @Override
+    public void onChildTaskStatusChanged(StageTaskListener stageListener, int taskId,
             boolean present, boolean visible) {
         int stage;
         if (present) {
-            stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+            stage = stageListener == mSideStage ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
         } else {
             // No longer on any stage
             stage = STAGE_TYPE_UNDEFINED;
@@ -1721,13 +1719,14 @@
 
 
     @VisibleForTesting
-    void onRootTaskAppeared() {
+    @Override
+    public void onRootTaskAppeared() {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
-                mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask);
+                mRootTaskInfo, mMainStage.mHasRootTask, mSideStage.mHasRootTask);
         // Wait unit all root tasks appeared.
         if (mRootTaskInfo == null
-                || !mMainStageListener.mHasRootTask
-                || !mSideStageListener.mHasRootTask) {
+                || !mMainStage.mHasRootTask
+                || !mSideStage.mHasRootTask) {
             return;
         }
 
@@ -1747,35 +1746,8 @@
         mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token);
     }
 
-    /** Callback when split roots have child task appeared under it, this is a little different from
-     * #onStageHasChildrenChanged because this would be called every time child task appeared.
-     * NOTICE: This only be called on legacy transition. */
-    private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d",
-                stageListener == mMainStageListener, taskId);
-        // Handle entering split screen while there is a split pair running in the background.
-        if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
-                && mSplitRequest == null) {
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            prepareEnterSplitScreen(wct);
-            mMainStage.evictAllChildren(wct);
-            mSideStage.evictOtherChildren(wct, taskId);
-
-            mSyncQueue.queue(wct);
-            mSyncQueue.runInSync(t -> {
-                if (mIsDropEntering) {
-                    updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
-                    mIsDropEntering = false;
-                    mSkipEvictingMainStageChildren = false;
-                } else {
-                    mShowDecorImmediately = true;
-                    mSplitLayout.flingDividerToCenter(/*finishCallback*/ null);
-                }
-            });
-        }
-    }
-
-    private void onRootTaskVanished() {
+    @Override
+    public void onRootTaskVanished() {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished");
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mLaunchAdjacentController.clearLaunchAdjacentRoot();
@@ -1792,15 +1764,16 @@
 
     /** Callback when split roots visiblility changed.
      * NOTICE: This only be called on legacy transition. */
-    private void onStageVisibilityChanged(StageListenerImpl stageListener) {
+    @Override
+    public void onStageVisibilityChanged(StageTaskListener stageListener) {
         // If split didn't active, just ignore this callback because we should already did these
         // on #applyExitSplitScreen.
         if (!isSplitActive()) {
             return;
         }
 
-        final boolean sideStageVisible = mSideStageListener.mVisible;
-        final boolean mainStageVisible = mMainStageListener.mVisible;
+        final boolean sideStageVisible = mSideStage.mVisible;
+        final boolean mainStageVisible = mMainStage.mVisible;
 
         // Wait for both stages having the same visibility to prevent causing flicker.
         if (mainStageVisible != sideStageVisible) {
@@ -1937,18 +1910,19 @@
 
     /** Callback when split roots have child or haven't under it.
      * NOTICE: This only be called on legacy transition. */
-    private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
+    @Override
+    public void onStageHasChildrenChanged(StageTaskListener stageListener) {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b",
-                stageListener == mMainStageListener);
+                stageListener == mMainStage);
         final boolean hasChildren = stageListener.mHasChildren;
-        final boolean isSideStage = stageListener == mSideStageListener;
+        final boolean isSideStage = stageListener == mSideStage;
         if (!hasChildren && !mIsExiting && isSplitActive()) {
-            if (isSideStage && mMainStageListener.mVisible) {
+            if (isSideStage && mMainStage.mVisible) {
                 // Exit to main stage if side stage no longer has children.
                 mSplitLayout.flingDividerToDismiss(
                         mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT,
                         EXIT_REASON_APP_FINISHED);
-            } else if (!isSideStage && mSideStageListener.mVisible) {
+            } else if (!isSideStage && mSideStage.mVisible) {
                 // Exit to side stage if main stage no longer has children.
                 mSplitLayout.flingDividerToDismiss(
                         mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
@@ -1973,7 +1947,7 @@
                 }
             });
         }
-        if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
+        if (mMainStage.mHasChildren && mSideStage.mHasChildren) {
             mShouldUpdateRecents = true;
             clearRequestIfPresented();
             updateRecentTasksSplitPair();
@@ -1989,6 +1963,35 @@
     }
 
     @Override
+    public void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener,
+            ActivityManager.RunningTaskInfo taskInfo) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
+        if (isSplitActive()) {
+            final boolean isMainStage = mMainStage == stageTaskListener;
+
+            // If visible, we preserve the app and keep it running. If an app becomes
+            // unsupported in the bg, break split without putting anything on top
+            boolean splitScreenVisible = isSplitScreenVisible();
+            int stageType = STAGE_TYPE_UNDEFINED;
+            if (splitScreenVisible) {
+                stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+            }
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            prepareExitSplitScreen(stageType, wct);
+            clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+            mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
+                    EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+            Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow",
+                    "app package " + taskInfo.baseIntent.getComponent()
+                            + " does not support splitscreen, or is a controlled activity"
+                            + " type"));
+            if (splitScreenVisible) {
+                handleUnsupportedSplitStart();
+            }
+        }
+    }
+
+    @Override
     public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s",
                 bottomOrRight, exitReasonToString(exitReason));
@@ -2060,6 +2063,13 @@
             mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish");
         }, mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager());
 
+        if (Flags.enableFlexibleTwoAppSplit()) {
+            switch (layout.calculateCurrentSnapPosition()) {
+                case SNAP_TO_2_10_90 -> grantFocusToPosition(false /* leftOrTop */);
+                case SNAP_TO_2_90_10 -> grantFocusToPosition(true /* leftOrTop */);
+            }
+        }
+
         mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
     }
 
@@ -2513,6 +2523,26 @@
                         mTaskOrganizer.applyTransaction(wct);
                     }
                     continue;
+                } else if (Flags.enableFlexibleTwoAppSplit() && isOrderOnly(change)) {
+                    int focusedStageIndex = SPLIT_INDEX_UNDEFINED;
+                    if (taskInfo.token.equals(mMainStage.mRootTaskInfo.token)) {
+                        focusedStageIndex = mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+                                ? SPLIT_INDEX_0 : SPLIT_INDEX_1;
+                    } else if (taskInfo.token.equals(mSideStage.mRootTaskInfo.token)) {
+                        focusedStageIndex = mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+                                ? SPLIT_INDEX_1 : SPLIT_INDEX_0;
+                    }
+
+                    if (focusedStageIndex != SPLIT_INDEX_UNDEFINED) {
+                        @PersistentSnapPosition int currentSnapPosition =
+                                mSplitLayout.calculateCurrentSnapPosition();
+                        boolean offscreenTaskFocused =
+                                isPartiallyOffscreen(focusedStageIndex, currentSnapPosition);
+
+                        if (offscreenTaskFocused) {
+                            mSplitLayout.flingDividerToOtherSide(currentSnapPosition);
+                        }
+                    }
                 }
                 final StageTaskListener stage = getStageOfTask(taskInfo);
                 if (stage == null) {
@@ -2677,6 +2707,16 @@
         }
     }
 
+    /**
+     * Sets whether launch-adjacent is disabled or enabled.
+     */
+    private void setLaunchAdjacentDisabled(boolean disabled) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLaunchAdjacentDisabled: disabled=%b", disabled);
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setDisableLaunchAdjacent(mRootTaskInfo.token, disabled);
+        mTaskOrganizer.applyTransaction(wct);
+    }
+
     /** Starts the pending transition animation. */
     public boolean startPendingAnimation(@NonNull IBinder transition,
             @NonNull TransitionInfo info,
@@ -2689,6 +2729,14 @@
         if (mSplitTransitions.isPendingEnter(transition)) {
             shouldAnimate = startPendingEnterAnimation(transition,
                     mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction);
+
+            // Disable launch adjacent after an enter animation to prevent cases where apps are
+            // incorrectly trampolining and incorrectly triggering a double launch-adjacent task
+            // launch (ie. main -> split -> main). See b/344216031
+            setLaunchAdjacentDisabled(true);
+            mMainHandler.removeCallbacks(mReEnableLaunchAdjacentOnRoot);
+            mMainHandler.postDelayed(mReEnableLaunchAdjacentOnRoot,
+                    DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS);
         } else if (mSplitTransitions.isPendingDismiss(transition)) {
             final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss;
             shouldAnimate = startPendingDismissAnimation(
@@ -3197,13 +3245,9 @@
         pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition()));
         pw.println(childPrefix + "isActive=" + isSplitActive());
         mMainStage.dump(pw, childPrefix);
-        pw.println(innerPrefix + "MainStageListener");
-        mMainStageListener.dump(pw, childPrefix);
         pw.println(innerPrefix + "SideStage");
         pw.println(childPrefix + "stagePosition=" + splitPositionToString(getSideStagePosition()));
         mSideStage.dump(pw, childPrefix);
-        pw.println(innerPrefix + "SideStageListener");
-        mSideStageListener.dump(pw, childPrefix);
         if (mSplitLayout != null) {
             mSplitLayout.dump(pw, childPrefix);
         }
@@ -3219,8 +3263,8 @@
      */
     private void setSplitsVisible(boolean visible) {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible);
-        mMainStageListener.mVisible = mSideStageListener.mVisible = visible;
-        mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
+        mMainStage.mVisible = mSideStage.mVisible = visible;
+        mMainStage.mHasChildren = mSideStage.mHasChildren = visible;
     }
 
     /**
@@ -3270,86 +3314,4 @@
                 !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */,
                 mSplitLayout.isLeftRightSplit());
     }
-
-    class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
-        boolean mHasRootTask = false;
-        boolean mVisible = false;
-        boolean mHasChildren = false;
-
-        @Override
-        public void onRootTaskAppeared() {
-            mHasRootTask = true;
-            StageCoordinator.this.onRootTaskAppeared();
-        }
-
-        @Override
-        public void onChildTaskAppeared(int taskId) {
-            StageCoordinator.this.onChildTaskAppeared(this, taskId);
-        }
-
-        @Override
-        public void onStatusChanged(boolean visible, boolean hasChildren) {
-            if (!mHasRootTask) return;
-
-            if (mHasChildren != hasChildren) {
-                mHasChildren = hasChildren;
-                StageCoordinator.this.onStageHasChildrenChanged(this);
-            }
-            if (mVisible != visible) {
-                mVisible = visible;
-                StageCoordinator.this.onStageVisibilityChanged(this);
-            }
-        }
-
-        @Override
-        public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) {
-            StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible);
-        }
-
-        @Override
-        public void onRootTaskVanished() {
-            reset();
-            StageCoordinator.this.onRootTaskVanished();
-        }
-
-        @Override
-        public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
-            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
-            if (isSplitActive()) {
-                final boolean isMainStage = mMainStageListener == this;
-
-                // If visible, we preserve the app and keep it running. If an app becomes
-                // unsupported in the bg, break split without putting anything on top
-                boolean splitScreenVisible = isSplitScreenVisible();
-                int stageType = STAGE_TYPE_UNDEFINED;
-                if (splitScreenVisible) {
-                    stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
-                }
-                final WindowContainerTransaction wct = new WindowContainerTransaction();
-                prepareExitSplitScreen(stageType, wct);
-                clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
-                mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
-                        EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
-                Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow",
-                        "app package " + taskInfo.baseIntent.getComponent()
-                                + " does not support splitscreen, or is a controlled activity"
-                                + " type"));
-                if (splitScreenVisible) {
-                    handleUnsupportedSplitStart();
-                }
-            }
-        }
-
-        private void reset() {
-            mHasRootTask = false;
-            mVisible = false;
-            mHasChildren = false;
-        }
-
-        public void dump(@NonNull PrintWriter pw, String prefix) {
-            pw.println(prefix + "mHasRootTask=" + mHasRootTask);
-            pw.println(prefix + "mVisible=" + mVisible);
-            pw.println(prefix + "mHasChildren=" + mHasChildren);
-        }
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index d64c0a2..6313231 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -22,20 +22,17 @@
 import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
 
 import android.annotation.CallSuper;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -74,20 +71,21 @@
     // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the
     // stages should have this be set/being used
     private boolean mIsActive;
-
     /** Callback interface for listening to changes in a split-screen stage. */
     public interface StageListenerCallbacks {
         void onRootTaskAppeared();
 
-        void onChildTaskAppeared(int taskId);
+        void onStageHasChildrenChanged(StageTaskListener stageTaskListener);
 
-        void onStatusChanged(boolean visible, boolean hasChildren);
+        void onStageVisibilityChanged(StageTaskListener stageTaskListener);
 
-        void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
+        void onChildTaskStatusChanged(StageTaskListener stage, int taskId, boolean present,
+                boolean visible);
 
         void onRootTaskVanished();
 
-        void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo);
+        void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener,
+                ActivityManager.RunningTaskInfo taskInfo);
     }
 
     private final Context mContext;
@@ -96,6 +94,12 @@
     private final IconProvider mIconProvider;
     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
 
+    /** Whether or not the root task has been created. */
+    boolean mHasRootTask = false;
+    /** Whether or not the root task is visible. */
+    boolean mVisible = false;
+    /** Whether or not the root task has any children or not. */
+    boolean mHasChildren = false;
     protected ActivityManager.RunningTaskInfo mRootTaskInfo;
     protected SurfaceControl mRootLeash;
     protected SurfaceControl mDimLayer;
@@ -201,6 +205,7 @@
             mSplitDecorManager = new SplitDecorManager(
                     mRootTaskInfo.configuration,
                     mIconProvider);
+            mHasRootTask = true;
             mCallbacks.onRootTaskAppeared();
             sendStatusChanged();
             mSyncQueue.runInSync(t -> mDimLayer =
@@ -209,15 +214,8 @@
             final int taskId = taskInfo.taskId;
             mChildrenLeashes.put(taskId, leash);
             mChildrenTaskInfo.put(taskId, taskInfo);
-            mCallbacks.onChildTaskStatusChanged(taskId, true /* present */,
+            mCallbacks.onChildTaskStatusChanged(this, taskId, true /* present */,
                     taskInfo.isVisible && taskInfo.isVisibleRequested);
-            if (ENABLE_SHELL_TRANSITIONS) {
-                // Status is managed/synchronized by the transition lifecycle.
-                return;
-            }
-            updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
-            mCallbacks.onChildTaskAppeared(taskId);
-            sendStatusChanged();
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -231,14 +229,6 @@
                 taskInfo.taskId, taskInfo.baseActivity);
         mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
         if (mRootTaskInfo.taskId == taskInfo.taskId) {
-            // Inflates split decor view only when the root task is visible.
-            if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) {
-                if (taskInfo.isVisible) {
-                    mSplitDecorManager.inflate(mContext, mRootLeash);
-                } else {
-                    mSyncQueue.runInSync(t -> mSplitDecorManager.release(t));
-                }
-            }
             mRootTaskInfo = taskInfo;
         } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
             if (!taskInfo.supportsMultiWindow
@@ -250,25 +240,17 @@
                         taskInfo.taskId);
                 // Leave split screen if the task no longer supports multi window or have
                 // uncontrolled task.
-                mCallbacks.onNoLongerSupportMultiWindow(taskInfo);
+                mCallbacks.onNoLongerSupportMultiWindow(this, taskInfo);
                 return;
             }
             mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
-            mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
-                    taskInfo.isVisible && taskInfo.isVisibleRequested);
-            if (!ENABLE_SHELL_TRANSITIONS) {
-                updateChildTaskSurface(
-                        taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
-            }
+            mVisible = taskInfo.isVisible && taskInfo.isVisibleRequested;
+            mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */,
+                    mVisible);
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
         }
-        if (ENABLE_SHELL_TRANSITIONS) {
-            // Status is managed/synchronized by the transition lifecycle.
-            return;
-        }
-        sendStatusChanged();
     }
 
     @Override
@@ -278,6 +260,9 @@
         final int taskId = taskInfo.taskId;
         mWindowDecorViewModel.ifPresent(vm -> vm.onTaskVanished(taskInfo));
         if (mRootTaskInfo.taskId == taskId) {
+            mHasRootTask = false;
+            mVisible = false;
+            mHasChildren = false;
             mCallbacks.onRootTaskVanished();
             mRootTaskInfo = null;
             mRootLeash = null;
@@ -288,12 +273,8 @@
         } else if (mChildrenTaskInfo.contains(taskId)) {
             mChildrenTaskInfo.remove(taskId);
             mChildrenLeashes.remove(taskId);
-            mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
-            if (ENABLE_SHELL_TRANSITIONS) {
-                // Status is managed/synchronized by the transition lifecycle.
-                return;
-            }
-            sendStatusChanged();
+            mCallbacks.onChildTaskStatusChanged(this, taskId, false /* present */,
+                    taskInfo.isVisible);
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -455,26 +436,6 @@
         }
     }
 
-    private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo,
-            SurfaceControl leash, boolean firstAppeared) {
-        final Point taskPositionInParent = taskInfo.positionInParent;
-        mSyncQueue.runInSync(t -> {
-            // The task surface might be released before running in the sync queue for the case like
-            // trampoline launch, so check if the surface is valid before processing it.
-            if (!leash.isValid()) {
-                Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId);
-                return;
-            }
-            t.setCrop(leash, null);
-            t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
-            if (firstAppeared) {
-                t.setAlpha(leash, 1f);
-                t.setMatrix(leash, 1, 0, 0, 1);
-                t.show(leash);
-            }
-        });
-    }
-
     // ---------
     // Previously only used in MainStage
     boolean isActive() {
@@ -538,7 +499,19 @@
     }
 
     private void sendStatusChanged() {
-        mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0);
+        boolean hasChildren = mChildrenTaskInfo.size() > 0;
+        boolean visible = mRootTaskInfo.isVisible;
+        if (!mHasRootTask) return;
+
+        if (mHasChildren != hasChildren) {
+            mHasChildren = hasChildren;
+            mCallbacks.onStageHasChildrenChanged(this);
+        }
+
+        if (mVisible != visible) {
+            mVisible = visible;
+            mCallbacks.onStageVisibilityChanged(this);
+        }
     }
 
     @Override
@@ -554,5 +527,8 @@
                         + " baseActivity=" + taskInfo.baseActivity);
             }
         }
+        pw.println(prefix + "mHasRootTask=" + mHasRootTask);
+        pw.println(prefix + "mVisible=" + mVisible);
+        pw.println(prefix + "mHasChildren=" + mHasChildren);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index a2439a9..5437167 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -61,6 +61,7 @@
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation;
 import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
 import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
 import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
@@ -68,8 +69,6 @@
 import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -81,7 +80,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Color;
-import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -90,12 +88,10 @@
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.util.ArrayMap;
-import android.view.Choreographer;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
-import android.view.animation.Transformation;
 import android.window.TransitionInfo;
 import android.window.TransitionMetrics;
 import android.window.TransitionRequestInfo;
@@ -362,7 +358,6 @@
                     isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS;
                     if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
                         final int flags = wallpaperTransit != WALLPAPER_TRANSITION_NONE
-                                && Flags.commonSurfaceAnimator()
                                 ? ScreenRotationAnimation.FLAG_HAS_WALLPAPER : 0;
                         startRotationAnimation(startTransaction, change, info, anim, flags,
                                 animations, onAnimFinish);
@@ -823,72 +818,6 @@
         return a;
     }
 
-    /** Builds an animator for the surface and adds it to the `animations` list. */
-    static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
-            @NonNull Animation anim, @NonNull SurfaceControl leash,
-            @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
-            @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
-            @Nullable Rect clipRect, boolean isActivity) {
-        if (Flags.commonSurfaceAnimator()) {
-            DefaultSurfaceAnimator.buildSurfaceAnimation(animations, anim, leash, finishCallback,
-                    pool, mainExecutor, position, cornerRadius, clipRect, isActivity);
-            return;
-        }
-        final SurfaceControl.Transaction transaction = pool.acquire();
-        final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
-        final Transformation transformation = new Transformation();
-        final float[] matrix = new float[9];
-        // Animation length is already expected to be scaled.
-        va.overrideDurationScale(1.0f);
-        va.setDuration(anim.computeDurationHint());
-        final ValueAnimator.AnimatorUpdateListener updateListener = animation -> {
-            final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
-
-            applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
-                    position, cornerRadius, clipRect, isActivity);
-        };
-        va.addUpdateListener(updateListener);
-
-        final Runnable finisher = () -> {
-            applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
-                    position, cornerRadius, clipRect, isActivity);
-
-            pool.release(transaction);
-            mainExecutor.execute(() -> {
-                animations.remove(va);
-                finishCallback.run();
-            });
-        };
-        va.addListener(new AnimatorListenerAdapter() {
-            // It is possible for the end/cancel to be called more than once, which may cause
-            // issues if the animating surface has already been released. Track the finished
-            // state here to skip duplicate callbacks. See b/252872225.
-            private boolean mFinished = false;
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                onFinish();
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                onFinish();
-            }
-
-            private void onFinish() {
-                if (mFinished) return;
-                mFinished = true;
-                finisher.run();
-                // The update listener can continue to be called after the animation has ended if
-                // end() is called manually again before the finisher removes the animation.
-                // Remove it manually here to prevent animating a released surface.
-                // See b/252872225.
-                va.removeUpdateListener(updateListener);
-            }
-        });
-        animations.add(va);
-    }
-
     private void attachThumbnail(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, TransitionInfo.Change change,
             TransitionInfo.AnimationOptions options, float cornerRadius) {
@@ -1014,38 +943,4 @@
                 || animType == ANIM_CLIP_REVEAL || animType == ANIM_OPEN_CROSS_PROFILE_APPS
                 || animType == ANIM_FROM_STYLE;
     }
-
-    private static void applyTransformation(long time, SurfaceControl.Transaction t,
-            SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
-            Point position, float cornerRadius, @Nullable Rect immutableClipRect,
-            boolean isActivity) {
-        tmpTransformation.clear();
-        anim.getTransformation(time, tmpTransformation);
-        if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
-                && anim.getExtensionEdges() != 0x0 && isActivity) {
-            t.setEdgeExtensionEffect(leash, anim.getExtensionEdges());
-        }
-        if (position != null) {
-            tmpTransformation.getMatrix().postTranslate(position.x, position.y);
-        }
-        t.setMatrix(leash, tmpTransformation.getMatrix(), matrix);
-        t.setAlpha(leash, tmpTransformation.getAlpha());
-
-        final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect);
-        Insets extensionInsets = Insets.min(tmpTransformation.getInsets(), Insets.NONE);
-        if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
-            // Clip out any overflowing edge extension
-            clipRect.inset(extensionInsets);
-            t.setCrop(leash, clipRect);
-        }
-
-        if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
-            // We can only apply rounded corner if a crop is set
-            t.setCrop(leash, clipRect);
-            t.setCornerRadius(leash, cornerRadius);
-        }
-
-        t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
-        t.apply();
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
index 399e39a..6d01e24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
@@ -16,7 +16,8 @@
 
 package com.android.wm.shell.transition;
 
-import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 
@@ -24,10 +25,11 @@
 import static com.android.wm.shell.transition.Transitions.TransitionObserver;
 
 import android.annotation.NonNull;
-import android.os.IBinder;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.os.RemoteException;
+import android.util.ArraySet;
 import android.util.Slog;
-import android.view.SurfaceControl;
+import android.util.SparseArray;
 import android.window.TransitionInfo;
 
 import com.android.wm.shell.shared.FocusTransitionListener;
@@ -43,44 +45,64 @@
  * It reports transitions to callers outside of the process via {@link IFocusTransitionListener},
  * and callers within the process via {@link FocusTransitionListener}.
  */
-public class FocusTransitionObserver implements TransitionObserver {
+public class FocusTransitionObserver {
     private static final String TAG = FocusTransitionObserver.class.getSimpleName();
 
     private IFocusTransitionListener mRemoteListener;
     private final Map<FocusTransitionListener, Executor> mLocalListeners =
             new HashMap<>();
 
-    private int mFocusedDisplayId = INVALID_DISPLAY;
+    private int mFocusedDisplayId = DEFAULT_DISPLAY;
+    private final SparseArray<RunningTaskInfo> mFocusedTaskOnDisplay = new SparseArray<>();
+
+    private final ArraySet<RunningTaskInfo> mTmpTasksToBeNotified = new ArraySet<>();
 
     public FocusTransitionObserver() {}
 
-    @Override
-    public void onTransitionReady(@NonNull IBinder transition,
-            @NonNull TransitionInfo info,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction) {
+    /**
+     * Update display/window focus state from the given transition info and notifies changes if any.
+     */
+    public void updateFocusState(@NonNull TransitionInfo info) {
+        if (!enableDisplayFocusInShellTransitions()) {
+            return;
+        }
         final List<TransitionInfo.Change> changes = info.getChanges();
         for (int i = changes.size() - 1; i >= 0; i--) {
             final TransitionInfo.Change change = changes.get(i);
+
+            final RunningTaskInfo task = change.getTaskInfo();
+            if (task != null
+                    && (change.hasFlags(FLAG_MOVED_TO_TOP) || change.getMode() == TRANSIT_OPEN)) {
+                final RunningTaskInfo lastFocusedTaskOnDisplay =
+                        mFocusedTaskOnDisplay.get(task.displayId);
+                if (lastFocusedTaskOnDisplay != null) {
+                    mTmpTasksToBeNotified.add(lastFocusedTaskOnDisplay);
+                }
+                mTmpTasksToBeNotified.add(task);
+                mFocusedTaskOnDisplay.put(task.displayId, task);
+            }
+
             if (change.hasFlags(FLAG_IS_DISPLAY) && change.hasFlags(FLAG_MOVED_TO_TOP)) {
                 if (mFocusedDisplayId != change.getEndDisplayId()) {
+                    final RunningTaskInfo lastGloballyFocusedTask =
+                            mFocusedTaskOnDisplay.get(mFocusedDisplayId);
+                    if (lastGloballyFocusedTask != null) {
+                        mTmpTasksToBeNotified.add(lastGloballyFocusedTask);
+                    }
                     mFocusedDisplayId = change.getEndDisplayId();
                     notifyFocusedDisplayChanged();
+                    final RunningTaskInfo currentGloballyFocusedTask =
+                            mFocusedTaskOnDisplay.get(mFocusedDisplayId);
+                    if (currentGloballyFocusedTask != null) {
+                        mTmpTasksToBeNotified.add(currentGloballyFocusedTask);
+                    }
                 }
-                return;
             }
         }
+        mTmpTasksToBeNotified.forEach(this::notifyTaskFocusChanged);
+        mTmpTasksToBeNotified.clear();
     }
 
-    @Override
-    public void onTransitionStarting(@NonNull IBinder transition) {}
-
-    @Override
-    public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {}
-
-    @Override
-    public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {}
-
     /**
      * Sets the focus transition listener that receives any transitions resulting in focus switch.
      * This is for calls from outside the Shell, within the host process.
@@ -92,7 +114,10 @@
             return;
         }
         mLocalListeners.put(listener, executor);
-        executor.execute(() -> listener.onFocusedDisplayChanged(mFocusedDisplayId));
+        executor.execute(() -> {
+            listener.onFocusedDisplayChanged(mFocusedDisplayId);
+            mTmpTasksToBeNotified.forEach(this::notifyTaskFocusChanged);
+        });
     }
 
     /**
@@ -120,13 +145,20 @@
         notifyFocusedDisplayChangedToRemote();
     }
 
-    /**
-     * Notifies the listener that display focus has changed.
-     */
-    public void notifyFocusedDisplayChanged() {
+    private void notifyTaskFocusChanged(RunningTaskInfo task) {
+        final boolean isFocusedOnDisplay = isFocusedOnDisplay(task);
+        final boolean isFocusedGlobally = hasGlobalFocus(task);
+        mLocalListeners.forEach((listener, executor) ->
+                executor.execute(() -> listener.onFocusedTaskChanged(task.taskId,
+                        isFocusedOnDisplay, isFocusedGlobally)));
+    }
+
+    private void notifyFocusedDisplayChanged() {
         notifyFocusedDisplayChangedToRemote();
         mLocalListeners.forEach((listener, executor) ->
-                executor.execute(() -> listener.onFocusedDisplayChanged(mFocusedDisplayId)));
+                executor.execute(() -> {
+                    listener.onFocusedDisplayChanged(mFocusedDisplayId);
+                }));
     }
 
     private void notifyFocusedDisplayChangedToRemote() {
@@ -138,4 +170,23 @@
             }
         }
     }
+
+    private boolean isFocusedOnDisplay(@NonNull RunningTaskInfo task) {
+        if (!enableDisplayFocusInShellTransitions()) {
+            return task.isFocused;
+        }
+        final RunningTaskInfo focusedTaskOnDisplay = mFocusedTaskOnDisplay.get(task.displayId);
+        return focusedTaskOnDisplay != null && focusedTaskOnDisplay.taskId == task.taskId;
+    }
+
+    /**
+     * Checks whether the given task has focused globally on the system.
+     * (Note {@link RunningTaskInfo#isFocused} represents per-display focus.)
+     */
+    public boolean hasGlobalFocus(@NonNull RunningTaskInfo task) {
+        if (!enableDisplayFocusInShellTransitions()) {
+            return task.isFocused;
+        }
+        return task.displayId == mFocusedDisplayId && isFocusedOnDisplay(task);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 1a04997..6f3aa11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -21,7 +21,7 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
 import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
 
-import static com.android.wm.shell.transition.DefaultTransitionHandler.buildSurfaceAnimation;
+import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation;
 import static com.android.wm.shell.transition.Transitions.TAG;
 
 import android.animation.Animator;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index d5e92e6..346f21b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -392,8 +392,6 @@
 
         mShellCommandHandler.addCommandCallback("transitions", this, this);
         mShellCommandHandler.addDumpCallback(this::dump, this);
-
-        registerObserver(mFocusTransitionObserver);
     }
 
     public boolean isRegistered() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index f783b45..3e0e15a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -16,22 +16,26 @@
 
 package com.android.wm.shell.unfold;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
 
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
 
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
+import android.os.Handler;
 import android.os.IBinder;
+import android.util.Slog;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.shared.TransactionPool;
@@ -45,6 +49,8 @@
 import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
 import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -56,30 +62,58 @@
  */
 public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener {
 
+    private static final String TAG = "UnfoldTransitionHandler";
+    @VisibleForTesting
+    static final int FINISH_ANIMATION_TIMEOUT_MILLIS = 5_000;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE,
+            DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD,
+            DefaultDisplayChange.DEFAULT_DISPLAY_FOLD,
+    })
+    private @interface DefaultDisplayChange {
+        int DEFAULT_DISPLAY_NO_CHANGE = 0;
+        int DEFAULT_DISPLAY_UNFOLD = 1;
+        int DEFAULT_DISPLAY_FOLD = 2;
+    }
+
     private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
     private final Transitions mTransitions;
     private final Executor mExecutor;
     private final TransactionPool mTransactionPool;
+    private final Handler mHandler;
 
     @Nullable
     private TransitionFinishCallback mFinishCallback;
     @Nullable
     private IBinder mTransition;
 
+    // TODO: b/318803244 - remove when we could guarantee finishing the animation
+    //  after startAnimation callback
     private boolean mAnimationFinished = false;
+    private float mLastAnimationProgress = 0.0f;
     private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
 
+    private final Runnable mAnimationPlayingTimeoutRunnable = () -> {
+        Slog.wtf(TAG, "Timeout occurred when playing the unfold animation, "
+                + "force finishing the transition");
+        finishTransitionIfNeeded();
+    };
+
     public UnfoldTransitionHandler(ShellInit shellInit,
             ShellUnfoldProgressProvider unfoldProgressProvider,
             FullscreenUnfoldTaskAnimator fullscreenUnfoldAnimator,
             SplitTaskUnfoldAnimator splitUnfoldTaskAnimator,
             TransactionPool transactionPool,
             Executor executor,
+            Handler handler,
             Transitions transitions) {
         mUnfoldProgressProvider = unfoldProgressProvider;
         mTransitions = transitions;
         mTransactionPool = transactionPool;
         mExecutor = executor;
+        mHandler = handler;
 
         mAnimators.add(splitUnfoldTaskAnimator);
         mAnimators.add(fullscreenUnfoldAnimator);
@@ -107,16 +141,6 @@
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull TransitionFinishCallback finishCallback) {
-        if (shouldPlayUnfoldAnimation(info) && transition != mTransition) {
-            // Take over transition that has unfold, we might receive it if no other handler
-            // accepted request in handleRequest, e.g. for rotation + unfold or
-            // TRANSIT_NONE + unfold transitions
-            mTransition = transition;
-
-            ProtoLog.v(WM_SHELL_TRANSITIONS, "UnfoldTransitionHandler: "
-                    + "take over startAnimation");
-        }
-
         if (transition != mTransition) return false;
 
         for (int i = 0; i < mAnimators.size(); i++) {
@@ -151,6 +175,11 @@
         // finish shell transition immediately
         if (mAnimationFinished) {
             finishTransitionIfNeeded();
+        } else {
+            // TODO: b/318803244 - remove timeout handling when we could guarantee that
+            //  the animation will be always finished after receiving startAnimation
+            mHandler.removeCallbacks(mAnimationPlayingTimeoutRunnable);
+            mHandler.postDelayed(mAnimationPlayingTimeoutRunnable, FINISH_ANIMATION_TIMEOUT_MILLIS);
         }
 
         return true;
@@ -158,6 +187,8 @@
 
     @Override
     public void onStateChangeProgress(float progress) {
+        mLastAnimationProgress = progress;
+
         if (mTransition == null) return;
 
         SurfaceControl.Transaction transaction = null;
@@ -182,8 +213,14 @@
 
     @Override
     public void onStateChangeFinished() {
-        mAnimationFinished = true;
         finishTransitionIfNeeded();
+
+        // mLastAnimationProgress is guaranteed to be 0f when folding finishes, see
+        // {@link PhysicsBasedUnfoldTransitionProgressProvider#cancelTransition}.
+        // We can use it as an indication that the next animation progress events will be related
+        // to unfolding, so let's reset mAnimationFinished to 'false' in this case.
+        final boolean isFoldingFinished = mLastAnimationProgress == 0f;
+        mAnimationFinished = !isFoldingFinished;
     }
 
     @Override
@@ -211,6 +248,12 @@
         // Apply changes happening during the unfold animation immediately
         t.apply();
         finishCallback.onTransitionFinished(null);
+
+        if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) {
+            // Force-finish current unfold animation as we are processing folding now which doesn't
+            // have any animations on the Shell side
+            finishTransitionIfNeeded();
+        }
     }
 
     /** Whether `request` contains an unfold action. */
@@ -219,18 +262,25 @@
         if (!ValueAnimator.areAnimatorsEnabled()) return false;
 
         return (request.getType() == TRANSIT_CHANGE
-                && request.getDisplayChange() != null
-                && isUnfoldDisplayChange(request.getDisplayChange()));
+                && getDefaultDisplayChange(request.getDisplayChange())
+                == DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD);
     }
 
-    private boolean isUnfoldDisplayChange(
-            @NonNull TransitionRequestInfo.DisplayChange displayChange) {
+    @DefaultDisplayChange
+    private int getDefaultDisplayChange(
+            @Nullable TransitionRequestInfo.DisplayChange displayChange) {
+        if (displayChange == null) return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
+
+        if (displayChange.getDisplayId() != DEFAULT_DISPLAY) {
+            return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
+        }
+
         if (!displayChange.isPhysicalDisplayChanged()) {
-            return false;
+            return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
         }
 
         if (displayChange.getStartAbsBounds() == null || displayChange.getEndAbsBounds() == null) {
-            return false;
+            return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
         }
 
         // Handle only unfolding, currently we don't have an animation when folding
@@ -239,17 +289,11 @@
         final int startArea = displayChange.getStartAbsBounds().width()
                 * displayChange.getStartAbsBounds().height();
 
-        return endArea > startArea;
+        return endArea > startArea ? DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD
+                : DefaultDisplayChange.DEFAULT_DISPLAY_FOLD;
     }
 
-    /** Whether `transitionInfo` contains an unfold action. */
-    public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) {
-        // Unfold animation won't play when animations are disabled
-        if (!ValueAnimator.areAnimatorsEnabled()) return false;
-        // Only handle transitions that are marked as physical display switch
-        // See PhysicalDisplaySwitchTransitionLauncher for the conditions
-        if ((transitionInfo.getFlags() & TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH) == 0) return false;
-
+    private int getDefaultDisplayChange(@NonNull TransitionInfo transitionInfo) {
         for (int i = 0; i < transitionInfo.getChanges().size(); i++) {
             final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
             // We are interested only in display container changes
@@ -268,11 +312,13 @@
                     * change.getStartAbsBounds().height();
 
             if (afterArea > beforeArea) {
-                return true;
+                return DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD;
+            } else {
+                return DefaultDisplayChange.DEFAULT_DISPLAY_FOLD;
             }
         }
 
-        return false;
+        return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
     }
 
     @Nullable
@@ -293,10 +339,6 @@
     @Override
     public void onFoldStateChanged(boolean isFolded) {
         if (isFolded) {
-            // Reset unfold animation finished flag on folding, so it could be used next time
-            // when we unfold the device as an indication that animation hasn't finished yet
-            mAnimationFinished = false;
-
             // If we are currently animating unfold animation we should finish it because
             // the animation might not start and finish as the device was folded
             finishTransitionIfNeeded();
@@ -312,6 +354,7 @@
             animator.stop();
         }
 
+        mHandler.removeCallbacks(mAnimationPlayingTimeoutRunnable);
         mFinishCallback.onTransitionFinished(null);
         mFinishCallback = null;
         mTransition = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index c540ede..be4fd7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -58,9 +58,11 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.FocusTransitionListener;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 
@@ -68,7 +70,7 @@
  * View model for the window decoration with a caption and shadows. Works with
  * {@link CaptionWindowDecoration}.
  */
-public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
+public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusTransitionListener {
     private static final String TAG = "CaptionWindowDecorViewModel";
 
     private final ShellTaskOrganizer mTaskOrganizer;
@@ -85,6 +87,7 @@
     private final Region mExclusionRegion = Region.obtain();
     private final InputManager mInputManager;
     private TaskOperations mTaskOperations;
+    private FocusTransitionObserver mFocusTransitionObserver;
 
     /**
      * Whether to pilfer the next motion event to send cancellations to the windows below.
@@ -121,7 +124,8 @@
             DisplayController displayController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             SyncTransactionQueue syncQueue,
-            Transitions transitions) {
+            Transitions transitions,
+            FocusTransitionObserver focusTransitionObserver) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
@@ -133,6 +137,7 @@
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mSyncQueue = syncQueue;
         mTransitions = transitions;
+        mFocusTransitionObserver = focusTransitionObserver;
         if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
             mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
         }
@@ -148,6 +153,16 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register window manager callbacks", e);
         }
+        mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+    }
+
+    @Override
+    public void onFocusedTaskChanged(int taskId, boolean isFocusedOnDisplay,
+            boolean isFocusedGlobally) {
+        final WindowDecoration decor = mWindowDecorByTaskId.get(taskId);
+        if (decor != null) {
+            decor.relayout(decor.mTaskInfo, isFocusedGlobally);
+        }
     }
 
     @Override
@@ -180,7 +195,7 @@
             return;
         }
 
-        decoration.relayout(taskInfo);
+        decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
     }
 
     @Override
@@ -217,7 +232,8 @@
             createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         } else {
             decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
-                    false /* setTaskCropAndPosition */);
+                    false /* setTaskCropAndPosition */,
+                    mFocusTransitionObserver.hasGlobalFocus(taskInfo));
         }
     }
 
@@ -230,7 +246,8 @@
         if (decoration == null) return;
 
         decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
-                false /* setTaskCropAndPosition */);
+                false /* setTaskCropAndPosition */,
+                mFocusTransitionObserver.hasGlobalFocus(taskInfo));
     }
 
     @Override
@@ -308,7 +325,8 @@
         windowDecoration.setDragPositioningCallback(taskPositioner);
         windowDecoration.setTaskDragResizer(taskPositioner);
         windowDecoration.relayout(taskInfo, startT, finishT,
-                false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */);
+                false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */,
+                mFocusTransitionObserver.hasGlobalFocus(taskInfo));
     }
 
     private class CaptionTouchEventListener implements
@@ -359,7 +377,7 @@
             }
             if (e.getAction() == MotionEvent.ACTION_DOWN) {
                 final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-                if (!taskInfo.isFocused) {
+                if (!mFocusTransitionObserver.hasGlobalFocus(taskInfo)) {
                     final WindowContainerTransaction wct = new WindowContainerTransaction();
                     wct.reorder(mTaskToken, true /* onTop */, true /* includingParents */);
                     mSyncQueue.queue(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 839973f..509cb85 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.windowdecor;
 
-import static android.window.flags.DesktopModeFlags.ENABLE_WINDOWING_SCALED_RESIZING;
+import static android.window.DesktopModeFlags.ENABLE_WINDOWING_SCALED_RESIZING;
 
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
@@ -174,7 +174,7 @@
     }
 
     @Override
-    void relayout(RunningTaskInfo taskInfo) {
+    void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         // The crop and position of the task should only be set when a task is fluid resizing. In
         // all other cases, it is expected that the transition handler positions and crops the task
@@ -185,7 +185,7 @@
         // synced with the buffer transaction (that draws the View). Both will be shown on screen
         // at the same, whereas applying them independently causes flickering. See b/270202228.
         relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
-                shouldSetTaskPositionAndCrop);
+                shouldSetTaskPositionAndCrop, hasGlobalFocus);
     }
 
     @VisibleForTesting
@@ -196,12 +196,13 @@
             boolean setTaskCropAndPosition,
             boolean isStatusBarVisible,
             boolean isKeyguardVisibleAndOccluded,
-            InsetsState displayInsetsState) {
+            InsetsState displayInsetsState,
+            boolean hasGlobalFocus) {
         relayoutParams.reset();
         relayoutParams.mRunningTaskInfo = taskInfo;
         relayoutParams.mLayoutResId = R.layout.caption_window_decor;
         relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
-        relayoutParams.mShadowRadiusId = taskInfo.isFocused
+        relayoutParams.mShadowRadiusId = hasGlobalFocus
                 ? R.dimen.freeform_decor_shadow_focused_thickness
                 : R.dimen.freeform_decor_shadow_unfocused_thickness;
         relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
@@ -233,7 +234,8 @@
     @SuppressLint("MissingPermission")
     void relayout(RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition) {
+            boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition,
+            boolean hasGlobalFocus) {
         final boolean isFreeform =
                 taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
         final boolean isDragResizeable = ENABLE_WINDOWING_SCALED_RESIZING.isTrue()
@@ -245,7 +247,7 @@
 
         updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw,
                 setTaskCropAndPosition, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
-                mDisplayController.getInsetsState(taskInfo.displayId));
+                mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus);
 
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
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 bcf48d9..9e089b2 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
@@ -56,7 +56,6 @@
 import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -77,10 +76,10 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.Toast;
+import android.window.DesktopModeFlags;
 import android.window.TaskSnapshot;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
-import android.window.flags.DesktopModeFlags;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
@@ -103,8 +102,8 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
-import com.android.wm.shell.desktopmode.DesktopRepository;
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
+import com.android.wm.shell.desktopmode.DesktopRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
 import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -112,6 +111,7 @@
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.FocusTransitionListener;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -124,6 +124,7 @@
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
@@ -146,7 +147,8 @@
  * {@link DesktopModeWindowDecoration}.
  */
 
-public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
+public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
+        FocusTransitionListener {
     private static final String TAG = "DesktopModeWindowDecorViewModel";
 
     private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
@@ -216,6 +218,7 @@
                 }
             };
     private final TaskPositionerFactory mTaskPositionerFactory;
+    private final FocusTransitionObserver mFocusTransitionObserver;
 
     public DesktopModeWindowDecorViewModel(
             Context context,
@@ -242,7 +245,8 @@
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) {
+            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
+            FocusTransitionObserver focusTransitionObserver) {
         this(
                 context,
                 shellExecutor,
@@ -274,7 +278,8 @@
                 appHandleEducationController,
                 windowDecorCaptionHandleRepository,
                 activityOrientationChangeHandler,
-                new TaskPositionerFactory());
+                new TaskPositionerFactory(),
+                focusTransitionObserver);
     }
 
     @VisibleForTesting
@@ -309,7 +314,8 @@
             AppHandleEducationController appHandleEducationController,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
-            TaskPositionerFactory taskPositionerFactory) {
+            TaskPositionerFactory taskPositionerFactory,
+            FocusTransitionObserver focusTransitionObserver) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
@@ -369,6 +375,7 @@
             }
         };
         mTaskPositionerFactory = taskPositionerFactory;
+        mFocusTransitionObserver = focusTransitionObserver;
 
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -402,11 +409,22 @@
                         return Unit.INSTANCE;
                     });
         }
+        mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+    }
+
+    @Override
+    public void onFocusedTaskChanged(int taskId, boolean isFocusedOnDisplay,
+            boolean isFocusedGlobally) {
+        final WindowDecoration decor = mWindowDecorByTaskId.get(taskId);
+        if (decor != null) {
+            decor.relayout(decor.mTaskInfo, isFocusedGlobally);
+        }
     }
 
     @Override
     public void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter) {
         mTaskOperations = new TaskOperations(transitionStarter, mContext, mSyncQueue);
+        mDesktopTasksController.setFreeformTaskTransitionStarter(transitionStarter);
     }
 
     @Override
@@ -447,7 +465,7 @@
             removeTaskFromEventReceiver(oldTaskInfo.displayId);
             incrementEventReceiverTasks(taskInfo.displayId);
         }
-        decoration.relayout(taskInfo);
+        decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
         mActivityOrientationChangeHandler.ifPresent(handler ->
                 handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
     }
@@ -486,7 +504,8 @@
             createWindowDecoration(taskInfo, taskSurface, startT, finishT);
         } else {
             decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
-                    false /* shouldSetTaskPositionAndCrop */);
+                    false /* shouldSetTaskPositionAndCrop */,
+                    mFocusTransitionObserver.hasGlobalFocus(taskInfo));
         }
     }
 
@@ -499,7 +518,8 @@
         if (decoration == null) return;
 
         decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
-                false /* shouldSetTaskPositionAndCrop */);
+                false /* shouldSetTaskPositionAndCrop */,
+                mFocusTransitionObserver.hasGlobalFocus(taskInfo));
     }
 
     @Override
@@ -774,11 +794,7 @@
                     onMaximizeOrRestore(decoration.mTaskInfo.taskId, "caption_bar_button");
                 }
             } else if (id == R.id.minimize_window) {
-                final WindowContainerTransaction wct = new WindowContainerTransaction();
-                mDesktopTasksController.onDesktopWindowMinimize(wct, mTaskId);
-                final IBinder transition = mTaskOperations.minimizeTask(mTaskToken, wct);
-                mDesktopTasksLimiter.ifPresent(limiter ->
-                        limiter.addPendingMinimizeChange(transition, mDisplayId, mTaskId));
+                mDesktopTasksController.minimizeTask(decoration.mTaskInfo);
             }
         }
 
@@ -895,7 +911,7 @@
         }
 
         private void moveTaskToFront(RunningTaskInfo taskInfo) {
-            if (!taskInfo.isFocused) {
+            if (!mFocusTransitionObserver.hasGlobalFocus(taskInfo)) {
                 mDesktopTasksController.moveTaskToFront(taskInfo);
             }
         }
@@ -1516,7 +1532,8 @@
         windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
         windowDecoration.setDragPositioningCallback(taskPositioner);
         windowDecoration.relayout(taskInfo, startT, finishT,
-                false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */);
+                false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
+                mFocusTransitionObserver.hasGlobalFocus(taskInfo));
         if (!Flags.enableHandleInputFix()) {
             incrementEventReceiverTasks(taskInfo.displayId);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 25d37fc..d3b7ca1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -24,8 +24,8 @@
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
-import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
-import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
+import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
+import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
 
 import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode;
@@ -72,9 +72,9 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.ImageButton;
+import android.window.DesktopModeFlags;
 import android.window.TaskSnapshot;
 import android.window.WindowContainerTransaction;
-import android.window.flags.DesktopModeFlags;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -352,7 +352,7 @@
     }
 
     @Override
-    void relayout(ActivityManager.RunningTaskInfo taskInfo) {
+    void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
         final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
         // The crop and position of the task should only be set when a task is fluid resizing. In
         // all other cases, it is expected that the transition handler positions and crops the task
@@ -365,7 +365,8 @@
         // the View). Both will be shown on screen at the same, whereas applying them independently
         // causes flickering. See b/270202228.
         final boolean applyTransactionOnDraw = taskInfo.isFreeform();
-        relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop);
+        relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop,
+                hasGlobalFocus);
         if (!applyTransactionOnDraw) {
             t.apply();
         }
@@ -373,18 +374,19 @@
 
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean hasGlobalFocus) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
         if (taskInfo.isFreeform()) {
             // The Task is in Freeform mode -> show its header in sync since it's an integral part
             // of the window itself - a delayed header might cause bad UX.
             relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
-                    shouldSetTaskPositionAndCrop);
+                    shouldSetTaskPositionAndCrop, hasGlobalFocus);
         } else {
             // The Task is outside Freeform mode -> allow the handle view to be delayed since the
             // handle is just a small addition to the window.
             relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
-                    shouldSetTaskPositionAndCrop);
+                    shouldSetTaskPositionAndCrop, hasGlobalFocus);
         }
         Trace.endSection();
     }
@@ -392,11 +394,12 @@
     /** Run the whole relayout phase immediately without delay. */
     private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean hasGlobalFocus) {
         // Clear the current ViewHost runnable as we will update the ViewHost here
         clearCurrentViewHostRunnable();
         updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
-                shouldSetTaskPositionAndCrop);
+                shouldSetTaskPositionAndCrop, hasGlobalFocus);
         if (mResult.mRootView != null) {
             updateViewHost(mRelayoutParams, startT, mResult);
         }
@@ -418,7 +421,8 @@
      */
     private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean hasGlobalFocus) {
         if (applyStartTransactionOnDraw) {
             throw new IllegalArgumentException(
                     "We cannot both sync viewhost ondraw and delay viewhost creation.");
@@ -426,7 +430,8 @@
         // Clear the current ViewHost runnable as we will update the ViewHost here
         clearCurrentViewHostRunnable();
         updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
-                false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop);
+                false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop,
+                hasGlobalFocus);
         if (mResult.mRootView == null) {
             // This means something blocks the window decor from showing, e.g. the task is hidden.
             // Nothing is set up in this case including the decoration surface.
@@ -440,7 +445,8 @@
     @SuppressLint("MissingPermission")
     private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean hasGlobalFocus) {
         Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
 
         if (Flags.enableDesktopWindowingAppToWeb()) {
@@ -459,7 +465,8 @@
                 .isTaskInFullImmersiveState(taskInfo.taskId);
         updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
                 shouldSetTaskPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
-                inFullImmersive, mDisplayController.getInsetsState(taskInfo.displayId));
+                inFullImmersive, mDisplayController.getInsetsState(taskInfo.displayId),
+                hasGlobalFocus);
 
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -507,12 +514,17 @@
             ));
         } else {
             mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
-                    mTaskInfo, TaskInfoKt.getRequestingImmersive(mTaskInfo), inFullImmersive
+                    mTaskInfo,
+                    TaskInfoKt.getRequestingImmersive(mTaskInfo),
+                    inFullImmersive,
+                    hasGlobalFocus,
+                    /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
+                            /* animatingTaskResizeOrReposition= */ false)
             ));
         }
         Trace.endSection();
 
-        if (!mTaskInfo.isFocused) {
+        if (!hasGlobalFocus) {
             closeHandleMenu();
             closeManageWindowsMenu();
             closeMaximizeMenu();
@@ -780,7 +792,8 @@
             boolean isStatusBarVisible,
             boolean isKeyguardVisibleAndOccluded,
             boolean inFullImmersiveMode,
-            @NonNull InsetsState displayInsetsState) {
+            @NonNull InsetsState displayInsetsState,
+            boolean hasGlobalFocus) {
         final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
         final boolean isAppHeader =
                 captionLayoutId == R.layout.desktop_mode_app_header;
@@ -790,6 +803,7 @@
         relayoutParams.mLayoutResId = captionLayoutId;
         relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
         relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
+        relayoutParams.mHasGlobalFocus = hasGlobalFocus;
 
         final boolean showCaption;
         if (Flags.enableFullyImmersiveInDesktop()) {
@@ -812,7 +826,7 @@
                     || (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
         }
         relayoutParams.mIsCaptionVisible = showCaption;
-
+        relayoutParams.mIsInsetSource = isAppHeader && !inFullImmersiveMode;
         if (isAppHeader) {
             if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
                 // If the app is requesting to customize the caption bar, allow input to fall
@@ -837,7 +851,6 @@
                         WindowInsets.Type.systemBars() & ~WindowInsets.Type.captionBar(),
                         false /* ignoreVisibility */);
                 relayoutParams.mCaptionTopPadding = systemBarInsets.top;
-                relayoutParams.mIsInsetSource = false;
             }
             // Report occluding elements as bounding rects to the insets system so that apps can
             // draw in the empty space in the center:
@@ -865,8 +878,8 @@
             relayoutParams.mInputFeatures
                     |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
         }
-        if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) {
-            relayoutParams.mShadowRadiusId = taskInfo.isFocused
+        if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ hasGlobalFocus)) {
+            relayoutParams.mShadowRadiusId = hasGlobalFocus
                     ? R.dimen.freeform_decor_shadow_focused_thickness
                     : R.dimen.freeform_decor_shadow_unfocused_thickness;
         }
@@ -1408,7 +1421,7 @@
     }
 
     boolean isFocused() {
-        return mTaskInfo.isFocused;
+        return mHasGlobalFocus;
     }
 
     /**
@@ -1592,7 +1605,7 @@
 
     private static int getCaptionHeightIdStatic(@WindowingMode int windowingMode) {
         return windowingMode == WINDOWING_MODE_FULLSCREEN
-                ? R.dimen.desktop_mode_fullscreen_decor_caption_height
+                ? com.android.internal.R.dimen.status_bar_height_default
                 : R.dimen.desktop_mode_freeform_decor_caption_height;
     }
 
@@ -1607,8 +1620,14 @@
 
     void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) {
         if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return;
-        asAppHeader(mWindowDecorViewHolder)
-                .setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition);
+        final boolean inFullImmersive =
+                mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId);
+        asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
+                mTaskInfo,
+                TaskInfoKt.getRequestingImmersive(mTaskInfo),
+                inFullImmersive,
+                isFocused(),
+                /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition)));
     }
 
     /**
@@ -1625,6 +1644,16 @@
         asAppHeader(mWindowDecorViewHolder).onMaximizeWindowHoverEnter();
     }
 
+    private boolean canOpenMaximizeMenu(boolean animatingTaskResizeOrReposition) {
+        if (!Flags.enableFullyImmersiveInDesktop()) {
+            return !animatingTaskResizeOrReposition;
+        }
+        final boolean inImmersiveAndRequesting =
+                mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId)
+                        && TaskInfoKt.getRequestingImmersive(mTaskInfo);
+        return !animatingTaskResizeOrReposition && !inImmersiveAndRequesting;
+    }
+
     @Override
     public String toString() {
         return "{"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index 38f9cfa..60c9222 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -27,7 +27,7 @@
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
-import android.window.flags.DesktopModeFlags;
+import android.window.DesktopModeFlags;
 
 import androidx.annotation.NonNull;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index d726f50..33d1c26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -18,7 +18,7 @@
 
 import static android.view.InputDevice.SOURCE_MOUSE;
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
-import static android.window.flags.DesktopModeFlags.ENABLE_WINDOWING_EDGE_DRAG_RESIZE;
+import static android.window.DesktopModeFlags.ENABLE_WINDOWING_EDGE_DRAG_RESIZE;
 
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index f4c7fe3..ccf329c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -93,7 +93,7 @@
                 mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
         mRepositionStartPoint.set(x, y);
         mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
-        if (mCtrlType != CTRL_TYPE_UNDEFINED && !mWindowDecoration.mTaskInfo.isFocused) {
+        if (mCtrlType != CTRL_TYPE_UNDEFINED && !mWindowDecoration.mHasGlobalFocus) {
             WindowContainerTransaction wct = new WindowContainerTransaction();
             wct.reorder(mWindowDecoration.mTaskInfo.token, true /* onTop */,
                     true /* includingParents */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 68a58ee0..376cd2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -33,7 +33,7 @@
 import androidx.core.animation.doOnStart
 import androidx.core.content.ContextCompat
 import com.android.wm.shell.R
-import android.window.flags.DesktopModeFlags
+import android.window.DesktopModeFlags
 
 private const val OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS = 350
 private const val MAX_DRAWABLE_ALPHA = 255
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 0cb219a..3ae5a1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -62,6 +62,7 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.calculateMaximizeBounds
 import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
@@ -73,7 +74,8 @@
 
 /**
  *  Menu that appears when user long clicks the maximize button. Gives the user the option to
- *  maximize the task or snap the task to the right or left half of the screen.
+ *  maximize the task or restore previous task bounds from the maximized state and to snap the task
+ *  to the right or left half of the screen.
  */
 class MaximizeMenu(
         private val syncQueue: SyncTransactionQueue,
@@ -176,6 +178,7 @@
                 "MaximizeMenu")
         maximizeMenuView = MaximizeMenuView(
             context = decorWindowContext,
+            sizeToggleDirection = getSizeToggleDirection(),
             menuHeight = menuHeight,
             menuPadding = menuPadding,
         ).also { menuView ->
@@ -202,6 +205,18 @@
         }
     }
 
+    private fun getSizeToggleDirection(): MaximizeMenuView.SizeToggleDirection {
+        val maximizeBounds = calculateMaximizeBounds(
+            displayController.getDisplayLayout(taskInfo.displayId)!!,
+            taskInfo
+        )
+        val maximized = taskInfo.configuration.windowConfiguration.bounds.equals(maximizeBounds)
+        return if (maximized)
+            MaximizeMenuView.SizeToggleDirection.RESTORE
+        else
+            MaximizeMenuView.SizeToggleDirection.MAXIMIZE
+    }
+
     private fun loadDimensionPixelSize(resourceId: Int): Int {
         return if (resourceId == Resources.ID_NULL) {
             0
@@ -236,18 +251,19 @@
      * resizing a Task.
      */
     class MaximizeMenuView(
-        context: Context,
+        private val context: Context,
+        private val sizeToggleDirection: SizeToggleDirection,
         private val menuHeight: Int,
-        private val menuPadding: Int,
+        private val menuPadding: Int
     ) {
         val rootView = LayoutInflater.from(context)
             .inflate(R.layout.desktop_mode_window_decor_maximize_menu, null /* root */) as ViewGroup
         private val container = requireViewById(R.id.container)
         private val overlay = requireViewById(R.id.maximize_menu_overlay)
-        private val maximizeText =
-            requireViewById(R.id.maximize_menu_maximize_window_text) as TextView
-        private val maximizeButton =
-            requireViewById(R.id.maximize_menu_maximize_button) as Button
+        private val sizeToggleButtonText =
+            requireViewById(R.id.maximize_menu_size_toggle_button_text) as TextView
+        private val sizeToggleButton =
+            requireViewById(R.id.maximize_menu_size_toggle_button) as Button
         private val snapWindowText =
             requireViewById(R.id.maximize_menu_snap_window_text) as TextView
         private val snapRightButton =
@@ -263,8 +279,6 @@
             .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_radius)
         private val outlineStroke = context.resources
             .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_stroke)
-        private val fillPadding = context.resources
-            .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_padding)
         private val fillRadius = context.resources
             .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_radius)
 
@@ -324,7 +338,7 @@
                 return@setOnHoverListener false
             }
 
-            maximizeButton.setOnClickListener { onMaximizeClickListener?.invoke() }
+            sizeToggleButton.setOnClickListener { onMaximizeClickListener?.invoke() }
             snapRightButton.setOnClickListener { onRightSnapClickListener?.invoke() }
             snapLeftButton.setOnClickListener { onLeftSnapClickListener?.invoke() }
             rootView.setOnTouchListener { _, event ->
@@ -335,9 +349,17 @@
                 true
             }
 
+            val btnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
+                R.string.desktop_mode_maximize_menu_restore_button_text
+            else
+                R.string.desktop_mode_maximize_menu_maximize_button_text
+            val btnText = context.resources.getText(btnTextId)
+            sizeToggleButton.contentDescription = btnText
+            sizeToggleButtonText.text = btnText
+
             // To prevent aliasing.
-            maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
-            maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+            sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+            sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
         }
 
         /** Bind the menu views to the new [RunningTaskInfo] data. */
@@ -348,8 +370,8 @@
             rootView.background.setTint(style.backgroundColor)
 
             // Maximize option.
-            maximizeButton.background = style.maximizeOption.drawable
-            maximizeText.setTextColor(style.textColor)
+            sizeToggleButton.background = style.maximizeOption.drawable
+            sizeToggleButtonText.setTextColor(style.textColor)
 
             // Snap options.
             snapWindowText.setTextColor(style.textColor)
@@ -358,8 +380,8 @@
 
         /** Animate the opening of the menu */
         fun animateOpenMenu(onEnd: () -> Unit) {
-            maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
-            maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             menuAnimatorSet = AnimatorSet()
             menuAnimatorSet?.playTogether(
                 ObjectAnimator.ofFloat(rootView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f)
@@ -388,9 +410,9 @@
                         // Scale up the children of the maximize menu so that the menu
                         // scale is cancelled out and only the background is scaled.
                         val value = animatedValue as Float
-                        maximizeButton.scaleY = value
+                        sizeToggleButton.scaleY = value
                         snapButtonsLayout.scaleY = value
-                        maximizeText.scaleY = value
+                        sizeToggleButtonText.scaleY = value
                         snapWindowText.scaleY = value
                     }
                 },
@@ -409,9 +431,9 @@
                         startDelay = CONTROLS_ALPHA_OPEN_MENU_ANIMATION_DELAY_MS
                         addUpdateListener {
                             val value = animatedValue as Float
-                            maximizeButton.alpha = value
+                            sizeToggleButton.alpha = value
                             snapButtonsLayout.alpha = value
-                            maximizeText.alpha = value
+                            sizeToggleButtonText.alpha = value
                             snapWindowText.alpha = value
                         }
                     },
@@ -423,8 +445,8 @@
             )
             menuAnimatorSet?.addListener(
                 onEnd = {
-                    maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
-                    maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                    sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                    sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
                     onEnd.invoke()
                 }
             )
@@ -433,8 +455,8 @@
 
         /** Animate the closing of the menu */
         fun animateCloseMenu(onEnd: (() -> Unit)) {
-            maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
-            maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             cancelAnimation()
             menuAnimatorSet = AnimatorSet()
             menuAnimatorSet?.playTogether(
@@ -464,9 +486,9 @@
                             // Scale up the children of the maximize menu so that the menu
                             // scale is cancelled out and only the background is scaled.
                             val value = animatedValue as Float
-                            maximizeButton.scaleY = value
+                            sizeToggleButton.scaleY = value
                             snapButtonsLayout.scaleY = value
-                            maximizeText.scaleY = value
+                            sizeToggleButtonText.scaleY = value
                             snapWindowText.scaleY = value
                         }
                     },
@@ -485,9 +507,9 @@
                                 duration = ALPHA_ANIMATION_DURATION_MS
                                 addUpdateListener {
                                     val value = animatedValue as Float
-                                    maximizeButton.alpha = value
+                                    sizeToggleButton.alpha = value
                                     snapButtonsLayout.alpha = value
-                                    maximizeText.alpha = value
+                                    sizeToggleButtonText.alpha = value
                                     snapWindowText.alpha = value
                                 }
                             },
@@ -498,8 +520,8 @@
             )
             menuAnimatorSet?.addListener(
                     onEnd = {
-                        maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
-                        maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                        sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                        sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
                         onEnd?.invoke()
                     }
             )
@@ -509,8 +531,8 @@
         /** Request that the accessibility service focus on the menu. */
         fun requestAccessibilityFocus() {
             // Focus the first button in the menu by default.
-            maximizeButton.post {
-                maximizeButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+            sizeToggleButton.post {
+                sizeToggleButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
             }
         }
 
@@ -685,15 +707,31 @@
                 paint.color = strokeAndFillColor
                 paint.style = Paint.Style.FILL
             })
+
+            val (horizontalFillPadding, verticalFillPadding) =
+                if (sizeToggleDirection == SizeToggleDirection.MAXIMIZE) {
+                    context.resources.getDimensionPixelSize(R.dimen
+                        .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) to
+                            context.resources.getDimensionPixelSize(R.dimen
+                                .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
+                } else {
+                    context.resources.getDimensionPixelSize(R.dimen
+                        .desktop_mode_maximize_menu_restore_button_fill_horizontal_padding) to
+                            context.resources.getDimensionPixelSize(R.dimen
+                                .desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
+                }
+
             return LayerDrawable(layers.toTypedArray()).apply {
                 when (numberOfLayers) {
                     3 -> {
                         setLayerInset(1, outlineStroke)
-                        setLayerInset(2, fillPadding)
+                        setLayerInset(2, horizontalFillPadding, verticalFillPadding,
+                            horizontalFillPadding, verticalFillPadding)
                     }
                     4 -> {
                         setLayerInset(intArrayOf(1, 2), outlineStroke)
-                        setLayerInset(3, fillPadding)
+                        setLayerInset(3, horizontalFillPadding, verticalFillPadding,
+                            horizontalFillPadding, verticalFillPadding)
                     }
                     else -> error("Unexpected number of layers: $numberOfLayers")
                 }
@@ -737,6 +775,11 @@
         enum class SnapToHalfSelection {
             NONE, LEFT, RIGHT
         }
+
+        /** The possible selection states of the size toggle button in the maximize menu. */
+        enum class SizeToggleDirection {
+            MAXIMIZE, RESTORE
+        }
     }
 
     companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index a1f76d2..ff3b455 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -106,7 +106,7 @@
             // Capture CUJ for re-sizing window in DW mode.
             mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface,
                     mDesktopWindowDecoration.mContext, mHandler, CUJ_DESKTOP_MODE_RESIZE_WINDOW);
-            if (!mDesktopWindowDecoration.mTaskInfo.isFocused) {
+            if (!mDesktopWindowDecoration.mHasGlobalFocus) {
                 WindowContainerTransaction wct = new WindowContainerTransaction();
                 wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true /* onTop */,
                         true /* includingParents */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index f8aed41..ce5cfd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -125,7 +125,7 @@
                     }
 
                     mDisplayController.removeDisplayWindowListener(this);
-                    relayout(mTaskInfo);
+                    relayout(mTaskInfo, mHasGlobalFocus);
                 }
             };
 
@@ -146,6 +146,7 @@
 
     boolean mIsStatusBarVisible;
     boolean mIsKeyguardVisibleAndOccluded;
+    boolean mHasGlobalFocus;
 
     /** The most recent set of insets applied to this window decoration. */
     private WindowDecorationInsets mWindowDecorationInsets;
@@ -199,8 +200,9 @@
      *
      * @param taskInfo The previous {@link RunningTaskInfo} passed into {@link #relayout} or the
      *                 constructor.
+     * @param hasGlobalFocus Whether the task is focused
      */
-    abstract void relayout(RunningTaskInfo taskInfo);
+    abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus);
 
     /**
      * Used by the {@link DragPositioningCallback} associated with the implementing class to
@@ -225,6 +227,7 @@
         if (params.mRunningTaskInfo != null) {
             mTaskInfo = params.mRunningTaskInfo;
         }
+        mHasGlobalFocus = params.mHasGlobalFocus;
         final int oldLayoutResId = mLayoutResId;
         mLayoutResId = params.mLayoutResId;
 
@@ -246,7 +249,7 @@
         final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
         outResult.mWidth = taskBounds.width();
         outResult.mHeight = taskBounds.height();
-        outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+        outResult.mRootView.setTaskFocusState(mHasGlobalFocus);
         final Resources resources = mDecorWindowContext.getResources();
         outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId)
                 + params.mCaptionTopPadding;
@@ -391,11 +394,11 @@
 
         final WindowDecorationInsets newInsets = new WindowDecorationInsets(
                 mTaskInfo.token, mOwner, captionInsetsRect, boundingRects,
-                params.mInsetSourceFlags);
+                params.mInsetSourceFlags, params.mIsInsetSource);
         if (!newInsets.equals(mWindowDecorationInsets)) {
             // Add or update this caption as an insets source.
             mWindowDecorationInsets = newInsets;
-            mWindowDecorationInsets.addOrUpdate(wct);
+            mWindowDecorationInsets.update(wct);
         }
     }
 
@@ -512,7 +515,7 @@
         mIsKeyguardVisibleAndOccluded = visible && occluded;
         final boolean changed = prevVisAndOccluded != mIsKeyguardVisibleAndOccluded;
         if (changed) {
-            relayout(mTaskInfo);
+            relayout(mTaskInfo, mHasGlobalFocus);
         }
     }
 
@@ -522,7 +525,7 @@
         final boolean changed = prevStatusBarVisibility != mIsStatusBarVisible;
 
         if (changed) {
-            relayout(mTaskInfo);
+            relayout(mTaskInfo, mHasGlobalFocus);
         }
     }
 
@@ -710,10 +713,11 @@
         final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId);
         final Rect captionInsets = new Rect(0, 0, 0, captionHeight);
         final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token,
-                mOwner, captionInsets, null /* boundingRets */, 0 /* flags */);
+                mOwner, captionInsets, null /* boundingRets */, 0 /* flags */,
+                true /* shouldAddCaptionInset */);
         if (!newInsets.equals(mWindowDecorationInsets)) {
             mWindowDecorationInsets = newInsets;
-            mWindowDecorationInsets.addOrUpdate(wct);
+            mWindowDecorationInsets.update(wct);
         }
     }
 
@@ -737,6 +741,7 @@
 
         boolean mApplyStartTransactionOnDraw;
         boolean mSetTaskPositionAndCrop;
+        boolean mHasGlobalFocus;
 
         void reset() {
             mLayoutResId = Resources.ID_NULL;
@@ -756,6 +761,7 @@
             mApplyStartTransactionOnDraw = false;
             mSetTaskPositionAndCrop = false;
             mWindowDecorConfig = null;
+            mHasGlobalFocus = false;
         }
 
         boolean hasInputFeatureSpy() {
@@ -814,21 +820,26 @@
         private final Rect mFrame;
         private final Rect[] mBoundingRects;
         private final @InsetsSource.Flags int mFlags;
+        private final boolean mShouldAddCaptionInset;
 
         private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame,
-                Rect[] boundingRects, @InsetsSource.Flags int flags) {
+                Rect[] boundingRects, @InsetsSource.Flags int flags,
+                boolean shouldAddCaptionInset) {
             mToken = token;
             mOwner = owner;
             mFrame = frame;
             mBoundingRects = boundingRects;
             mFlags = flags;
+            mShouldAddCaptionInset = shouldAddCaptionInset;
         }
 
-        void addOrUpdate(WindowContainerTransaction wct) {
-            wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects,
-                    mFlags);
-            wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame,
-                    mBoundingRects, 0 /* flags */);
+        void update(WindowContainerTransaction wct) {
+            if (mShouldAddCaptionInset) {
+                wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects,
+                        mFlags);
+                wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame,
+                        mBoundingRects, 0 /* flags */);
+            }
         }
 
         void remove(WindowContainerTransaction wct) {
@@ -843,7 +854,8 @@
             return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner,
                     that.mOwner) && Objects.equals(mFrame, that.mFrame)
                     && Objects.deepEquals(mBoundingRects, that.mBoundingRects)
-                    && mFlags == that.mFlags;
+                    && mFlags == that.mFlags
+                    && mShouldAddCaptionInset == that.mShouldAddCaptionInset;
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index 52bf400..d943918 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -49,7 +49,7 @@
 import com.android.window.flags.Flags
 import com.android.window.flags.Flags.enableMinimizeButton
 import com.android.wm.shell.R
-import android.window.flags.DesktopModeFlags
+import android.window.DesktopModeFlags
 import com.android.wm.shell.windowdecor.MaximizeButtonView
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.OPACITY_100
@@ -70,7 +70,7 @@
         rootView: View,
         onCaptionTouchListener: View.OnTouchListener,
         onCaptionButtonClickListener: View.OnClickListener,
-        onLongClickListener: OnLongClickListener,
+        private val onLongClickListener: OnLongClickListener,
         onCaptionGenericMotionListener: View.OnGenericMotionListener,
         appName: CharSequence,
         appIconBitmap: Bitmap,
@@ -81,6 +81,8 @@
         val taskInfo: RunningTaskInfo,
         val isRequestingImmersive: Boolean,
         val inFullImmersiveState: Boolean,
+        val hasGlobalFocus: Boolean,
+        val enableMaximizeLongClick: Boolean,
     ) : Data()
 
     private val decorThemeUtil = DecorThemeUtil(context)
@@ -159,24 +161,38 @@
     }
 
     override fun bindData(data: HeaderData) {
-        bindData(data.taskInfo, data.isRequestingImmersive, data.inFullImmersiveState)
+        bindData(
+            data.taskInfo,
+            data.isRequestingImmersive,
+            data.inFullImmersiveState,
+            data.hasGlobalFocus,
+            data.enableMaximizeLongClick
+        )
     }
 
     private fun bindData(
         taskInfo: RunningTaskInfo,
         isRequestingImmersive: Boolean,
         inFullImmersiveState: Boolean,
+        hasGlobalFocus: Boolean,
+        enableMaximizeLongClick: Boolean,
     ) {
         if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
-            bindDataWithThemedHeaders(taskInfo, isRequestingImmersive, inFullImmersiveState)
+            bindDataWithThemedHeaders(
+                taskInfo,
+                isRequestingImmersive,
+                inFullImmersiveState,
+                hasGlobalFocus,
+                enableMaximizeLongClick,
+            )
         } else {
-            bindDataLegacy(taskInfo)
+            bindDataLegacy(taskInfo, hasGlobalFocus)
         }
     }
 
-    private fun bindDataLegacy(taskInfo: RunningTaskInfo) {
-        captionView.setBackgroundColor(getCaptionBackgroundColor(taskInfo))
-        val color = getAppNameAndButtonColor(taskInfo)
+    private fun bindDataLegacy(taskInfo: RunningTaskInfo, hasGlobalFocus: Boolean) {
+        captionView.setBackgroundColor(getCaptionBackgroundColor(taskInfo, hasGlobalFocus))
+        val color = getAppNameAndButtonColor(taskInfo, hasGlobalFocus)
         val alpha = Color.alpha(color)
         closeWindowButton.imageTintList = ColorStateList.valueOf(color)
         maximizeWindowButton.imageTintList = ColorStateList.valueOf(color)
@@ -210,9 +226,11 @@
     private fun bindDataWithThemedHeaders(
         taskInfo: RunningTaskInfo,
         requestingImmersive: Boolean,
-        inFullImmersiveState: Boolean
+        inFullImmersiveState: Boolean,
+        hasGlobalFocus: Boolean,
+        enableMaximizeLongClick: Boolean,
     ) {
-        val header = fillHeaderInfo(taskInfo)
+        val header = fillHeaderInfo(taskInfo, hasGlobalFocus)
         val headerStyle = getHeaderStyle(header)
 
         // Caption Background
@@ -276,6 +294,16 @@
                 drawableInsets = closeDrawableInsets
             )
         }
+        if (!enableMaximizeLongClick) {
+            maximizeButtonView.cancelHoverAnimation()
+        }
+        maximizeButtonView.hoverDisabled = !enableMaximizeLongClick
+        maximizeWindowButton.onLongClickListener = if (enableMaximizeLongClick) {
+            onLongClickListener
+        } else {
+            // Disable long-click to open maximize menu when in immersive.
+            null
+        }
     }
 
     override fun onHandleMenuOpened() {}
@@ -286,14 +314,6 @@
         }
     }
 
-    fun setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition: Boolean) {
-        // If animating a task resize or reposition, cancel any running hover animations
-        if (animatingTaskResizeOrReposition) {
-            maximizeButtonView.cancelHoverAnimation()
-        }
-        maximizeButtonView.hoverDisabled = animatingTaskResizeOrReposition
-    }
-
     fun onMaximizeWindowHoverExit() {
         maximizeButtonView.cancelHoverAnimation()
     }
@@ -359,6 +379,11 @@
     private fun shouldShowExitFullImmersiveIcon(
         requestingImmersive: Boolean,
         inFullImmersiveState: Boolean
+    ): Boolean = isInFullImmersiveStateAndRequesting(requestingImmersive, inFullImmersiveState)
+
+    private fun isInFullImmersiveStateAndRequesting(
+        requestingImmersive: Boolean,
+        inFullImmersiveState: Boolean
     ): Boolean = Flags.enableFullyImmersiveInDesktop()
             && requestingImmersive && inFullImmersiveState
 
@@ -455,7 +480,7 @@
         }
     }
 
-    private fun fillHeaderInfo(taskInfo: RunningTaskInfo): Header {
+    private fun fillHeaderInfo(taskInfo: RunningTaskInfo, hasGlobalFocus: Boolean): Header {
         return Header(
             type = if (taskInfo.isTransparentCaptionBarAppearance) {
                 Header.Type.CUSTOM
@@ -463,7 +488,7 @@
                 Header.Type.DEFAULT
             },
             appTheme = decorThemeUtil.getAppTheme(taskInfo),
-            isFocused = taskInfo.isFocused,
+            isFocused = hasGlobalFocus,
             isAppearanceCaptionLight = taskInfo.isLightCaptionBarAppearance
         )
     }
@@ -544,19 +569,19 @@
     }
 
     @ColorInt
-    private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int {
+    private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo, hasGlobalFocus: Boolean): Int {
         if (taskInfo.isTransparentCaptionBarAppearance) {
             return Color.TRANSPARENT
         }
         val materialColorAttr: Int =
             if (isDarkMode()) {
-                if (!taskInfo.isFocused) {
+                if (!hasGlobalFocus) {
                     materialColorSurfaceContainerHigh
                 } else {
                     materialColorSurfaceDim
                 }
             } else {
-                if (!taskInfo.isFocused) {
+                if (!hasGlobalFocus) {
                     materialColorSurfaceContainerLow
                 } else {
                     materialColorSecondaryContainer
@@ -569,7 +594,7 @@
     }
 
     @ColorInt
-    private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo): Int {
+    private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo, hasGlobalFocus: Boolean): Int {
         val materialColorAttr = when {
             taskInfo.isTransparentCaptionBarAppearance &&
                     taskInfo.isLightCaptionBarAppearance -> materialColorOnSecondaryContainer
@@ -579,8 +604,8 @@
             else -> materialColorOnSecondaryContainer
         }
         val appDetailsOpacity = when {
-            isDarkMode() && !taskInfo.isFocused -> DARK_THEME_UNFOCUSED_OPACITY
-            !isDarkMode() && !taskInfo.isFocused -> LIGHT_THEME_UNFOCUSED_OPACITY
+            isDarkMode() && !hasGlobalFocus -> DARK_THEME_UNFOCUSED_OPACITY
+            !isDarkMode() && !hasGlobalFocus -> LIGHT_THEME_UNFOCUSED_OPACITY
             else -> FOCUSED_OPACITY
         }
         context.withStyledAttributes(null, intArrayOf(materialColorAttr), 0, 0) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 7640cb1..b7ddfd1 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -154,6 +154,7 @@
                         TaggedCujTransitionMatcher(associatedTransitionRequired = false)
                     )
                     .build(),
+                // TODO(373638597) Add AppLayerIncreasesInSize assertion
                 assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS
             )
 
@@ -208,7 +209,7 @@
                 assertions =
                 AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
                         listOf(
-                            AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+                            // TODO(373638597) Add AppLayerIncreasesInSize assertion
                             AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
                             AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
                         ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
index 0b98ba2..aa4f133 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
@@ -24,7 +24,7 @@
 import android.tools.flicker.config.FlickerServiceConfig
 import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
 import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE
-import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import com.android.wm.shell.scenarios.MaximiseAppWithCornerResize
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -35,7 +35,7 @@
  * Assert that the maximum window size constraint is maintained.
  */
 @RunWith(FlickerServiceJUnit4ClassRunner::class)
-class ResizeAppToMaximumWindowSizeLandscape : ResizeAppWithCornerResize(
+class ResizeAppToMaximumWindowSizeLandscape : MaximiseAppWithCornerResize(
     rotation = Rotation.ROTATION_90
 ) {
     @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
index b1c04d3..e6b1ccf 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
@@ -23,7 +23,7 @@
 import android.tools.flicker.config.FlickerServiceConfig
 import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
 import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE
-import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import com.android.wm.shell.scenarios.MaximiseAppWithCornerResize
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -34,7 +34,7 @@
  * Assert that the maximum window size constraint is maintained.
  */
 @RunWith(FlickerServiceJUnit4ClassRunner::class)
-class ResizeAppToMaximumWindowSizePortrait : ResizeAppWithCornerResize() {
+class ResizeAppToMaximumWindowSizePortrait : MaximiseAppWithCornerResize() {
     @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
     @Test
     override fun resizeAppWithCornerResizeToMaximumSize() =
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt
new file mode 100644
index 0000000..6637b01
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximiseAppWithCornerResize.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper.AppProperty
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Test Base Class")
+abstract class MaximiseAppWithCornerResize(
+    val rotation: Rotation = Rotation.ROTATION_0,
+    val appProperty: AppProperty = AppProperty.STANDARD
+) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val maxResizeChange = 3000
+    private val testApp =
+        DesktopModeAppHelper(
+            when (appProperty) {
+                AppProperty.STANDARD -> SimpleAppHelper(instrumentation)
+                AppProperty.NON_RESIZABLE -> NonResizeableAppHelper(instrumentation)
+            }
+        )
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        testApp.cornerResize(
+            wmHelper,
+            device,
+            DesktopModeAppHelper.Corners.RIGHT_TOP,
+            maxResizeChange,
+            -maxResizeChange
+        )
+    }
+
+    @Test
+    open fun resizeAppWithCornerResizeToMaximumSize() {
+        testApp.cornerResize(
+            wmHelper,
+            device,
+            DesktopModeAppHelper.Corners.LEFT_BOTTOM,
+            -maxResizeChange,
+            maxResizeChange
+        )
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
index bd25639..a7cebf4 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -19,11 +19,13 @@
 import android.app.Instrumentation
 import android.tools.NavBar
 import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.UiDevice
 import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper.AppProperty
 import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.window.flags.Flags
@@ -63,6 +65,7 @@
     fun setup() {
         Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
         tapl.setEnableRotation(true)
+        ChangeDisplayOrientationRule.setRotation(rotation)
         tapl.setExpectedRotation(rotation.value)
         testApp.enterDesktopWithDrag(wmHelper, device)
     }
@@ -78,34 +81,8 @@
         )
     }
 
-    @Test
-    open fun resizeAppWithCornerResizeToMaximumSize() {
-        val maxResizeChange = 3000
-        testApp.cornerResize(
-            wmHelper,
-            device,
-            DesktopModeAppHelper.Corners.RIGHT_TOP,
-            maxResizeChange,
-            -maxResizeChange
-        )
-        testApp.cornerResize(
-            wmHelper,
-            device,
-            DesktopModeAppHelper.Corners.LEFT_BOTTOM,
-            -maxResizeChange,
-            maxResizeChange
-        )
-    }
-
     @After
     fun teardown() {
         testApp.exit(wmHelper)
     }
-
-    companion object {
-        enum class AppProperty {
-            STANDARD,
-            NON_RESIZABLE
-        }
-    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
index 2980d51..e176f47 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.flicker.appcompat
 
 import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
 import android.tools.flicker.assertions.FlickerTest
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
@@ -109,9 +108,7 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTest> {
-            return LegacyFlickerTestFactory.nonRotationTests(
-                supportedRotations = listOf(Rotation.ROTATION_90)
-            )
+            return LegacyFlickerTestFactory.nonRotationTests()
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
index 2484f67..9b8c949 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
@@ -20,7 +20,6 @@
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.RequiresDevice
 import android.tools.NavBar
-import android.tools.Rotation
 import android.tools.flicker.assertions.FlickerTest
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
@@ -266,8 +265,7 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTest> {
             return LegacyFlickerTestFactory.nonRotationTests(
-                supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
-                supportedRotations = listOf(Rotation.ROTATION_90)
+                supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
             )
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
index 77423af..43ee186 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.flicker.appcompat
 
 import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
 import android.tools.flicker.assertions.FlickerTest
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
@@ -91,9 +90,7 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTest> {
-            return LegacyFlickerTestFactory.nonRotationTests(
-                supportedRotations = listOf(Rotation.ROTATION_90)
-            )
+            return LegacyFlickerTestFactory.nonRotationTests()
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 1da4ef6..266e484 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -53,6 +53,7 @@
         return new BackMotionEvent(
                 /* touchX = */ touchX,
                 /* touchY = */ 0,
+                /* frameTime = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ false,
                 /* swipeEdge = */ BackEvent.EDGE_LEFT,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 2235c20..2cc52c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -221,6 +221,7 @@
         BackMotionEvent(
             /* touchX = */ touchX,
             /* touchY = */ 0f,
+            /* frameTime = */ 0,
             /* progress = */ progress,
             /* triggerBack = */ false,
             /* swipeEdge = */ BackEvent.EDGE_LEFT,
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 094af96..a1d4a1a 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
@@ -26,6 +26,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.wm.shell.ShellTestCase;
 
 import org.junit.Before;
@@ -45,6 +46,7 @@
     private TestableBubblePositioner mPositioner;
     private BubbleOverflow mOverflow;
     private BubbleExpandedViewManager mExpandedViewManager;
+    private BubbleLogger mBubbleLogger;
 
     @Mock
     private BubbleController mBubbleController;
@@ -58,6 +60,7 @@
         mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(mBubbleController);
         mPositioner = new TestableBubblePositioner(mContext,
                 mContext.getSystemService(WindowManager.class));
+        mBubbleLogger = new BubbleLogger(new UiEventLoggerFake());
         when(mBubbleController.getPositioner()).thenReturn(mPositioner);
         when(mBubbleController.getStackView()).thenReturn(mBubbleStackView);
 
@@ -77,7 +80,7 @@
 
     @Test
     public void test_initialize_forBubbleBar() {
-        mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner);
+        mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner, mBubbleLogger);
 
         assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull();
         assertThat(mOverflow.getExpandedView()).isNull();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt
index cae6095..b137468 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt
@@ -15,23 +15,42 @@
  */
 package com.android.wm.shell.desktopmode
 
+import android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS
+import android.graphics.Rect
+import android.os.Binder
 import android.os.IBinder
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.Surface
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TransitionFlags
+import android.view.WindowManager.TransitionType
+import android.window.TransitionInfo
+import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
@@ -40,14 +59,19 @@
 /**
  * Tests for [DesktopFullImmersiveTransitionHandler].
  *
- * Usage: atest WMShellUnitTests:DesktopFullImmersiveTransitionHandler
+ * Usage: atest WMShellUnitTests:DesktopFullImmersiveTransitionHandlerTest
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() {
 
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
     @Mock private lateinit var mockTransitions: Transitions
     private lateinit var desktopRepository: DesktopRepository
+    @Mock private lateinit var mockDisplayController: DisplayController
+    @Mock private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
+    @Mock private lateinit var mockDisplayLayout: DisplayLayout
     private val transactionSupplier = { SurfaceControl.Transaction() }
 
     private lateinit var immersiveHandler: DesktopFullImmersiveTransitionHandler
@@ -57,19 +81,25 @@
         desktopRepository = DesktopRepository(
             context, ShellInit(TestShellExecutor()), mock(), mock()
         )
+        whenever(mockDisplayController.getDisplayLayout(DEFAULT_DISPLAY))
+            .thenReturn(mockDisplayLayout)
+        whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { invocation ->
+            (invocation.getArgument(0) as Rect).set(STABLE_BOUNDS)
+        }
         immersiveHandler = DesktopFullImmersiveTransitionHandler(
             transitions = mockTransitions,
             desktopRepository = desktopRepository,
-            transactionSupplier = transactionSupplier
+            displayController = mockDisplayController,
+            shellTaskOrganizer = mockShellTaskOrganizer,
+            transactionSupplier = transactionSupplier,
         )
     }
 
     @Test
     fun enterImmersive_transitionReady_updatesRepository() {
         val task = createFreeformTask()
-        val wct = WindowContainerTransaction()
         val mockBinder = mock(IBinder::class.java)
-        whenever(mockTransitions.startTransition(TRANSIT_CHANGE, wct, immersiveHandler))
+        whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)))
             .thenReturn(mockBinder)
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
@@ -77,18 +107,55 @@
             immersive = false
         )
 
-        immersiveHandler.enterImmersive(task, wct)
-        immersiveHandler.onTransitionReady(mockBinder)
+        immersiveHandler.moveTaskToImmersive(task)
+        immersiveHandler.onTransitionReady(
+            transition = mockBinder,
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply {
+                        taskInfo = task
+                    }
+                )
+            )
+        )
 
         assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isTrue()
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE)
+    fun enterImmersive_savesPreImmersiveBounds() {
+        val task = createFreeformTask()
+        val mockBinder = mock(IBinder::class.java)
+        whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)))
+            .thenReturn(mockBinder)
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = task.displayId,
+            taskId = task.taskId,
+            immersive = false
+        )
+        assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNull()
+
+        immersiveHandler.moveTaskToImmersive(task)
+        immersiveHandler.onTransitionReady(
+            transition = mockBinder,
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply {
+                        taskInfo = task
+                    }
+                )
+            )
+        )
+
+        assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNotNull()
+    }
+
+    @Test
     fun exitImmersive_transitionReady_updatesRepository() {
         val task = createFreeformTask()
-        val wct = WindowContainerTransaction()
         val mockBinder = mock(IBinder::class.java)
-        whenever(mockTransitions.startTransition(TRANSIT_CHANGE, wct, immersiveHandler))
+        whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)))
             .thenReturn(mockBinder)
         desktopRepository.setTaskInFullImmersiveState(
             displayId = task.displayId,
@@ -96,8 +163,70 @@
             immersive = true
         )
 
-        immersiveHandler.exitImmersive(task, wct)
-        immersiveHandler.onTransitionReady(mockBinder)
+        immersiveHandler.moveTaskToNonImmersive(task)
+        immersiveHandler.onTransitionReady(
+            transition = mockBinder,
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply {
+                        taskInfo = task
+                    }
+                )
+            )
+        )
+
+        assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE)
+    fun exitImmersive_onTransitionReady_removesBoundsBeforeImmersive() {
+        val task = createFreeformTask()
+        val mockBinder = mock(IBinder::class.java)
+        whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)))
+            .thenReturn(mockBinder)
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = task.displayId,
+            taskId = task.taskId,
+            immersive = true
+        )
+        desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
+
+        immersiveHandler.moveTaskToNonImmersive(task)
+        immersiveHandler.onTransitionReady(
+            transition = mockBinder,
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply {
+                        taskInfo = task
+                    }
+                )
+            )
+        )
+
+        assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
+    }
+
+    @Test
+    fun onTransitionReady_displayRotation_exitsImmersive() {
+        val task = createFreeformTask()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = task.displayId,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.onTransitionReady(
+            transition = mock(IBinder::class.java),
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply {
+                        taskInfo = task
+                        setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90)
+                    }
+                )
+            )
+        )
 
         assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse()
     }
@@ -105,28 +234,361 @@
     @Test
     fun enterImmersive_inProgress_ignores() {
         val task = createFreeformTask()
-        val wct = WindowContainerTransaction()
         val mockBinder = mock(IBinder::class.java)
-        whenever(mockTransitions.startTransition(TRANSIT_CHANGE, wct, immersiveHandler))
+        whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)))
             .thenReturn(mockBinder)
 
-        immersiveHandler.enterImmersive(task, wct)
-        immersiveHandler.enterImmersive(task, wct)
+        immersiveHandler.moveTaskToImmersive(task)
+        immersiveHandler.moveTaskToImmersive(task)
 
-        verify(mockTransitions, times(1)).startTransition(TRANSIT_CHANGE, wct, immersiveHandler)
+        verify(mockTransitions, times(1))
+            .startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))
     }
 
     @Test
     fun exitImmersive_inProgress_ignores() {
         val task = createFreeformTask()
-        val wct = WindowContainerTransaction()
         val mockBinder = mock(IBinder::class.java)
-        whenever(mockTransitions.startTransition(TRANSIT_CHANGE, wct, immersiveHandler))
+        whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)))
             .thenReturn(mockBinder)
 
-        immersiveHandler.exitImmersive(task, wct)
-        immersiveHandler.exitImmersive(task, wct)
+        immersiveHandler.moveTaskToNonImmersive(task)
+        immersiveHandler.moveTaskToNonImmersive(task)
 
-        verify(mockTransitions, times(1)).startTransition(TRANSIT_CHANGE, wct, immersiveHandler)
+        verify(mockTransitions, times(1))
+            .startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_inImmersive_addsPendingExit() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+
+        assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit ->
+            exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
+                    && exit.taskId == task.taskId
+        }).isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_notInImmersive_doesNotAddPendingExit() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = false
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+
+        assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit ->
+            exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
+                    && exit.taskId == task.taskId
+        }).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byDisplay_inImmersive_changesTaskBounds() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+
+        assertThat(wct.hasBoundsChange(task.token)).isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byDisplay_notInImmersive_doesNotChangeTaskBounds() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = false
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+
+        assertThat(wct.hasBoundsChange(task.token)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byTask_inImmersive_changesTaskBounds() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+
+        assertThat(wct.hasBoundsChange(task.token)).isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byTask_notInImmersive_doesNotChangeTaskBounds() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = false
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(wct, task.taskId)
+
+        assertThat(wct.hasBoundsChange(task.token)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byTask_inImmersive_addsPendingExitOnRun() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(wct, task.taskId)?.invoke(transition)
+
+        assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit ->
+            exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
+                    && exit.taskId == task.taskId
+        }).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byTask_notInImmersive_doesNotAddPendingExitOnRun() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = false
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(wct, task.taskId)?.invoke(transition)
+
+        assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit ->
+            exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
+                    && exit.taskId == task.taskId
+        }).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun onTransitionReady_pendingExit_removesPendingExit() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+        immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+
+        immersiveHandler.onTransitionReady(
+            transition = transition,
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
+                )
+            )
+        )
+
+        assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit ->
+            exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
+                    && exit.taskId == task.taskId
+        }).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun onTransitionReady_pendingExit_updatesRepository() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+        immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+
+        immersiveHandler.onTransitionReady(
+            transition = transition,
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
+                )
+            )
+        )
+
+        assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
+        Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE
+    )
+    fun onTransitionReady_pendingExit_removesBoundsBeforeImmersive() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+        desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600))
+        immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY)
+
+        immersiveHandler.onTransitionReady(
+            transition = transition,
+            info = createTransitionInfo(
+                changes = listOf(
+                    TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
+                )
+            )
+        )
+
+        assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    @DisableFlags(Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE)
+    fun exitImmersiveIfApplicable_changesBoundsToMaximize() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+
+        assertThat(
+            wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task))
+        ).isTrue()
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
+        Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE
+    )
+    fun exitImmersiveIfApplicable_preImmersiveBoundsSaved_changesBoundsToPreImmersiveBounds() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+        val preImmersiveBounds = Rect(100, 100, 500, 500)
+        desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, preImmersiveBounds)
+
+        immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+
+        assertThat(
+            wct.hasBoundsChange(task.token, preImmersiveBounds)
+        ).isTrue()
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
+        Flags.FLAG_ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE,
+        Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+    )
+    fun exitImmersiveIfApplicable_preImmersiveBoundsNotSaved_changesBoundsToInitialBounds() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task)
+
+        assertThat(
+            wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task))
+        ).isTrue()
+    }
+
+    private fun createTransitionInfo(
+        @TransitionType type: Int = TRANSIT_CHANGE,
+        @TransitionFlags flags: Int = 0,
+        changes: List<TransitionInfo.Change> = emptyList()
+    ): TransitionInfo = TransitionInfo(type, flags).apply {
+        changes.forEach { change -> addChange(change) }
+    }
+
+    private fun WindowContainerTransaction.hasBoundsChange(token: WindowContainerToken): Boolean =
+        this.changes.any { change ->
+            change.key == token.asBinder()
+                    && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
+        }
+
+    private fun WindowContainerTransaction.hasBoundsChange(
+        token: WindowContainerToken,
+        bounds: Rect,
+    ): Boolean = this.changes.any { change ->
+        change.key == token.asBinder()
+                && (change.value.windowSetMask and WINDOW_CONFIG_BOUNDS) != 0
+                && change.value.configuration.windowConfiguration.bounds == bounds
+    }
+
+    companion object {
+        private val STABLE_BOUNDS = Rect(0, 100, 2000, 1900)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index d7a132d..0825b6b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -16,10 +16,12 @@
 
 package com.android.wm.shell.desktopmode
 
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
+import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations
+import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import com.android.window.flags.Flags
@@ -27,15 +29,16 @@
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.NO_SESSION_ID
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskSizeUpdate
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON
-import kotlinx.coroutines.runBlocking
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.mockito.kotlin.eq
@@ -50,40 +53,87 @@
     @JvmField
     @Rule(order = 0)
     val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
-            .mockStatic(FrameworkStatsLog::class.java)
-            .mockStatic(EventLogTags::class.java).build()!!
+        .mockStatic(FrameworkStatsLog::class.java)
+        .mockStatic(EventLogTags::class.java).build()!!
 
     @JvmField
     @Rule(order = 1)
     val setFlagsRule = SetFlagsRule()
 
     @Test
-    fun logSessionEnter_enterReason() = runBlocking {
-        desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER)
+    fun logSessionEnter_logsEnterReasonWithNewSessionId() {
+        desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
 
+        val sessionId = desktopModeEventLogger.currentSessionId.get()
+        assertThat(sessionId).isNotEqualTo(NO_SESSION_ID)
         verify {
             FrameworkStatsLog.write(
                 eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
                 /* event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER),
                 /* enter_reason */
-                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER),
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER),
                 /* exit_reason */
                 eq(0),
                 /* sessionId */
-                eq(SESSION_ID)
+                eq(sessionId)
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellEnterDesktopMode(
-                eq(EnterReason.UNKNOWN_ENTER.reason),
-                eq(SESSION_ID))
+                eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
+                eq(sessionId)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logSessionExit_exitReason() = runBlocking {
-        desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT)
+    fun logSessionEnter_ongoingSession_logsEnterReasonWithNewSessionId() {
+        val previousSessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
+
+        val sessionId = desktopModeEventLogger.currentSessionId.get()
+        assertThat(sessionId).isNotEqualTo(NO_SESSION_ID)
+        assertThat(sessionId).isNotEqualTo(previousSessionId)
+        verify {
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
+                /* event */
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER),
+                /* enter_reason */
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER),
+                /* exit_reason */
+                eq(0),
+                /* sessionId */
+                eq(sessionId)
+            )
+        }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verify {
+            EventLogTags.writeWmShellEnterDesktopMode(
+                eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
+                eq(sessionId)
+            )
+        }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logSessionExit_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logSessionExit_logsExitReasonAndClearsSessionId() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
 
         verify {
             FrameworkStatsLog.write(
@@ -93,24 +143,39 @@
                 /* enter_reason */
                 eq(0),
                 /* exit_reason */
-                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT),
+                eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT),
                 /* sessionId */
-                eq(SESSION_ID)
+                eq(sessionId)
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellExitDesktopMode(
-                eq(ExitReason.UNKNOWN_EXIT.reason),
-                eq(SESSION_ID))
+                eq(ExitReason.DRAG_TO_EXIT.reason),
+                eq(sessionId)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
     }
 
     @Test
-    fun logTaskAdded_taskUpdate() = runBlocking {
-        desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE)
+    fun logTaskAdded_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskAdded_logsTaskUpdate() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
                 /* instance_id */
@@ -126,36 +191,52 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskRemoved_taskUpdate() = runBlocking {
-        desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE)
+    fun logTaskRemoved_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskRemoved_taskUpdate() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
                 /* instance_id */
@@ -171,39 +252,57 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskInfoChanged_taskUpdate() = runBlocking {
-        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE)
+    fun logTaskInfoChanged_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskInfoChanged_taskUpdate() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 /* instance_id */
                 eq(TASK_UPDATE.instanceId),
                 /* uid */
@@ -217,40 +316,51 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking {
-        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
-            createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT))
+    fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskInfoChanged(
+            createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT)
+        )
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 /* instance_id */
                 eq(TASK_UPDATE.instanceId),
                 /* uid */
@@ -264,42 +374,53 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* minimize_reason */
                 eq(MinimizeReason.TASK_LIMIT.reason),
                 /* unminimize_reason */
                 eq(UNSET_UNMINIMIZE_REASON),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(MinimizeReason.TASK_LIMIT.reason),
                 eq(UNSET_UNMINIMIZE_REASON),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
-    fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking {
-        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
-            createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP))
+    fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskInfoChanged(
+            createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP)
+        )
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 /* instance_id */
                 eq(TASK_UPDATE.instanceId),
                 /* uid */
@@ -313,39 +434,55 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* minimize_reason */
                 eq(UNSET_MINIMIZE_REASON),
                 /* unminimize_reason */
                 eq(UnminimizeReason.TASKBAR_TAP.reason),
                 /* visible_task_count */
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
-
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
         verify {
             EventLogTags.writeWmShellDesktopModeTaskUpdate(
-                eq(FrameworkStatsLog
-                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                eq(
+                    FrameworkStatsLog
+                        .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+                ),
                 eq(TASK_UPDATE.instanceId),
                 eq(TASK_UPDATE.uid),
                 eq(TASK_UPDATE.taskHeight),
                 eq(TASK_UPDATE.taskWidth),
                 eq(TASK_UPDATE.taskX),
                 eq(TASK_UPDATE.taskY),
-                eq(SESSION_ID),
+                eq(sessionId),
                 eq(UNSET_MINIMIZE_REASON),
                 eq(UnminimizeReason.TASKBAR_TAP.reason),
-                eq(TASK_COUNT))
+                eq(TASK_COUNT)
+            )
         }
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+    }
+
+    @Test
+    fun logTaskResizingStarted_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
-    fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() = runBlocking {
-        desktopModeEventLogger.logTaskResizingStarted(sessionId = SESSION_ID, createTaskSizeUpdate())
+    fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
                 /* resize_trigger */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
                 /* resizing_stage */
@@ -353,7 +490,7 @@
                 /* input_method */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
                 /* desktop_mode_session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* instance_id */
                 eq(TASK_SIZE_UPDATE.instanceId),
                 /* uid */
@@ -366,15 +503,27 @@
                 eq(TASK_SIZE_UPDATE.displayArea),
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+    }
+
+    @Test
+    fun logTaskResizingEnded_noOngoingSession_doesNotLog() {
+        desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE)
+
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
-    fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() = runBlocking {
-        desktopModeEventLogger.logTaskResizingEnded(sessionId = SESSION_ID, createTaskSizeUpdate())
+    fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() {
+        val sessionId = startDesktopModeSession()
+
+        desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE)
 
         verify {
-            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+            FrameworkStatsLog.write(
+                eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
                 /* resize_trigger */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
                 /* resizing_stage */
@@ -382,7 +531,7 @@
                 /* input_method */
                 eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
                 /* desktop_mode_session_id */
-                eq(SESSION_ID),
+                eq(sessionId),
                 /* instance_id */
                 eq(TASK_SIZE_UPDATE.instanceId),
                 /* uid */
@@ -395,10 +544,49 @@
                 eq(TASK_SIZE_UPDATE.displayArea),
             )
         }
+        verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+    }
+
+    private fun startDesktopModeSession(): Int {
+        desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
+        clearInvocations(staticMockMarker(FrameworkStatsLog::class.java))
+        clearInvocations(staticMockMarker(EventLogTags::class.java))
+        return desktopModeEventLogger.currentSessionId.get()
+    }
+
+    @Test
+    fun logTaskInfoStateInit_logsTaskInfoChangedStateInit() {
+        desktopModeEventLogger.logTaskInfoStateInit()
+        verify {
+            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+                /* task_event */
+                eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD),
+                /* instance_id */
+                eq(0),
+                /* uid */
+                eq(0),
+                /* task_height */
+                eq(0),
+                /* task_width */
+                eq(0),
+                /* task_x */
+                eq(0),
+                /* task_y */
+                eq(0),
+                /* session_id */
+                eq(0),
+                /* minimize_reason */
+                eq(UNSET_MINIMIZE_REASON),
+                /* unminimize_reason */
+                eq(UNSET_UNMINIMIZE_REASON),
+                /* visible_task_count */
+                eq(0)
+            )
+        }
     }
 
     private companion object {
-        private const val SESSION_ID = 1
+        private const val sessionId = 1
         private const val TASK_ID = 1
         private const val TASK_UID = 1
         private const val TASK_X = 0
@@ -423,23 +611,12 @@
             DISPLAY_AREA,
         )
 
-        private fun createTaskSizeUpdate(
-            resizeTrigger: ResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
-            inputMethod: InputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
-        ) = TaskSizeUpdate(
-            resizeTrigger,
-            inputMethod,
-            TASK_ID,
-            TASK_UID,
-            TASK_HEIGHT,
-            TASK_WIDTH,
-            DISPLAY_AREA,
-        )
-
         private fun createTaskUpdate(
             minimizeReason: MinimizeReason? = null,
             unminimizeReason: UnminimizeReason? = null,
-        ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
-            unminimizeReason, TASK_COUNT)
+        ) = TaskUpdate(
+            TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
+            unminimizeReason, TASK_COUNT
+        )
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index daf7e7d..f25faa5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -59,8 +59,8 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
-import junit.framework.Assert.assertNotNull
-import junit.framework.Assert.assertNull
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -115,6 +115,9 @@
     val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
     verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
     initRunnableCaptor.value.run()
+    // verify this initialisation interaction to leave the desktopmodeEventLogger mock in a
+    // consistent state with no outstanding interactions when test cases start executing.
+    verify(desktopModeEventLogger).logTaskInfoStateInit()
   }
 
   @Test
@@ -139,8 +142,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
-    verify(desktopModeEventLogger, never()).logTaskAdded(any(), any())
+    verify(desktopModeEventLogger, never()).logSessionEnter(any())
+    verify(desktopModeEventLogger, never()).logTaskAdded(any())
   }
 
   @Test
@@ -225,11 +228,10 @@
   @Test
   fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -238,7 +240,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
@@ -256,11 +259,10 @@
   @Test
   fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -269,7 +271,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
@@ -287,11 +290,10 @@
   @Test
   fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -300,7 +302,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
     // next transition involving freeform windows
@@ -321,11 +324,10 @@
     // Tests for AppFromOverview precedence in compared to cancelled Overview
 
     // previous exit to overview transition
-    val previousSessionId = 1
     // add a freeform task
     val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
-    transitionObserver.setLoggerSessionId(previousSessionId)
+    transitionObserver.isSessionActive = true
     val previousTransitionInfo =
         TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
             .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -334,7 +336,8 @@
     callOnTransitionReady(previousTransitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
@@ -376,11 +379,10 @@
   fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() {
     val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
     // Previous Exit reason recorded as Screen Off
-    val sessionId = 1
     transitionObserver.addTaskInfosToCachedMap(freeformTask)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
     callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
     // Enter desktop through back transition, this happens when user enters after dismissing
     // keyguard
     val change = createChange(TRANSIT_TO_FRONT, freeformTask)
@@ -396,11 +398,10 @@
   fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() {
     val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
     // Previous Exit reason recorded as Screen Off
-    val sessionId = 1
     transitionObserver.addTaskInfosToCachedMap(freeformTask)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
     callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
 
     // Enter desktop through app handle drag. This represents cases where instead of moving to
     // desktop right after turning the screen on, we move to fullscreen then move another task
@@ -416,24 +417,22 @@
   }
 
   @Test
-  fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() {
-    val sessionId = 1
+  fun transitSleep_logTaskRemovedAndExitReasonScreenOff() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -441,15 +440,14 @@
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -459,16 +457,14 @@
             .build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -476,16 +472,14 @@
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
-    val sessionId = 1
+  fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // window mode changing from FREEFORM to FULLSCREEN
     val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -493,15 +487,14 @@
         TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
   }
 
   @Test
-  fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
-    val sessionId = 1
+  fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // recents transition
     val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
@@ -510,31 +503,30 @@
     callOnTransitionReady(transitionInfo)
 
     verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
   }
 
   @Test
-  fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
-    val sessionId = 1
+  fun transitClose_logTaskRemovedAndExitReasonTaskFinished() {
     // add a freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task closing
     val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
     val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
+    verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
   }
 
   @Test
   fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
-    val sessionId = 1
     // add a freeform task to an existing session
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // recents transition sent freeform window to back
     val change = createChange(TRANSIT_TO_BACK, taskInfo)
@@ -543,7 +535,8 @@
     callOnTransitionReady(transitionInfo1)
 
     verifyTaskRemovedAndExitLogging(
-        sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+        ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+    )
 
     val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
     callOnTransitionReady(transitionInfo2)
@@ -554,10 +547,9 @@
 
   @Test
   fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
-    val sessionId = 1
     // add an existing freeform task
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // new freeform task added
     val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
@@ -565,18 +557,16 @@
     callOnTransitionReady(transitionInfo)
 
     verify(desktopModeEventLogger, times(1))
-        .logTaskAdded(eq(sessionId),
-            eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
-    verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
+        .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
+    verify(desktopModeEventLogger, never()).logSessionEnter(any())
   }
 
   @Test
   fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
-    val sessionId = 1
     // add an existing freeform task
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task position changed
     val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
@@ -588,18 +578,17 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId),
-            eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1)))
+            eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
+        )
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
   @Test
   fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
-    val sessionId = 1
     // add an existing freeform task
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
     transitionObserver.addTaskInfosToCachedMap(taskInfo)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task resized
     val newTaskInfo =
@@ -615,23 +604,22 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId),
             eq(
                 DEFAULT_TASK_UPDATE.copy(
                     taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100,
-                    visibleTaskCount = 1)))
+                    visibleTaskCount = 1))
+        )
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
   @Test
   fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
-    val sessionId = 1
     // add 2 existing freeform task
     val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
     val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
     transitionObserver.addTaskInfosToCachedMap(taskInfo1)
     transitionObserver.addTaskInfosToCachedMap(taskInfo2)
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // task 1 position update
     val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
@@ -643,8 +631,9 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
-                taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2)))
+            eq(DEFAULT_TASK_UPDATE.copy(
+                taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
+        )
     verifyZeroInteractions(desktopModeEventLogger)
 
     // task 2 resize
@@ -663,7 +652,6 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId),
             eq(
                 DEFAULT_TASK_UPDATE.copy(
                     instanceId = 2,
@@ -676,11 +664,10 @@
 
   @Test
   fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
-    val sessionId = 1
     // add two existing freeform tasks
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
     transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
-    transitionObserver.setLoggerSessionId(sessionId)
+    transitionObserver.isSessionActive = true
 
     // new freeform task closed
     val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
@@ -688,9 +675,11 @@
     callOnTransitionReady(transitionInfo)
 
     verify(desktopModeEventLogger, times(1))
-        .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
-            instanceId = 2, visibleTaskCount = 1)))
-    verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
+        .logTaskRemoved(
+            eq(DEFAULT_TASK_UPDATE.copy(
+                instanceId = 2, visibleTaskCount = 1))
+        )
+    verify(desktopModeEventLogger, never()).logSessionExit(any())
   }
 
   /** Simulate calling the onTransitionReady() method */
@@ -703,10 +692,9 @@
   }
 
   private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
-    val sessionId = transitionObserver.getLoggerSessionId()
-    assertNotNull(sessionId)
-    verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason))
-    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate))
+    assertTrue(transitionObserver.isSessionActive)
+    verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate))
     ExtendedMockito.verify {
         Trace.setCounter(
             eq(Trace.TRACE_TAG_WINDOW_MANAGER),
@@ -722,14 +710,13 @@
   }
 
   private fun verifyTaskRemovedAndExitLogging(
-      sessionId: Int,
       exitReason: ExitReason,
       taskUpdate: TaskUpdate
   ) {
-    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate))
-    verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason))
+    assertFalse(transitionObserver.isSessionActive)
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
+    verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
     verifyZeroInteractions(desktopModeEventLogger)
-    assertNull(transitionObserver.getLoggerSessionId())
   }
 
   private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 1308114..3e22803 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -746,6 +746,18 @@
     }
 
     @Test
+    fun removeFreeformTask_removesTaskBoundsBeforeImmersive() {
+        val taskId = 1
+        repo.addActiveTask(THIRD_DISPLAY, taskId)
+        repo.addOrMoveFreeformTaskToTop(THIRD_DISPLAY, taskId)
+        repo.saveBoundsBeforeFullImmersive(taskId, Rect(0, 0, 200, 200))
+
+        repo.removeFreeformTask(THIRD_DISPLAY, taskId)
+
+        assertThat(repo.removeBoundsBeforeFullImmersive(taskId)).isNull()
+    }
+
+    @Test
     fun removeFreeformTask_removesActiveTask() {
         val taskId = 1
         val listener = TestListener()
@@ -805,6 +817,28 @@
     }
 
     @Test
+    fun saveBoundsBeforeImmersive_boundsSavedByTaskId() {
+        val taskId = 1
+        val bounds = Rect(0, 0, 200, 200)
+
+        repo.saveBoundsBeforeFullImmersive(taskId, bounds)
+
+        assertThat(repo.removeBoundsBeforeFullImmersive(taskId)).isEqualTo(bounds)
+    }
+
+    @Test
+    fun removeBoundsBeforeImmersive_returnsNullAfterBoundsRemoved() {
+        val taskId = 1
+        val bounds = Rect(0, 0, 200, 200)
+        repo.saveBoundsBeforeFullImmersive(taskId, bounds)
+        repo.removeBoundsBeforeFullImmersive(taskId)
+
+        val boundsBeforeImmersive = repo.removeBoundsBeforeFullImmersive(taskId)
+
+        assertThat(boundsBeforeImmersive).isNull()
+    }
+
+    @Test
     fun isMinimizedTask_minimizeTaskNotCalled_noTasksMinimized() {
         assertThat(repo.isMinimizedTask(taskId = 0)).isFalse()
         assertThat(repo.isMinimizedTask(taskId = 1)).isFalse()
@@ -957,6 +991,15 @@
         assertThat(repo.getActiveTasks(displayId = DEFAULT_DISPLAY)).isEmpty()
     }
 
+    @Test
+    fun getTaskInFullImmersiveState_byDisplay() {
+        repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID, taskId = 1, immersive = true)
+        repo.setTaskInFullImmersiveState(DEFAULT_DESKTOP_ID + 1, taskId = 2, immersive = true)
+
+        assertThat(repo.getTaskInFullImmersiveState(DEFAULT_DESKTOP_ID)).isEqualTo(1)
+        assertThat(repo.getTaskInFullImmersiveState(DEFAULT_DESKTOP_ID + 1)).isEqualTo(2)
+    }
+
     class TestListener : DesktopRepository.ActiveTasksListener {
         var activeChangesOnDefaultDisplay = 0
         var activeChangesOnSecondaryDisplay = 0
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 27deb0b..113990e 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
@@ -42,6 +42,7 @@
 import android.os.Binder
 import android.os.Bundle
 import android.os.Handler
+import android.os.IBinder
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
@@ -96,9 +97,11 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
 import com.android.wm.shell.desktopmode.persistence.Desktop
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.draganddrop.DragAndDropController
+import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.recents.RecentTasksController
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
@@ -120,6 +123,7 @@
 import java.util.Optional
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
+import kotlin.test.assertIs
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlinx.coroutines.CoroutineScope
@@ -144,13 +148,11 @@
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.times
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
-import org.mockito.kotlin.argThat
 import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.capture
 import org.mockito.kotlin.eq
@@ -201,6 +203,7 @@
   private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
   @Mock private lateinit var mockSurface: SurfaceControl
   @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
+  @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter
   @Mock private lateinit var mockHandler: Handler
   @Mock lateinit var persistentRepository: DesktopPersistentRepository
 
@@ -266,6 +269,7 @@
 
     controller = createController()
     controller.setSplitScreenController(splitScreenController)
+    controller.freeformTaskTransitionStarter = freeformTaskTransitionStarter
 
     shellInit.init()
 
@@ -1336,7 +1340,7 @@
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
 
-    controller.moveTaskToFront(task1)
+    controller.moveTaskToFront(task1, remoteTransition = null)
 
     val wct = getLatestWct(type = TRANSIT_TO_FRONT)
     assertThat(wct.hierarchyOps).hasSize(1)
@@ -1348,7 +1352,7 @@
     setUpHomeTask()
     val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
 
-    controller.moveTaskToFront(freeformTasks[0])
+    controller.moveTaskToFront(freeformTasks[0], remoteTransition = null)
 
     val wct = getLatestWct(type = TRANSIT_TO_FRONT)
     assertThat(wct.hierarchyOps.size).isEqualTo(2) // move-to-front + minimize
@@ -1357,11 +1361,40 @@
   }
 
   @Test
+  fun moveTaskToFront_remoteTransition_usesOneshotHandler() {
+    setUpHomeTask()
+    val freeformTasks = List(MAX_TASK_LIMIT) { setUpFreeformTask() }
+    val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+    whenever(
+      transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
+    ).thenReturn(Binder())
+
+    controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
+
+    assertIs<OneShotRemoteHandler>(transitionHandlerArgCaptor.value)
+  }
+
+  @Test
+  fun moveTaskToFront_bringsTasksOverLimit_remoteTransition_usesWindowLimitHandler() {
+    setUpHomeTask()
+    val freeformTasks = List(MAX_TASK_LIMIT + 1) { setUpFreeformTask() }
+    val transitionHandlerArgCaptor = ArgumentCaptor.forClass(TransitionHandler::class.java)
+    whenever(
+      transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture())
+    ).thenReturn(Binder())
+
+    controller.moveTaskToFront(freeformTasks[0], RemoteTransition(TestRemoteTransition()))
+
+    assertThat(transitionHandlerArgCaptor.value)
+      .isInstanceOf(DesktopWindowLimitRemoteHandler::class.java)
+  }
+
+  @Test
   fun moveTaskToFront_backgroundTask_launchesTask() {
     val task = createTaskInfo(1)
     whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
 
-    controller.moveTaskToFront(task.taskId)
+    controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
     val wct = getLatestWct(type = TRANSIT_OPEN)
     assertThat(wct.hierarchyOps).hasSize(1)
@@ -1374,12 +1407,12 @@
     val task = createTaskInfo(1001)
     whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
 
-    controller.moveTaskToFront(task.taskId)
+    controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
     val wct = getLatestWct(type = TRANSIT_OPEN)
     assertThat(wct.hierarchyOps.size).isEqualTo(2) // launch + minimize
-    wct.assertReorderAt(0, freeformTasks[0], toTop = false)
-    wct.assertLaunchTaskAt(1, task.taskId, WINDOWING_MODE_FREEFORM)
+    wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+    wct.assertReorderAt(1, freeformTasks[0], toTop = false)
   }
 
   @Test
@@ -1542,75 +1575,142 @@
   }
 
   @Test
-  fun onDesktopWindowMinimize_noActiveTask_doesntUpdateTransaction() {
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowMinimize(wct, taskId = 1)
-    // Nothing happens.
-    assertThat(wct.hierarchyOps).isEmpty()
+  fun onDesktopWindowMinimize_noActiveTask_doesntRemoveWallpaper() {
+    val task = setUpFreeformTask(active = false)
+    val transition = Binder()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
+    val wallpaperToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = wallpaperToken
+
+    controller.minimizeTask(task)
+
+    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+    captor.value.hierarchyOps.none { hop ->
+      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+    }
   }
 
   @Test
-  fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntUpdateTransaction() {
-    val task = setUpFreeformTask()
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowMinimize(wct, taskId = task.taskId)
-    // Nothing happens.
-    assertThat(wct.hierarchyOps).isEmpty()
+  fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
+    val task = setUpFreeformTask(active = true)
+    val transition = Binder()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
+
+    controller.minimizeTask(task)
+
+    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+    captor.value.hierarchyOps.none { hop ->
+      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK
+    }
   }
 
   @Test
   fun onDesktopWindowMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
     val task = setUpFreeformTask()
+    val transition = Binder()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
     val wallpaperToken = MockToken().token()
     taskRepository.wallpaperActivityToken = wallpaperToken
 
-    val wct = WindowContainerTransaction()
     // The only active task is being minimized.
-    controller.onDesktopWindowMinimize(wct, taskId = task.taskId)
+    controller.minimizeTask(task)
+
+    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
     // Adds remove wallpaper operation
-    wct.assertRemoveAt(index = 0, wallpaperToken)
+    captor.value.assertRemoveAt(index = 0, wallpaperToken)
   }
 
   @Test
-  fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntUpdateTransaction() {
+  fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntRemoveWallpaper() {
     val task = setUpFreeformTask()
+    val transition = Binder()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
     val wallpaperToken = MockToken().token()
     taskRepository.wallpaperActivityToken = wallpaperToken
     taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
 
-    val wct = WindowContainerTransaction()
     // The only active task is already minimized.
-    controller.onDesktopWindowMinimize(wct, taskId = task.taskId)
-    // Doesn't modify transaction
-    assertThat(wct.hierarchyOps).isEmpty()
+    controller.minimizeTask(task)
+
+    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+    captor.value.hierarchyOps.none { hop ->
+      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+    }
   }
 
   @Test
-  fun onDesktopWindowMinimize_multipleActiveTasks_doesntUpdateTransaction() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
+  fun onDesktopWindowMinimize_multipleActiveTasks_doesntRemoveWallpaper() {
+    val task1 = setUpFreeformTask(active = true)
+    setUpFreeformTask(active = true)
+    val transition = Binder()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
     val wallpaperToken = MockToken().token()
     taskRepository.wallpaperActivityToken = wallpaperToken
 
-    val wct = WindowContainerTransaction()
-    controller.onDesktopWindowMinimize(wct, taskId = task1.taskId)
-    // Doesn't modify transaction
-    assertThat(wct.hierarchyOps).isEmpty()
+    controller.minimizeTask(task1)
+
+    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+    captor.value.hierarchyOps.none { hop ->
+      hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
+    }
   }
 
   @Test
   fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() {
-    val task1 = setUpFreeformTask()
-    val task2 = setUpFreeformTask()
+    val task1 = setUpFreeformTask(active = true)
+    val task2 = setUpFreeformTask(active = true)
+    val transition = Binder()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
     val wallpaperToken = MockToken().token()
     taskRepository.wallpaperActivityToken = wallpaperToken
     taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
 
-    val wct = WindowContainerTransaction()
     // task1 is the only visible task as task2 is minimized.
-    controller.onDesktopWindowMinimize(wct, taskId = task1.taskId)
+    controller.minimizeTask(task1)
     // Adds remove wallpaper operation
-    wct.assertRemoveAt(index = 0, wallpaperToken)
+    val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+    verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
+    // Adds remove wallpaper operation
+    captor.value.assertRemoveAt(index = 0, wallpaperToken)
+  }
+
+  @Test
+  fun onDesktopWindowMinimize_triesToExitImmersive() {
+    val task = setUpFreeformTask()
+    val transition = Binder()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
+
+    controller.minimizeTask(task)
+
+    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(any(), eq(task))
+  }
+
+  @Test
+  fun onDesktopWindowMinimize_invokesImmersiveTransitionStartCallback() {
+    val task = setUpFreeformTask()
+    val transition = Binder()
+    val runOnTransit = RunOnStartTransitionCallback()
+    whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
+      .thenReturn(transition)
+    whenever(mockDesktopFullImmersiveTransitionHandler.exitImmersiveIfApplicable(any(), eq(task)))
+      .thenReturn(runOnTransit)
+
+    controller.minimizeTask(task)
+
+    assertThat(runOnTransit.invocations).isEqualTo(1)
+    assertThat(runOnTransit.lastInvoked).isEqualTo(transition)
   }
 
   @Test
@@ -2833,7 +2933,8 @@
     runOpenNewWindow(task)
     verify(splitScreenController)
       .startIntent(any(), anyInt(), any(), any(),
-        optionsCaptor.capture(), anyOrNull())
+        optionsCaptor.capture(), anyOrNull(), eq(true)
+      )
     assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
       .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
   }
@@ -2848,7 +2949,7 @@
     verify(splitScreenController)
       .startIntent(
         any(), anyInt(), any(), any(),
-        optionsCaptor.capture(), anyOrNull()
+        optionsCaptor.capture(), anyOrNull(), eq(true)
       )
     assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
       .isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
@@ -2915,6 +3016,58 @@
       .launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
   }
 
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+  fun openInstance_fromFreeform_minimizesIfNeeded() {
+    setUpLandscapeDisplay()
+    val homeTask = setUpHomeTask()
+    val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+    val oldestTask = freeformTasks.first()
+    val newestTask = freeformTasks.last()
+
+    runOpenInstance(newestTask, freeformTasks[1].taskId)
+
+    val wct = getLatestWct(type = TRANSIT_OPEN)
+    // Home is moved to front of everything.
+    assertThat(
+      wct.hierarchyOps.any { hop ->
+        hop.container == homeTask.token.asBinder() && hop.toTop
+      }
+    ).isTrue()
+    // And the oldest task isn't moved in front of home, effectively minimizing it.
+    assertThat(
+      wct.hierarchyOps.none { hop ->
+        hop.container == oldestTask.token.asBinder() && hop.toTop
+      }
+    ).isTrue()
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+  fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
+    setUpLandscapeDisplay()
+    val homeTask = setUpHomeTask()
+    val freeformTask = setUpFreeformTask()
+    val immersiveTask = setUpFreeformTask()
+    taskRepository.setTaskInFullImmersiveState(
+      displayId = immersiveTask.displayId,
+      taskId = immersiveTask.taskId,
+      immersive = true
+    )
+    val runOnStartTransit = RunOnStartTransitionCallback()
+    val transition = Binder()
+    whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull()))
+      .thenReturn(transition)
+    whenever(mockDesktopFullImmersiveTransitionHandler
+      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))).thenReturn(runOnStartTransit)
+
+    runOpenInstance(immersiveTask, freeformTask.taskId)
+
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))
+    runOnStartTransit.assertOnlyInvocation(transition)
+  }
+
   private fun runOpenInstance(
     callingTask: RunningTaskInfo,
     requestedTaskId: Int
@@ -3166,27 +3319,23 @@
   }
 
   @Test
-  fun toggleImmersive_enter_resizesToDisplayBounds() {
+  fun toggleImmersive_enter_movesToImmersive() {
     val task = setUpFreeformTask(DEFAULT_DISPLAY)
     taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, false /* immersive */)
 
     controller.toggleDesktopTaskFullImmersiveState(task)
 
-    verify(mockDesktopFullImmersiveTransitionHandler).enterImmersive(eq(task), argThat { wct ->
-      wct.hasBoundsChange(task.token, Rect())
-    })
+    verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToImmersive(task)
   }
 
   @Test
-  fun toggleImmersive_exit_resizesToStableBounds() {
+  fun toggleImmersive_exit_movesToNonImmersive() {
     val task = setUpFreeformTask(DEFAULT_DISPLAY)
     taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, true /* immersive */)
 
     controller.toggleDesktopTaskFullImmersiveState(task)
 
-    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersive(eq(task), argThat { wct ->
-      wct.hasBoundsChange(task.token, STABLE_BOUNDS)
-    })
+    verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToNonImmersive(task)
   }
 
   @Test
@@ -3198,7 +3347,7 @@
     task.requestedVisibleTypes = WindowInsets.Type.statusBars()
     controller.onTaskInfoChanged(task)
 
-    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersive(eq(task), any())
+    verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToNonImmersive(task)
   }
 
   @Test
@@ -3210,7 +3359,113 @@
     task.requestedVisibleTypes = WindowInsets.Type.statusBars()
     controller.onTaskInfoChanged(task)
 
-    verify(mockDesktopFullImmersiveTransitionHandler, never()).exitImmersive(eq(task), any())
+    verify(mockDesktopFullImmersiveTransitionHandler, never()).moveTaskToNonImmersive(task)
+  }
+
+  @Test
+  fun moveTaskToDesktop_background_attemptsImmersiveExit() {
+    val task = setUpFreeformTask(background = true)
+    val wct = WindowContainerTransaction()
+    val runOnStartTransit = RunOnStartTransitionCallback()
+    val transition = Binder()
+    whenever(mockDesktopFullImmersiveTransitionHandler
+      .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit)
+    whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+
+    controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
+
+    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId)
+    runOnStartTransit.assertOnlyInvocation(transition)
+  }
+
+  @Test
+  fun moveTaskToDesktop_foreground_attemptsImmersiveExit() {
+    val task = setUpFreeformTask(background = false)
+    val wct = WindowContainerTransaction()
+    val runOnStartTransit = RunOnStartTransitionCallback()
+    val transition = Binder()
+    whenever(mockDesktopFullImmersiveTransitionHandler
+      .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit)
+    whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
+
+    controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
+
+    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId)
+    runOnStartTransit.assertOnlyInvocation(transition)
+  }
+
+  @Test
+  fun moveTaskToFront_background_attemptsImmersiveExit() {
+    val task = setUpFreeformTask(background = true)
+    val runOnStartTransit = RunOnStartTransitionCallback()
+    val transition = Binder()
+    whenever(mockDesktopFullImmersiveTransitionHandler
+      .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit)
+    whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+
+    controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(any(), eq(task.displayId))
+    runOnStartTransit.assertOnlyInvocation(transition)
+  }
+
+  @Test
+  fun moveTaskToFront_foreground_attemptsImmersiveExit() {
+    val task = setUpFreeformTask(background = false)
+    val runOnStartTransit = RunOnStartTransitionCallback()
+    val transition = Binder()
+    whenever(mockDesktopFullImmersiveTransitionHandler
+      .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit)
+    whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+
+    controller.moveTaskToFront(task.taskId, remoteTransition = null)
+
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(any(), eq(task.displayId))
+    runOnStartTransit.assertOnlyInvocation(transition)
+  }
+
+  @Test
+  fun handleRequest_freeformLaunchToDesktop_attemptsImmersiveExit() {
+    markTaskVisible(setUpFreeformTask())
+    val task = setUpFreeformTask()
+    markTaskVisible(task)
+    val binder = Binder()
+
+    controller.handleRequest(binder, createTransition(task))
+
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId))
+  }
+
+  @Test
+  fun handleRequest_fullscreenLaunchToDesktop_attemptsImmersiveExit() {
+    setUpFreeformTask()
+    val task = setUpFullscreenTask()
+    val binder = Binder()
+
+    controller.handleRequest(binder, createTransition(task))
+
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId))
+  }
+
+  private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
+    var invocations = 0
+      private set
+    var lastInvoked: IBinder? = null
+      private set
+
+    override fun invoke(transition: IBinder) {
+      invocations++
+      lastInvoked = transition
+    }
+  }
+
+  private fun RunOnStartTransitionCallback.assertOnlyInvocation(transition: IBinder) {
+    assertThat(invocations).isEqualTo(1)
+    assertThat(lastInvoked).isEqualTo(transition)
   }
 
   /**
@@ -3291,18 +3546,27 @@
   private fun setUpFreeformTask(
       displayId: Int = DEFAULT_DISPLAY,
       bounds: Rect? = null,
-      active: Boolean = true
+      active: Boolean = true,
+      background: Boolean = false,
   ): RunningTaskInfo {
     val task = createFreeformTask(displayId, bounds)
     val activityInfo = ActivityInfo()
     task.topActivityInfo = activityInfo
-    whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+    if (background) {
+      whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
+      whenever(recentTasksController.findTaskInBackground(task.taskId))
+        .thenReturn(createTaskInfo(task.taskId))
+    } else {
+      whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+    }
     if (active) {
       taskRepository.addActiveTask(displayId, task.taskId)
       taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
     }
     taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
-    runningTasks.add(task)
+    if (!background) {
+      runningTasks.add(task)
+    }
     return task
   }
 
@@ -3556,6 +3820,21 @@
   assertThat(op.container).isEqualTo(token.asBinder())
 }
 
+private fun WindowContainerTransaction.assertNoRemoveAt(index: Int, token: WindowContainerToken) {
+  assertIndexInBounds(index)
+  val op = hierarchyOps[index]
+  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+  assertThat(op.container).isEqualTo(token.asBinder())
+}
+
+private fun WindowContainerTransaction.hasRemoveAt(index: Int, token: WindowContainerToken) {
+
+  assertIndexInBounds(index)
+  val op = hierarchyOps[index]
+  assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+  assertThat(op.container).isEqualTo(token.asBinder())
+}
+
 private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) {
   assertIndexInBounds(index)
   val op = hierarchyOps[index]
@@ -3578,13 +3857,6 @@
       .isEqualTo(windowingMode)
 }
 
-private fun WindowContainerTransaction.hasBoundsChange(
-  token: WindowContainerToken,
-  bounds: Rect
-): Boolean = this.changes.any { change ->
-  change.key == token.asBinder() && change.value.configuration.windowConfiguration.bounds == bounds
-}
-
 private fun WindowContainerTransaction?.anyDensityConfigChange(
     token: WindowContainerToken
 ): Boolean {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index 598df34..737439c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -22,26 +22,39 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.os.IBinder
 import android.platform.test.annotations.EnableFlags
 import android.view.Display.DEFAULT_DISPLAY
+import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CLOSE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.IWindowContainerToken
 import android.window.TransitionInfo
 import android.window.TransitionInfo.Change
 import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import com.android.window.flags.Flags
+import com.android.wm.shell.MockToken
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isA
 import org.mockito.Mockito
 import org.mockito.kotlin.any
+import org.mockito.kotlin.isNull
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.spy
@@ -114,14 +127,14 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-    fun removeTasks_onTaskFullscreenLaunch_taskRemovedFromRepo() {
+    fun removeTasks_onTaskFullscreenLaunchWithOpenTransition_taskRemovedFromRepo() {
         val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)
         whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
         whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true)
 
         transitionObserver.onTransitionReady(
             transition = mock(),
-            info = createOpenTransition(task),
+            info = createOpenChangeTransition(task),
             startTransaction = mock(),
             finishTransaction = mock(),
         )
@@ -130,6 +143,45 @@
         verify(taskRepository).removeFreeformTask(task.displayId, task.taskId)
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun removeTasks_onTaskFullscreenLaunchExitDesktopTransition_taskRemovedFromRepo() {
+        val task = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)
+        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
+        whenever(taskRepository.isActiveTask(task.taskId)).thenReturn(true)
+
+        transitionObserver.onTransitionReady(
+            transition = mock(),
+            info = createOpenChangeTransition(task, TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId)
+        verify(taskRepository).removeFreeformTask(task.displayId, task.taskId)
+    }
+
+    @Test
+    fun closeLastTask_wallpaperTokenExists_wallpaperIsRemoved() {
+        val mockTransition = Mockito.mock(IBinder::class.java)
+        val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
+        val wallpaperToken = MockToken().token()
+        whenever(taskRepository.getVisibleTaskCount(task.displayId)).thenReturn(1)
+        whenever(taskRepository.wallpaperActivityToken).thenReturn(wallpaperToken)
+
+        transitionObserver.onTransitionReady(
+            transition = mockTransition,
+            info = createCloseTransition(task),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+        transitionObserver.onTransitionFinished(mockTransition, false)
+
+        val wct = getLatestWct(type = TRANSIT_CLOSE)
+        assertThat(wct.hierarchyOps).hasSize(1)
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
     private fun createBackNavigationTransition(
         task: RunningTaskInfo?
     ): TransitionInfo {
@@ -145,8 +197,9 @@
         }
     }
 
-    private fun createOpenTransition(
-        task: RunningTaskInfo?
+    private fun createOpenChangeTransition(
+        task: RunningTaskInfo?,
+        type: Int = TRANSIT_OPEN
     ): TransitionInfo {
         return TransitionInfo(TRANSIT_OPEN, 0 /* flags */).apply {
             addChange(
@@ -160,6 +213,48 @@
         }
     }
 
+    private fun createCloseTransition(
+        task: RunningTaskInfo?
+    ): TransitionInfo {
+        return TransitionInfo(TRANSIT_CLOSE, 0 /* flags */).apply {
+            addChange(
+                Change(mock(), mock()).apply {
+                    mode = TRANSIT_CLOSE
+                    parent = null
+                    taskInfo = task
+                    flags = flags
+                }
+            )
+        }
+    }
+
+    private fun getLatestWct(
+        @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+        handlerClass: Class<out Transitions.TransitionHandler>? = null
+    ): WindowContainerTransaction {
+        val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        if (handlerClass == null) {
+            Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isNull())
+        } else {
+            Mockito.verify(transitions)
+                .startTransition(eq(type), arg.capture(), isA(handlerClass))
+        }
+        return arg.value
+    }
+
+    private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
+        assertIndexInBounds(index)
+        val op = hierarchyOps[index]
+        assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+        assertThat(op.container).isEqualTo(token.asBinder())
+    }
+
+    private fun WindowContainerTransaction.assertIndexInBounds(index: Int) {
+        assertWithMessage("WCT does not have a hierarchy operation at index $index")
+            .that(hierarchyOps.size)
+            .isGreaterThan(index)
+    }
+
     private fun createTaskInfo(id: Int, windowingMode: Int = WINDOWING_MODE_FREEFORM) =
         RunningTaskInfo().apply {
             taskId = id
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 230f7e6..0bd3e08 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -1,5 +1,6 @@
 package com.android.wm.shell.desktopmode
 
+import android.animation.AnimatorTestRule
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
@@ -24,6 +25,7 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
 import com.android.wm.shell.splitscreen.SplitScreenController
@@ -38,6 +40,7 @@
 import junit.framework.Assert.assertTrue
 import org.junit.After
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
@@ -58,6 +61,9 @@
 @RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 class DragToDesktopTransitionHandlerTest : ShellTestCase() {
+    @JvmField
+    @Rule
+    val mAnimatorTestRule = AnimatorTestRule(this)
 
     @Mock private lateinit var transitions: Transitions
     @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@@ -267,16 +273,36 @@
     }
 
     @Test
-    fun cancelDragToDesktop_startWasReady_cancel() {
-        startDrag(defaultHandler)
+    fun cancelDragToDesktop_startWasReady_cancel_merged() {
+        val startToken = startDrag(defaultHandler)
 
         // Then user cancelled after it had already started.
-        defaultHandler.cancelDragToDesktopTransition(
-            DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL
-        )
+        val cancelToken = cancelDragToDesktopTransition(
+            defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+        defaultHandler.mergeAnimation(
+            cancelToken,
+            TransitionInfo(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, 0),
+            mock<SurfaceControl.Transaction>(),
+            startToken,
+            mock<Transitions.TransitionFinishCallback>())
 
         // Cancel animation should run since it had already started.
         verify(dragAnimator).cancelAnimator()
+        assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress)
+    }
+
+    @Test
+    fun cancelDragToDesktop_startWasReady_cancel_aborted() {
+        val startToken = startDrag(defaultHandler)
+
+        // Then user cancelled after it had already started.
+        val cancelToken = cancelDragToDesktopTransition(
+            defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
+        defaultHandler.onTransitionConsumed(cancelToken, aborted = true, null)
+
+        // Cancel animation should run since it had already started.
+        verify(dragAnimator).cancelAnimator()
+        assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress)
     }
 
     @Test
@@ -585,6 +611,23 @@
         return token
     }
 
+    private fun cancelDragToDesktopTransition(
+        handler: DragToDesktopTransitionHandler,
+        cancelState: DragToDesktopTransitionHandler.CancelState): IBinder {
+        val token = mock<IBinder>()
+        whenever(
+                transitions.startTransition(
+                    eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
+                    any(),
+                    eq(handler)
+                )
+            )
+            .thenReturn(token)
+        handler.cancelDragToDesktopTransition(cancelState)
+        mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+        return token
+    }
+
     private fun performEarlyCancel(
         handler: DragToDesktopTransitionHandler,
         cancelState: DragToDesktopTransitionHandler.CancelState
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
new file mode 100644
index 0000000..6a5d9f6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandlerTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.minimize
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.Binder
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl.Transaction
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.IRemoteTransition
+import android.window.RemoteTransition
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.transition.TransitionInfoBuilder
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopWindowLimitRemoteHandlerTest {
+
+    private val shellExecutor = TestShellExecutor()
+    private val transition: IBinder = Binder()
+
+    private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+    private val remoteTransition = mock<RemoteTransition>()
+    private val iRemoteTransition = mock<IRemoteTransition>()
+    private val startT = mock<Transaction>()
+    private val finishT = mock<Transaction>()
+    private val finishCallback = mock<Transitions.TransitionFinishCallback>()
+
+    @Before
+    fun setUp() {
+        whenever(remoteTransition.remoteTransition).thenReturn(iRemoteTransition)
+        whenever(iRemoteTransition.asBinder()).thenReturn(mock(IBinder::class.java))
+    }
+
+    private fun createRemoteHandler(taskIdToMinimize: Int) =
+        DesktopWindowLimitRemoteHandler(
+            shellExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
+
+    @Test
+    fun startAnimation_dontSetTransition_returnsFalse() {
+        val minimizeTask = createDesktopTask()
+        val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId)
+
+        assertThat(remoteHandler.startAnimation(transition,
+            createMinimizeTransitionInfo(minimizeTask), startT, finishT, finishCallback)
+        ).isFalse()
+    }
+
+    @Test
+    fun startAnimation_noMinimizeChange_returnsFalse() {
+        val remoteHandler = createRemoteHandler(taskIdToMinimize = 1)
+        remoteHandler.setTransition(transition)
+        val info = createToFrontTransitionInfo()
+
+        assertThat(
+            remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
+        ).isFalse()
+    }
+
+    @Test
+    fun startAnimation_correctTransition_returnsTrue() {
+        val minimizeTask = createDesktopTask()
+        val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId)
+        remoteHandler.setTransition(transition)
+        val info = createMinimizeTransitionInfo(minimizeTask)
+
+        assertThat(
+            remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
+        ).isTrue()
+    }
+
+    @Test
+    fun startAnimation_noMinimizeChange_doesNotReparentMinimizeChange() {
+        val remoteHandler = createRemoteHandler(taskIdToMinimize = 1)
+        remoteHandler.setTransition(transition)
+        val info = createToFrontTransitionInfo()
+
+        remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
+
+        verify(rootTaskDisplayAreaOrganizer, times(0))
+            .reparentToDisplayArea(anyInt(), any(), any())
+    }
+
+    @Test
+    fun startAnimation_hasMinimizeChange_reparentsMinimizeChange() {
+        val minimizeTask = createDesktopTask()
+        val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId)
+        remoteHandler.setTransition(transition)
+        val info = createMinimizeTransitionInfo(minimizeTask)
+
+        remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
+
+        verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(anyInt(), any(), any())
+    }
+
+    @Test
+    fun startAnimation_noMinimizeChange_doesNotStartRemoteAnimation() {
+        val minimizeTask = createDesktopTask()
+        val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId)
+        remoteHandler.setTransition(transition)
+        val info = createToFrontTransitionInfo()
+
+        remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
+
+        verify(iRemoteTransition, times(0)).startAnimation(any(), any(), any(), any())
+    }
+
+    @Test
+    fun startAnimation_hasMinimizeChange_startsRemoteAnimation() {
+        val minimizeTask = createDesktopTask()
+        val remoteHandler = createRemoteHandler(taskIdToMinimize = minimizeTask.taskId)
+        remoteHandler.setTransition(transition)
+        val info = createMinimizeTransitionInfo(minimizeTask)
+
+        remoteHandler.startAnimation(transition, info, startT, finishT, finishCallback)
+
+        verify(iRemoteTransition).startAnimation(any(), any(), any(), any())
+    }
+
+    private fun createDesktopTask() =
+        TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
+
+    private fun createToFrontTransitionInfo() =
+        TransitionInfoBuilder(TRANSIT_TO_FRONT)
+            .addChange(TRANSIT_TO_FRONT,
+                TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build())
+            .build()
+
+    private fun createMinimizeTransitionInfo(minimizeTask: ActivityManager.RunningTaskInfo) =
+        TransitionInfoBuilder(TRANSIT_TO_FRONT)
+            .addChange(TRANSIT_TO_FRONT,
+                TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build())
+            .addChange(TRANSIT_TO_BACK, minimizeTask)
+            .build()
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
index 46b60499..eb74218 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
@@ -150,7 +150,8 @@
         mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
         mInsets = Insets.of(0, 0, 0, 0);
 
-        mPolicy = spy(new SplitDragPolicy(mContext, mSplitScreenStarter, mFullscreenStarter));
+        mPolicy = spy(new SplitDragPolicy(mContext, mSplitScreenStarter, mFullscreenStarter,
+                mock(DragZoneAnimator.class)));
         mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
         mLaunchableIntentPendingIntent = mock(PendingIntent.class);
         when(mLaunchableIntentPendingIntent.getCreatorUserHandle())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 36e0427..f95b0d1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -178,6 +178,7 @@
         mFreeformTaskListener.onTaskVanished(task);
 
         verify(mDesktopRepository, never()).minimizeTask(task.displayId, task.taskId);
+        verify(mDesktopRepository).removeClosingTask(task.taskId);
         verify(mDesktopRepository).removeFreeformTask(task.displayId, task.taskId);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index d4a319e..7ae0bcd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -22,7 +22,6 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.view.WindowManager.TRANSIT_CHANGE;
 
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -44,18 +43,14 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.window.flags.Flags;
-
-import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
 import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.TransitionInfoBuilder;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
-import java.util.Optional;
-
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -80,6 +75,9 @@
     private WindowDecorViewModel mWindowDecorViewModel;
     @Mock
     private TaskChangeListener mTaskChangeListener;
+    @Mock
+    private FocusTransitionObserver mFocusTransitionObserver;
+
     private FreeformTaskTransitionObserver mTransitionObserver;
 
     @Before
@@ -95,7 +93,7 @@
         mTransitionObserver = new FreeformTaskTransitionObserver(
                 context, mShellInit, mTransitions,
                 Optional.of(mDesktopFullImmersiveTransitionHandler),
-                mWindowDecorViewModel, Optional.of(mTaskChangeListener));
+                mWindowDecorViewModel, Optional.of(mTaskChangeListener), mFocusTransitionObserver);
 
         final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(
                 Runnable.class);
@@ -331,7 +329,7 @@
 
         mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
 
-        verify(mDesktopFullImmersiveTransitionHandler).onTransitionReady(transition);
+        verify(mDesktopFullImmersiveTransitionHandler).onTransitionReady(transition, info);
     }
 
     private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 9260a07..ef3af8e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -297,6 +297,28 @@
     }
 
     @Test
+    public void startIntent_forceLaunchNewTaskTrue_skipsBackgroundTasks() {
+        Intent startIntent = createStartIntent("startActivity");
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+        mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+                SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+                true /* forceLaunchNewTask */);
+        verify(mRecentTasks, never()).findTaskInBackground(any(), anyInt(), any());
+    }
+
+    @Test
+    public void startIntent_forceLaunchNewTaskFalse_checksBackgroundTasks() {
+        Intent startIntent = createStartIntent("startActivity");
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+        mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+                SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+                false /* forceLaunchNewTask */);
+        verify(mRecentTasks).findTaskInBackground(any(), anyInt(), any());
+    }
+
+    @Test
     public void testSwitchSplitPosition_checksIsSplitScreenVisible() {
         final String reason = "test";
         when(mSplitScreenController.isSplitScreenVisible()).thenReturn(true, false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 67eda8b..a6e33e5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -232,17 +232,6 @@
     }
 
     @Test
-    public void testRemoveFromSideStage() {
-        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
-
-        doReturn(false).when(mMainStage).isActive();
-        mStageCoordinator.removeFromSideStage(task.taskId);
-
-        verify(mSideStage).removeTask(
-                eq(task.taskId), any(), any(WindowContainerTransaction.class));
-    }
-
-    @Test
     public void testResolveStartStage_beforeSplitActivated_setsStagePosition() {
         mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index b7b7d0d..189684d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -23,10 +23,9 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
@@ -64,8 +63,6 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public final class StageTaskListenerTests extends ShellTestCase {
-    private static final boolean ENABLE_SHELL_TRANSITIONS =
-            SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
 
     @Mock
     private ShellTaskOrganizer mTaskOrganizer;
@@ -117,20 +114,20 @@
     public void testRootTaskAppeared() {
         assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId);
         verify(mCallbacks).onRootTaskAppeared();
-        verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(false));
+        verify(mCallbacks, never()).onStageHasChildrenChanged(mStageTaskListener);
+        verify(mCallbacks, never()).onStageVisibilityChanged(mStageTaskListener);
     }
 
     @Test
-    public void testChildTaskAppeared() {
-        // With shell transitions, the transition manages status changes, so skip this test.
-        assumeFalse(ENABLE_SHELL_TRANSITIONS);
-        final ActivityManager.RunningTaskInfo childTask =
-                new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+    public void testRootTaskVisible() {
+        mStageTaskListener.onTaskVanished(mRootTask);
+        mRootTask = new TestRunningTaskInfoBuilder().setVisible(true).build();
+        mRootTask.parentTaskId = INVALID_TASK_ID;
+        mSurfaceControl = new SurfaceControl.Builder().setName("test").build();
+        mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl);
 
-        mStageTaskListener.onTaskAppeared(childTask, mSurfaceControl);
+        verify(mCallbacks).onStageVisibilityChanged(mStageTaskListener);
 
-        assertThat(mStageTaskListener.mChildrenTaskInfo.contains(childTask.taskId)).isTrue();
-        verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -140,29 +137,13 @@
     }
 
     @Test
-    public void testTaskVanished() {
-        // With shell transitions, the transition manages status changes, so skip this test.
-        assumeFalse(ENABLE_SHELL_TRANSITIONS);
-        final ActivityManager.RunningTaskInfo childTask =
-                new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
-        mStageTaskListener.mRootTaskInfo = mRootTask;
-        mStageTaskListener.mChildrenTaskInfo.put(childTask.taskId, childTask);
-
-        mStageTaskListener.onTaskVanished(childTask);
-        verify(mCallbacks, times(2)).onStatusChanged(eq(mRootTask.isVisible), eq(false));
-
-        mStageTaskListener.onTaskVanished(mRootTask);
-        verify(mCallbacks).onRootTaskVanished();
-    }
-
-    @Test
     public void testTaskInfoChanged_notSupportsMultiWindow() {
         final ActivityManager.RunningTaskInfo childTask =
                 new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
         childTask.supportsMultiWindow = false;
 
         mStageTaskListener.onTaskInfoChanged(childTask);
-        verify(mCallbacks).onNoLongerSupportMultiWindow(childTask);
+        verify(mCallbacks).onNoLongerSupportMultiWindow(mStageTaskListener, childTask);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
index d63158c..015ea20 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
@@ -23,6 +23,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -30,9 +31,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -43,17 +41,11 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.window.flags.Flags;
-import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.shared.IFocusTransitionListener;
-import com.android.wm.shell.shared.TransactionPool;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.shared.FocusTransitionListener;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -75,57 +67,64 @@
 
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-    private IFocusTransitionListener mListener;
-    private Transitions mTransition;
+    private FocusTransitionListener mListener;
+    private final TestShellExecutor mShellExecutor = new TestShellExecutor();
     private FocusTransitionObserver mFocusTransitionObserver;
 
     @Before
     public void setUp() {
-        mListener = mock(IFocusTransitionListener.class);
-        when(mListener.asBinder()).thenReturn(mock(IBinder.class));
-
+        mListener = mock(FocusTransitionListener.class);
         mFocusTransitionObserver = new FocusTransitionObserver();
-        mTransition =
-                new Transitions(InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                        mock(ShellInit.class), mock(ShellController.class),
-                        mock(ShellTaskOrganizer.class), mock(TransactionPool.class),
-                        mock(DisplayController.class), new TestShellExecutor(),
-                        new Handler(Looper.getMainLooper()), new TestShellExecutor(),
-                        mock(HomeTransitionObserver.class),
-                mFocusTransitionObserver);
-        mFocusTransitionObserver.setRemoteFocusTransitionListener(mTransition, mListener);
+        mFocusTransitionObserver.setLocalFocusTransitionListener(mListener, mShellExecutor);
+        mShellExecutor.flushAll();
+        clearInvocations(mListener);
     }
 
     @Test
-    public void testOnlyDisplayChangeAffectsDisplayFocus() throws RemoteException {
-        final IBinder binder = mock(IBinder.class);
+    public void testBasicTaskAndDisplayFocusSwitch() throws RemoteException {
         final SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
 
-        // Open a task on the secondary display, but it doesn't change display focus because it only
-        // has a task change.
+        // First, open a task on the default display.
         TransitionInfo info = mock(TransitionInfo.class);
         final List<TransitionInfo.Change> changes = new ArrayList<>();
-        setupTaskChange(changes, 123 /* taskId */, TRANSIT_OPEN, SECONDARY_DISPLAY_ID,
-                true /* focused */);
+        setupTaskChange(changes, 1 /* taskId */, TRANSIT_OPEN,
+                DEFAULT_DISPLAY, true /* focused */);
         when(info.getChanges()).thenReturn(changes);
-        mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx);
-        verify(mListener, never()).onFocusedDisplayChanged(SECONDARY_DISPLAY_ID);
+        mFocusTransitionObserver.updateFocusState(info);
+        mShellExecutor.flushAll();
+        verify(mListener, never()).onFocusedDisplayChanged(anyInt());
+        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
+                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
         clearInvocations(mListener);
 
-        // Moving the secondary display to front must change display focus to it.
-        changes.clear();
+        // Open a task on the secondary display.
+        setupTaskChange(changes, 2 /* taskId */, TRANSIT_OPEN,
+                SECONDARY_DISPLAY_ID, true /* focused */);
         setupDisplayToTopChange(changes, SECONDARY_DISPLAY_ID);
         when(info.getChanges()).thenReturn(changes);
-        mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx);
+        mFocusTransitionObserver.updateFocusState(info);
+        mShellExecutor.flushAll();
         verify(mListener, times(1))
                 .onFocusedDisplayChanged(SECONDARY_DISPLAY_ID);
+        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
+                true /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
+        verify(mListener, times(1)).onFocusedTaskChanged(2 /* taskId */,
+                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
+        clearInvocations(mListener);
 
-        // Moving the secondary display to front must change display focus back to it.
+        // Moving only the default display back to front, and verify that affected tasks are also
+        // notified.
         changes.clear();
         setupDisplayToTopChange(changes, DEFAULT_DISPLAY);
         when(info.getChanges()).thenReturn(changes);
-        mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx);
-        verify(mListener, times(1)).onFocusedDisplayChanged(DEFAULT_DISPLAY);
+        mFocusTransitionObserver.updateFocusState(info);
+        mShellExecutor.flushAll();
+        verify(mListener, times(1))
+                .onFocusedDisplayChanged(DEFAULT_DISPLAY);
+        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
+                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
+        verify(mListener, times(1)).onFocusedTaskChanged(2 /* taskId */,
+                true /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
     }
 
     private void setupTaskChange(List<TransitionInfo.Change> changes, int taskId,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index cf2de91..c36b88e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -18,13 +18,15 @@
 
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
 import static android.view.WindowManager.TRANSIT_NONE;
 
+import static com.android.wm.shell.unfold.UnfoldTransitionHandler.FINISH_ANIMATION_TIMEOUT_MILLIS;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -32,7 +34,9 @@
 import android.app.ActivityManager;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.test.TestLooper;
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -50,6 +54,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.InOrder;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -66,6 +71,8 @@
     private FullscreenUnfoldTaskAnimator mFullscreenUnfoldTaskAnimator;
     private SplitTaskUnfoldAnimator mSplitTaskUnfoldAnimator;
     private Transitions mTransitions;
+    private TestLooper mTestLooper;
+    private Handler mHandler;
 
     private final IBinder mTransition = new Binder();
 
@@ -74,6 +81,9 @@
         final ShellExecutor executor = new TestSyncExecutor();
         final ShellInit shellInit = new ShellInit(executor);
 
+        mTestLooper = new TestLooper();
+        mHandler = new Handler(mTestLooper.getLooper());
+
         mFullscreenUnfoldTaskAnimator = mock(FullscreenUnfoldTaskAnimator.class);
         mSplitTaskUnfoldAnimator = mock(SplitTaskUnfoldAnimator.class);
         mTransitions = mock(Transitions.class);
@@ -85,6 +95,7 @@
                 mSplitTaskUnfoldAnimator,
                 mTransactionPool,
                 executor,
+                mHandler,
                 mTransitions
         );
 
@@ -140,6 +151,33 @@
     }
 
     @Test
+    public void handleFoldMergeRequest_finishesTheTransition() {
+        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+        // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to
+        // notify about the end of the animation
+        mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                mock(TransitionInfo.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+
+        // Send fold transition request
+        TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class);
+        mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(),
+                mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback);
+        mTestLooper.dispatchAll();
+
+        // Verify that fold transition is merged into unfold and that unfold is finished
+        final InOrder inOrder = inOrder(mergeFinishCallback, finishCallback);
+        inOrder.verify(mergeFinishCallback).onTransitionFinished(any());
+        inOrder.verify(finishCallback).onTransitionFinished(any());
+    }
+
+    @Test
     public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() {
         TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
         mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
@@ -174,19 +212,22 @@
     }
 
     @Test
-    public void startAnimation_differentTransitionFromRequestWithUnfold_startsAnimation() {
-        mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
+    public void startAnimation_animationHasNotFinishedAfterTimeout_finishesTheTransition() {
+        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
         TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
-
-        boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
+        mUnfoldTransitionHandler.startAnimation(
                 mTransition,
-                createUnfoldTransitionInfo(),
+                mock(TransitionInfo.class),
                 mock(SurfaceControl.Transaction.class),
                 mock(SurfaceControl.Transaction.class),
                 finishCallback
         );
 
-        assertThat(animationStarted).isTrue();
+        mTestLooper.moveTimeForward(FINISH_ANIMATION_TIMEOUT_MILLIS + 1);
+        mTestLooper.dispatchAll();
+
+        verify(finishCallback).onTransitionFinished(any());
     }
 
     @Test
@@ -196,7 +237,7 @@
 
         boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
                 mTransition,
-                createDisplayResizeTransitionInfo(),
+                createUnfoldTransitionInfo(),
                 mock(SurfaceControl.Transaction.class),
                 mock(SurfaceControl.Transaction.class),
                 finishCallback
@@ -247,6 +288,7 @@
         TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
 
         mShellUnfoldProgressProvider.onStateChangeStarted();
+        mShellUnfoldProgressProvider.onStateChangeProgress(0.5f);
         mShellUnfoldProgressProvider.onStateChangeFinished();
         mUnfoldTransitionHandler.startAnimation(
                 mTransition,
@@ -279,6 +321,8 @@
         clearInvocations(finishCallback);
 
         // Fold
+        mShellUnfoldProgressProvider.onStateChangeProgress(/* progress= */ 0.0f);
+        mShellUnfoldProgressProvider.onStateChangeFinished();
         mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ true);
 
         // Second unfold
@@ -370,6 +414,19 @@
                 triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
     }
 
+    private TransitionInfo createFoldTransitionInfo() {
+        final TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
+
+        final TransitionInfo.Change change = new TransitionInfo.Change(/* container= */ null,
+                /* leash= */ null);
+        change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
+        change.setStartAbsBounds(new Rect(0, 0, 200, 200));
+        change.setEndAbsBounds(new Rect(0, 0, 100, 100));
+        transitionInfo.addChange(change);
+
+        return transitionInfo;
+    }
+
     private TransitionRequestInfo createNoneTransitionInfo() {
         return new TransitionRequestInfo(TRANSIT_NONE,
                 /* triggerTask= */ null, /* remoteTransition= */ null,
@@ -446,17 +503,6 @@
         change.setEndAbsBounds(new Rect(0, 0, 100, 100));
         change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
         transitionInfo.addChange(change);
-        transitionInfo.setFlags(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH);
-        return transitionInfo;
-    }
-
-    private TransitionInfo createDisplayResizeTransitionInfo() {
-        TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
-        TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class));
-        change.setStartAbsBounds(new Rect(0, 0, 10, 10));
-        change.setEndAbsBounds(new Rect(0, 0, 100, 100));
-        change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
-        transitionInfo.addChange(change);
         return transitionInfo;
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
index 0f16b9d..5ebf517 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt
@@ -49,7 +49,8 @@
             false,
             true /* isStatusBarVisible */,
             false /* isKeyguardVisibleAndOccluded */,
-            InsetsState()
+            InsetsState(),
+            true /* hasGlobalFocus */
         )
 
         Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isTrue()
@@ -70,7 +71,8 @@
             false,
             true /* isStatusBarVisible */,
             false /* isKeyguardVisibleAndOccluded */,
-            InsetsState()
+            InsetsState(),
+            true /* hasGlobalFocus */
         )
 
         Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isFalse()
@@ -87,7 +89,8 @@
             false,
             true /* isStatusBarVisible */,
             false /* isKeyguardVisibleAndOccluded */,
-            InsetsState()
+            InsetsState(),
+            true /* hasGlobalFocus */
         )
         Truth.assertThat(relayoutParams.mOccludingCaptionElements.size).isEqualTo(2)
         Truth.assertThat(relayoutParams.mOccludingCaptionElements[0].mAlignment).isEqualTo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 4aa7e18..175fbd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -100,6 +100,7 @@
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.FocusTransitionObserver
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
@@ -126,7 +127,6 @@
 import org.mockito.Mockito.times
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.any
-import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.argThat
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.doNothing
@@ -192,6 +192,7 @@
             DesktopModeWindowDecorViewModel.TaskPositionerFactory
     @Mock private lateinit var mockTaskPositioner: TaskPositioner
     @Mock private lateinit var mockAppHandleEducationController: AppHandleEducationController
+    @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver
     @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
     private lateinit var spyContext: TestableContext
 
@@ -254,7 +255,8 @@
                 mockAppHandleEducationController,
                 mockCaptionHandleRepository,
                 Optional.of(mockActivityOrientationChangeHandler),
-                mockTaskPositionerFactory
+                mockTaskPositionerFactory,
+                mockFocusTransitionObserver
         )
         desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
         whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -455,24 +457,13 @@
 
         onClickListenerCaptor.value.onClick(view)
 
-        val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
-        verify(mockFreeformTaskTransitionStarter)
-            .startMinimizedModeTransition(transactionCaptor.capture())
-        val wct = transactionCaptor.firstValue
-
-        verify(mockTasksLimiter).addPendingMinimizeChange(
-                anyOrNull(), eq(DEFAULT_DISPLAY), eq(decor.mTaskInfo.taskId))
-
-        assertEquals(1, wct.getHierarchyOps().size)
-        assertEquals(HierarchyOp.HIERARCHY_OP_TYPE_REORDER, wct.getHierarchyOps().get(0).getType())
-        assertFalse(wct.getHierarchyOps().get(0).getToTop())
-        assertEquals(decor.mTaskInfo.token.asBinder(), wct.getHierarchyOps().get(0).getContainer())
+        verify(mockDesktopTasksController).minimizeTask(decor.mTaskInfo)
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() {
-        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
             isTopActivityTransparent = true
             isTopActivityStyleFloating = true
             numActivities = 1
@@ -487,7 +478,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     fun testDecorationIsNotCreatedForTopTranslucentActivitiesWithoutStyleFloating() {
-        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN).apply {
             isTopActivityTransparent = true
             isTopActivityStyleFloating = false
             numActivities = 1
@@ -500,7 +491,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     fun testDecorationIsNotCreatedForSystemUIActivities() {
-        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN)
 
         // Set task as systemUI package
         val systemUIPackageName = context.resources.getString(
@@ -573,7 +564,7 @@
         // Simulate default enforce device restrictions system property
         whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
 
-        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN)
         // Simulate device that doesn't support desktop mode
         doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
@@ -589,7 +580,7 @@
         // Simulate device that doesn't support desktop mode
         doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
-        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN)
         setUpMockDecorationsForTasks(task)
 
         onTaskOpening(task)
@@ -602,7 +593,7 @@
         // Simulate default enforce device restrictions system property
         whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
 
-        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN)
         doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
         setUpMockDecorationsForTasks(task)
 
@@ -1045,7 +1036,7 @@
 
     @Test
     fun testOnDisplayRotation_tasksOutOfValidArea_taskBoundsUpdated() {
-        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
         val secondTask =
             createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
         val thirdTask =
@@ -1073,7 +1064,7 @@
 
     @Test
     fun testOnDisplayRotation_taskInValidArea_taskBoundsNotUpdated() {
-        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
         val secondTask =
             createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
         val thirdTask =
@@ -1100,7 +1091,7 @@
 
     @Test
     fun testOnDisplayRotation_sameOrientationRotation_taskBoundsNotUpdated() {
-        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
         val secondTask =
             createTask(displayId = task.displayId, windowingMode = WINDOWING_MODE_FREEFORM)
         val thirdTask =
@@ -1124,7 +1115,7 @@
 
     @Test
     fun testOnDisplayRotation_differentDisplayId_taskBoundsNotUpdated() {
-        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
         val secondTask = createTask(displayId = -2, windowingMode = WINDOWING_MODE_FREEFORM)
         val thirdTask = createTask(displayId = -3, windowingMode = WINDOWING_MODE_FREEFORM)
 
@@ -1149,7 +1140,7 @@
 
     @Test
     fun testOnDisplayRotation_nonFreeformTask_taskBoundsNotUpdated() {
-        val task = createTask(focused = true, windowingMode = WINDOWING_MODE_FREEFORM)
+        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
         val secondTask = createTask(displayId = -2, windowingMode = WINDOWING_MODE_FULLSCREEN)
         val thirdTask = createTask(displayId = -3, windowingMode = WINDOWING_MODE_PINNED)
 
@@ -1322,7 +1313,6 @@
             displayId: Int = DEFAULT_DISPLAY,
             @WindowingMode windowingMode: Int,
             activityType: Int = ACTIVITY_TYPE_STANDARD,
-            focused: Boolean = true,
             activityInfo: ActivityInfo = ActivityInfo(),
             requestingImmersive: Boolean = false
     ): RunningTaskInfo {
@@ -1333,7 +1323,6 @@
                 .setActivityType(activityType)
                 .build().apply {
                     topActivityInfo = activityInfo
-                    isFocused = focused
                     isResizeable = true
                     requestedVisibleTypes = if (requestingImmersive) {
                         statusBars().inv()
@@ -1351,7 +1340,6 @@
                 any(), any(), any(), any(), any(), any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
-        whenever(decoration.isFocused).thenReturn(task.isFocused)
         whenever(decoration.user).thenReturn(mockUserHandle)
         if (task.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
             whenever(mockSplitScreenController.isTaskInSplitScreen(task.taskId))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 35be80e..1d11d2e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -279,7 +279,7 @@
         final DesktopModeWindowDecoration spyWindowDecor =
                 spy(createWindowDecoration(taskInfo));
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
 
         // Menus should close if open before the task being invisible causes relayout to return.
         verify(spyWindowDecor).closeHandleMenu();
@@ -298,7 +298,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
     }
@@ -318,7 +319,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
     }
@@ -343,7 +345,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(customTaskDensity);
     }
@@ -369,7 +372,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(systemDensity);
     }
@@ -391,7 +395,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.hasInputFeatureSpy()).isTrue();
     }
@@ -412,7 +417,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
     }
@@ -432,7 +438,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
     }
@@ -452,7 +459,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(hasNoInputChannelFeature(relayoutParams)).isFalse();
     }
@@ -473,7 +481,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
     }
@@ -494,7 +503,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
     }
@@ -516,7 +526,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
     }
@@ -539,7 +550,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
     }
@@ -560,7 +572,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(
                 (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0)
@@ -583,7 +596,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(
                 (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0)
@@ -612,7 +626,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ true,
-                insetsState);
+                insetsState,
+                /* hasGlobalFocus= */ true);
 
         // Takes status bar inset as padding, ignores caption bar inset.
         assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -634,7 +649,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ true,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mIsInsetSource).isFalse();
     }
@@ -655,7 +671,8 @@
                 /* isStatusBarVisible */ false,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         // Header is always shown because it's assumed the status bar is always visible.
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -676,7 +693,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
     }
@@ -696,7 +714,8 @@
                 /* isStatusBarVisible */ false,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -716,7 +735,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ true,
                 /* inFullImmersiveMode */ false,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -737,7 +757,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ true,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
 
@@ -750,7 +771,8 @@
                 /* isStatusBarVisible */ false,
                 /* isKeyguardVisibleAndOccluded */ false,
                 /* inFullImmersiveMode */ true,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -771,7 +793,8 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ true,
                 /* inFullImmersiveMode */ true,
-                new InsetsState());
+                new InsetsState(),
+                /* hasGlobalFocus= */ true);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -782,7 +805,7 @@
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockTransaction).apply();
         verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any());
@@ -797,7 +820,7 @@
         // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
         taskInfo.isResizeable = false;
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockTransaction, never()).apply();
         verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
@@ -809,7 +832,7 @@
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
     }
@@ -821,7 +844,7 @@
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         // Once for view host, the other for the AppHandle input layer.
         verify(mMockHandler, times(2)).post(runnableArgument.capture());
@@ -838,7 +861,7 @@
         // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
         taskInfo.isResizeable = false;
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
         verify(mMockHandler, never()).post(any());
@@ -850,11 +873,11 @@
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
         // Once for view host, the other for the AppHandle input layer.
         verify(mMockHandler, times(2)).post(runnableArgument.capture());
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
     }
@@ -865,7 +888,7 @@
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
         // Once for view host, the other for the AppHandle input layer.
         verify(mMockHandler, times(2)).post(runnableArgument.capture());
 
@@ -998,7 +1021,7 @@
         runnableArgument.getValue().run();
 
         // Relayout decor with same captured link
-        decor.relayout(taskInfo);
+        decor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         // Verify handle menu's browser link not set to captured link since link is expired
         createHandleMenu(decor);
@@ -1147,7 +1170,7 @@
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockCaptionHandleRepository, never()).notifyCaptionChanged(any());
     }
@@ -1164,7 +1187,7 @@
         ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
                 CaptionState.class);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged(
                 captionStateArgumentCaptor.capture());
@@ -1191,7 +1214,7 @@
         ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
                 CaptionState.class);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
         verify(mMockAppHeaderViewHolder, atLeastOnce()).runOnAppChipGlobalLayout(
                 runnableArgumentCaptor.capture());
         runnableArgumentCaptor.getValue().invoke();
@@ -1214,7 +1237,7 @@
         ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
                 CaptionState.class);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
 
         verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged(
                 captionStateArgumentCaptor.capture());
@@ -1234,7 +1257,7 @@
         ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
                 CaptionState.class);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
         createHandleMenu(spyWindowDecor);
 
         verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged(
@@ -1259,7 +1282,7 @@
         ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
                 CaptionState.class);
 
-        spyWindowDecor.relayout(taskInfo);
+        spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
         createHandleMenu(spyWindowDecor);
         spyWindowDecor.closeHandleMenu();
 
@@ -1356,7 +1379,7 @@
         windowDecor.setOpenInBrowserClickListener(mMockOpenInBrowserClickListener);
         windowDecor.mDecorWindowContext = mContext;
         if (relayout) {
-            windowDecor.relayout(taskInfo);
+            windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
         }
         return windowDecor;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 7543fed..ca1f9ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -624,7 +624,7 @@
 
     @Test
     fun testDragResize_resize_resizingTaskReorderedToTopWhenNotFocused() {
-        mockWindowDecoration.mTaskInfo.isFocused = false
+        mockWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
                 STARTING_BOUNDS.left.toFloat(),
@@ -640,7 +640,7 @@
 
     @Test
     fun testDragResize_resize_resizingTaskNotReorderedToTopWhenFocused() {
-        mockWindowDecoration.mTaskInfo.isFocused = true
+        mockWindowDecoration.mHasGlobalFocus = true
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
                 STARTING_BOUNDS.left.toFloat(),
@@ -656,7 +656,7 @@
 
     @Test
     fun testDragResize_drag_draggedTaskNotReorderedToTop() {
-        mockWindowDecoration.mTaskInfo.isFocused = false
+        mockWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_UNDEFINED, // drag
                 STARTING_BOUNDS.left.toFloat(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 1273ee8..1dfbd67 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -323,7 +323,7 @@
 
     @Test
     fun testDragResize_resize_resizingTaskReorderedToTopWhenNotFocused() = runOnUiThread {
-        mockDesktopWindowDecoration.mTaskInfo.isFocused = false
+        mockDesktopWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
                 STARTING_BOUNDS.left.toFloat(),
@@ -339,7 +339,7 @@
 
     @Test
     fun testDragResize_resize_resizingTaskNotReorderedToTopWhenFocused() = runOnUiThread {
-        mockDesktopWindowDecoration.mTaskInfo.isFocused = true
+        mockDesktopWindowDecoration.mHasGlobalFocus = true
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_RIGHT, // Resize right
                 STARTING_BOUNDS.left.toFloat(),
@@ -355,7 +355,7 @@
 
     @Test
     fun testDragResize_drag_draggedTaskNotReorderedToTop() = runOnUiThread {
-        mockDesktopWindowDecoration.mTaskInfo.isFocused = false
+        mockDesktopWindowDecoration.mHasGlobalFocus = false
         taskPositioner.onDragPositioningStart(
                 CTRL_TYPE_UNDEFINED, // drag
                 STARTING_BOUNDS.left.toFloat(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 54dd15ba..bb41e9c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -203,13 +203,12 @@
                 .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
                 .setVisible(false)
                 .build();
-        taskInfo.isFocused = false;
         // Density is 2. Shadow radius is 10px. Caption height is 64px.
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
 
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
 
         verify(decorContainerSurfaceBuilder, never()).build();
         verify(taskBackgroundSurfaceBuilder, never()).build();
@@ -243,13 +242,12 @@
                 .setVisible(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM)
                 .build();
-        taskInfo.isFocused = true;
         // Density is 2. Shadow radius is 10px. Caption height is 64px.
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
         mRelayoutParams.mIsCaptionVisible = true;
 
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(decorContainerSurfaceBuilder).setParent(mMockTaskSurface);
         verify(decorContainerSurfaceBuilder).setContainerLayer();
@@ -316,14 +314,13 @@
                 .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
                 .setVisible(true)
                 .build();
-        taskInfo.isFocused = true;
         // Density is 2. Shadow radius is 10px. Caption height is 64px.
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
 
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
         mRelayoutParams.mIsCaptionVisible = true;
 
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlViewHost, never()).release();
         verify(t, never()).apply();
@@ -333,7 +330,7 @@
         final SurfaceControl.Transaction t2 = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t2);
         taskInfo.isVisible = false;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, false /* hasGlobalFocus */);
 
         final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost);
         releaseOrder.verify(mMockSurfaceControlViewHost).release();
@@ -361,7 +358,7 @@
                 .build();
 
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         // It shouldn't show the window decoration when it can't obtain the display instance.
         assertThat(mRelayoutResult.mRootView).isNull();
@@ -417,10 +414,9 @@
                 .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
                 .setVisible(true)
                 .build();
-        taskInfo.isFocused = true;
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         final SurfaceControl additionalWindowSurface = mock(SurfaceControl.class);
         final SurfaceControl.Builder additionalWindowSurfaceBuilder =
@@ -470,11 +466,10 @@
                 .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
                 .setVisible(true)
                 .build();
-        taskInfo.isFocused = true;
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
         verify(captionContainerSurfaceBuilder).setContainerLayer();
@@ -510,11 +505,11 @@
                 .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
                 .setVisible(true)
                 .build();
-        taskInfo.isFocused = true;
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */);
+        windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */,
+                true /* hasGlobalFocus */);
 
         verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
     }
@@ -549,10 +544,9 @@
                 .setVisible(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM)
                 .build();
-        taskInfo.isFocused = true;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlStartT).setColor(mMockTaskSurface, new float[]{1.f, 1.f, 0.f});
 
@@ -575,7 +569,7 @@
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
         mRelayoutParams.mIsCaptionVisible = true;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
                 eq(0) /* index */, eq(captionBar()), any(), any(), anyInt());
@@ -611,10 +605,9 @@
                 .setVisible(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                 .build();
-        taskInfo.isFocused = true;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlStartT).unsetColor(mMockTaskSurface);
 
@@ -635,7 +628,7 @@
         // Hidden from the beginning, so no insets were ever added.
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
         mRelayoutParams.mIsCaptionVisible = false;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         // Never added.
         verify(mMockWindowContainerTransaction, never()).addInsetsSource(eq(taskInfo.token), any(),
@@ -663,7 +656,7 @@
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
         mRelayoutParams.mIsInsetSource = false;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         // Never added.
         verify(mMockWindowContainerTransaction, never()).addInsetsSource(eq(taskInfo.token), any(),
@@ -687,11 +680,11 @@
 
         mRelayoutParams.mIsCaptionVisible = true;
         mRelayoutParams.mIsInsetSource = true;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         mRelayoutParams.mIsCaptionVisible = true;
         mRelayoutParams.mIsInsetSource = false;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         // Insets should be removed.
         verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
@@ -715,7 +708,7 @@
 
         // Relayout will add insets.
         mRelayoutParams.mIsCaptionVisible = true;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
         verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
                 eq(0) /* index */, eq(captionBar()), any(), any(), anyInt());
         verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
@@ -768,10 +761,10 @@
         final ActivityManager.RunningTaskInfo firstTaskInfo =
                 builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build();
         final TestWindowDecoration windowDecor = createWindowDecoration(firstTaskInfo);
-        windowDecor.relayout(firstTaskInfo);
+        windowDecor.relayout(firstTaskInfo, true /* hasGlobalFocus */);
         final ActivityManager.RunningTaskInfo secondTaskInfo =
                 builder.setToken(token).setBounds(new Rect(50, 50, 1000, 1000)).build();
-        windowDecor.relayout(secondTaskInfo);
+        windowDecor.relayout(secondTaskInfo, true /* hasGlobalFocus */);
 
         // Insets should be applied twice.
         verify(mMockWindowContainerTransaction, times(2)).addInsetsSource(eq(token), any(),
@@ -796,10 +789,10 @@
         final ActivityManager.RunningTaskInfo firstTaskInfo =
                 builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build();
         final TestWindowDecoration windowDecor = createWindowDecoration(firstTaskInfo);
-        windowDecor.relayout(firstTaskInfo);
+        windowDecor.relayout(firstTaskInfo, true /* hasGlobalFocus */);
         final ActivityManager.RunningTaskInfo secondTaskInfo =
                 builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build();
-        windowDecor.relayout(secondTaskInfo);
+        windowDecor.relayout(secondTaskInfo, true /* hasGlobalFocus */);
 
         // Insets should only need to be applied once.
         verify(mMockWindowContainerTransaction, times(1)).addInsetsSource(eq(token), any(),
@@ -824,7 +817,7 @@
         mRelayoutParams.mIsCaptionVisible = true;
         mRelayoutParams.mInsetSourceFlags =
                 FLAG_FORCE_CONSUMING | FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         // Caption inset source should add params' flags.
         verify(mMockWindowContainerTransaction).addInsetsSource(eq(token), any(),
@@ -845,14 +838,13 @@
                 .setVisible(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM)
                 .build();
-        taskInfo.isFocused = true;
         // Density is 2. Shadow radius is 10px. Caption height is 64px.
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
 
         mRelayoutParams.mSetTaskPositionAndCrop = false;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlStartT, never()).setWindowCrop(
                 eq(mMockTaskSurface), anyInt(), anyInt());
@@ -875,13 +867,12 @@
                 .setVisible(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM)
                 .build();
-        taskInfo.isFocused = true;
         // Density is 2. Shadow radius is 10px. Caption height is 64px.
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
         mRelayoutParams.mSetTaskPositionAndCrop = true;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlStartT).setWindowCrop(
                 eq(mMockTaskSurface), anyInt(), anyInt());
@@ -932,12 +923,12 @@
         when(mMockDisplayController.getInsetsState(task.displayId))
                 .thenReturn(createInsetsState(statusBars(), true /* visible */));
         final TestWindowDecoration decor = spy(createWindowDecoration(task));
-        decor.relayout(task);
+        decor.relayout(task, true /* hasGlobalFocus */);
         assertTrue(decor.mIsStatusBarVisible);
 
         decor.onInsetsStateChanged(createInsetsState(statusBars(), false /* visible */));
 
-        verify(decor, times(2)).relayout(task);
+        verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */);
     }
 
     @Test
@@ -947,11 +938,11 @@
         when(mMockDisplayController.getInsetsState(task.displayId))
                 .thenReturn(createInsetsState(statusBars(), true /* visible */));
         final TestWindowDecoration decor = spy(createWindowDecoration(task));
-        decor.relayout(task);
+        decor.relayout(task, true /* hasGlobalFocus */);
 
         decor.onInsetsStateChanged(createInsetsState(statusBars(), true /* visible */));
 
-        verify(decor, times(1)).relayout(task);
+        verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */);
     }
 
     @Test
@@ -960,13 +951,13 @@
         when(mMockDisplayController.getInsetsState(task.displayId))
                 .thenReturn(createInsetsState(statusBars(), true /* visible */));
         final TestWindowDecoration decor = spy(createWindowDecoration(task));
-        decor.relayout(task);
+        decor.relayout(task, true /* hasGlobalFocus */);
         assertFalse(decor.mIsKeyguardVisibleAndOccluded);
 
         decor.onKeyguardStateChanged(true /* visible */, true /* occluding */);
 
         assertTrue(decor.mIsKeyguardVisibleAndOccluded);
-        verify(decor, times(2)).relayout(task);
+        verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */);
     }
 
     @Test
@@ -975,19 +966,18 @@
         when(mMockDisplayController.getInsetsState(task.displayId))
                 .thenReturn(createInsetsState(statusBars(), true /* visible */));
         final TestWindowDecoration decor = spy(createWindowDecoration(task));
-        decor.relayout(task);
+        decor.relayout(task, true /* hasGlobalFocus */);
         assertFalse(decor.mIsKeyguardVisibleAndOccluded);
 
         decor.onKeyguardStateChanged(false /* visible */, true /* occluding */);
 
-        verify(decor, times(1)).relayout(task);
+        verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */);
     }
 
     private ActivityManager.RunningTaskInfo createTaskInfo() {
         final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
                 .setVisible(true)
                 .build();
-        taskInfo.isFocused = true;
         return taskInfo;
     }
 
@@ -1055,8 +1045,8 @@
         }
 
         @Override
-        void relayout(ActivityManager.RunningTaskInfo taskInfo) {
-            relayout(taskInfo, false /* applyStartTransactionOnDraw */);
+        void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
+            relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus);
         }
 
         @Override
@@ -1078,10 +1068,11 @@
         }
 
         void relayout(ActivityManager.RunningTaskInfo taskInfo,
-                boolean applyStartTransactionOnDraw) {
+                boolean applyStartTransactionOnDraw, boolean hasGlobalFocus) {
             mRelayoutParams.mRunningTaskInfo = taskInfo;
             mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
             mRelayoutParams.mLayoutResId = R.layout.caption_layout;
+            mRelayoutParams.mHasGlobalFocus = hasGlobalFocus;
             relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
                     mMockWindowContainerTransaction, mMockView, mRelayoutResult);
         }
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 385fbfe..a39f30b 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -75,7 +75,6 @@
         "BigBufferStream.cpp",
         "ChunkIterator.cpp",
         "ConfigDescription.cpp",
-        "CursorWindow.cpp",
         "FileStream.cpp",
         "Idmap.cpp",
         "LoadedArsc.cpp",
@@ -114,6 +113,7 @@
             srcs: [
                 "BackupData.cpp",
                 "BackupHelpers.cpp",
+                "CursorWindow.cpp",
             ],
             shared_libs: [
                 "libbase",
@@ -147,6 +147,11 @@
                 "libz",
             ],
         },
+        host_linux: {
+            srcs: [
+                "CursorWindow.cpp",
+            ],
+        },
         windows: {
             enabled: true,
         },
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index abf2b0a..5e645cc 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -18,11 +18,10 @@
 
 #include <androidfw/CursorWindow.h>
 
-#include "android-base/logging.h"
-#include "android-base/mapped_file.h"
-#include "cutils/ashmem.h"
+#include <sys/mman.h>
 
-using android::base::MappedFile;
+#include "android-base/logging.h"
+#include "cutils/ashmem.h"
 
 namespace android {
 
@@ -40,7 +39,7 @@
 
 CursorWindow::~CursorWindow() {
     if (mAshmemFd != -1) {
-        mMappedFile.reset();
+        ::munmap(mData, mSize);
         ::close(mAshmemFd);
     } else {
         free(mData);
@@ -76,7 +75,6 @@
 status_t CursorWindow::maybeInflate() {
     int ashmemFd = 0;
     void* newData = nullptr;
-    std::unique_ptr<MappedFile> mappedFile;
 
     // Bail early when we can't expand any further
     if (mReadOnly || mSize == mInflatedSize) {
@@ -97,12 +95,11 @@
         goto fail_silent;
     }
 
-    mappedFile = MappedFile::FromFd(ashmemFd, 0, mInflatedSize, PROT_READ | PROT_WRITE);
-    if (mappedFile == nullptr) {
+    newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
+    if (newData == MAP_FAILED) {
         PLOG(ERROR) << "Failed mmap";
         goto fail_silent;
     }
-    newData = mappedFile->data();
 
     if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) {
         PLOG(ERROR) << "Failed ashmem_set_prot_region";
@@ -123,7 +120,6 @@
         mData = newData;
         mSize = mInflatedSize;
         mSlotsOffset = newSlotsOffset;
-        mMappedFile = std::move(mappedFile);
 
         updateSlotsData();
     }
@@ -134,12 +130,11 @@
 fail:
     LOG(ERROR) << "Failed maybeInflate";
 fail_silent:
-    mappedFile.reset();
+    ::munmap(newData, mInflatedSize);
     ::close(ashmemFd);
     return UNKNOWN_ERROR;
 }
 
-#ifdef __linux__
 status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) {
     *outWindow = nullptr;
 
@@ -172,12 +167,11 @@
             goto fail_silent;
         }
 
-        window->mMappedFile = MappedFile::FromFd(window->mAshmemFd, 0, window->mSize, PROT_READ);
-        if (window->mMappedFile == nullptr) {
+        window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0);
+        if (window->mData == MAP_FAILED) {
             PLOG(ERROR) << "Failed mmap";
             goto fail_silent;
         }
-        window->mData = window->mMappedFile->data();
     } else {
         window->mAshmemFd = -1;
 
@@ -241,7 +235,6 @@
 fail_silent:
     return UNKNOWN_ERROR;
 }
-#endif
 
 status_t CursorWindow::clear() {
     if (mReadOnly) {
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index f066e46..3ecd82b 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -65,13 +65,7 @@
   uint32_t string_pool_index_offset;
 };
 
-struct Idmap_target_entry {
-  uint32_t target_id;
-  uint32_t overlay_id;
-};
-
 struct Idmap_target_entry_inline {
-  uint32_t target_id;
   uint32_t start_value_index;
   uint32_t value_count;
 };
@@ -81,10 +75,9 @@
   Res_value value;
 };
 
-struct Idmap_overlay_entry {
-  uint32_t overlay_id;
-  uint32_t target_id;
-};
+static constexpr uint32_t convert_dev_target_id(uint32_t dev_target_id) {
+  return (0x00FFFFFFU & dtohl(dev_target_id));
+}
 
 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
     : data_header_(loaded_idmap->data_header_),
@@ -117,27 +110,29 @@
 }
 
 OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
-                                               const Idmap_overlay_entry* entries,
+                                               Idmap_overlay_entries entries,
                                                uint8_t target_assigned_package_id)
     : data_header_(data_header),
       entries_(entries),
-      target_assigned_package_id_(target_assigned_package_id) {}
+      target_assigned_package_id_(target_assigned_package_id) {
+}
 
 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
-  const Idmap_overlay_entry* first_entry = entries_;
-  const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
-  auto entry = std::lower_bound(first_entry, end_entry, *resId,
-                                [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
-    return dtohl(e1.overlay_id) < overlay_id;
-  });
+  const auto count = dtohl(data_header_->overlay_entry_count);
+  const auto overlay_it_end = entries_.overlay_id + count;
+  const auto entry_it = std::lower_bound(entries_.overlay_id, overlay_it_end, *resId,
+                                         [](uint32_t dev_overlay_id, uint32_t overlay_id) {
+                                           return dtohl(dev_overlay_id) < overlay_id;
+                                         });
 
-  if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
+  if (entry_it == overlay_it_end || dtohl(*entry_it) != *resId) {
     // A mapping for the target resource id could not be found.
     return DynamicRefTable::lookupResourceId(resId);
   }
 
-  *resId = (0x00FFFFFFU & dtohl(entry->target_id))
-      | (((uint32_t) target_assigned_package_id_) << 24U);
+  const auto index = entry_it - entries_.overlay_id;
+  *resId = convert_dev_target_id(entries_.target_id[index]) |
+           (((uint32_t)target_assigned_package_id_) << 24U);
   return NO_ERROR;
 }
 
@@ -145,12 +140,10 @@
   return DynamicRefTable::lookupResourceId(resId);
 }
 
-IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
-                         const Idmap_target_entry* entries,
-                         const Idmap_target_entry_inline* inline_entries,
+IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, Idmap_target_entries entries,
+                         Idmap_target_inline_entries inline_entries,
                          const Idmap_target_entry_inline_value* inline_entry_values,
-                         const ConfigDescription* configs,
-                         uint8_t target_assigned_package_id,
+                         const ConfigDescription* configs, uint8_t target_assigned_package_id,
                          const OverlayDynamicRefTable* overlay_ref_table)
     : data_header_(data_header),
       entries_(entries),
@@ -158,7 +151,8 @@
       inline_entry_values_(inline_entry_values),
       configurations_(configs),
       target_assigned_package_id_(target_assigned_package_id),
-      overlay_ref_table_(overlay_ref_table) { }
+      overlay_ref_table_(overlay_ref_table) {
+}
 
 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
   if ((target_res_id >> 24U) != target_assigned_package_id_) {
@@ -171,15 +165,15 @@
   target_res_id &= 0x00FFFFFFU;
 
   // Check if the target resource is mapped to an overlay resource.
-  auto first_entry = entries_;
-  auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
-  auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
-                                [](const Idmap_target_entry& e, const uint32_t target_id) {
-    return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
-  });
+  const auto target_end = entries_.target_id + dtohl(data_header_->target_entry_count);
+  auto target_it = std::lower_bound(entries_.target_id, target_end, target_res_id,
+                                    [](uint32_t dev_target_id, uint32_t target_id) {
+                                      return convert_dev_target_id(dev_target_id) < target_id;
+                                    });
 
-  if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) {
-    uint32_t overlay_resource_id = dtohl(entry->overlay_id);
+  if (target_it != target_end && convert_dev_target_id(*target_it) == target_res_id) {
+    const auto index = target_it - entries_.target_id;
+    uint32_t overlay_resource_id = dtohl(entries_.overlay_id[index]);
     // Lookup the resource without rewriting the overlay resource id back to the target resource id
     // being looked up.
     overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
@@ -187,20 +181,22 @@
   }
 
   // Check if the target resources is mapped to an inline table entry.
-  auto first_inline_entry = inline_entries_;
-  auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
-  auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
-                                       [](const Idmap_target_entry_inline& e,
-                                          const uint32_t target_id) {
-    return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
-  });
+  const auto inline_entry_target_end =
+      inline_entries_.target_id + dtohl(data_header_->target_inline_entry_count);
+  const auto inline_entry_target_it =
+      std::lower_bound(inline_entries_.target_id, inline_entry_target_end, target_res_id,
+                       [](uint32_t dev_target_id, uint32_t target_id) {
+                         return convert_dev_target_id(dev_target_id) < target_id;
+                       });
 
-  if (inline_entry != end_inline_entry &&
-      (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
+  if (inline_entry_target_it != inline_entry_target_end &&
+      convert_dev_target_id(*inline_entry_target_it) == target_res_id) {
+    const auto index = inline_entry_target_it - inline_entries_.target_id;
     std::map<ConfigDescription, Res_value> values_map;
-    for (int i = 0; i < inline_entry->value_count; i++) {
-      const auto& value = inline_entry_values_[inline_entry->start_value_index + i];
-      const auto& config = configurations_[value.config_index];
+    const auto& inline_entry = inline_entries_.entry[index];
+    for (int i = 0; i < dtohl(inline_entry.value_count); i++) {
+      const auto& value = inline_entry_values_[dtohl(inline_entry.start_value_index) + i];
+      const auto& config = configurations_[dtohl(value.config_index)];
       values_map[config] = value.value;
     }
     return Result(std::move(values_map));
@@ -210,15 +206,15 @@
 
 namespace {
 template <typename T>
-const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label,
+const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const char* label,
                   size_t count = 1) {
   if (!util::IsFourByteAligned(*in_out_data_ptr)) {
-    LOG(ERROR) << "Idmap " << label << " is not word aligned.";
+    LOG(ERROR) << "Idmap " << label << " in " << __func__ << " is not word aligned.";
     return {};
   }
   if ((*in_out_size / sizeof(T)) < count) {
-    LOG(ERROR) << "Idmap too small for the number of " << label << " entries ("
-               << count << ").";
+    LOG(ERROR) << "Idmap too small for the number of " << label << " in " << __func__
+               << " entries (" << count << ").";
     return nullptr;
   }
   auto data_ptr = *in_out_data_ptr;
@@ -229,8 +225,8 @@
 }
 
 std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
-                                           const std::string& label) {
-  const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length");
+                                           const char* label) {
+  const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label);
   if (len == nullptr) {
     return {};
   }
@@ -242,7 +238,7 @@
   const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
   for (uint32_t i = 0; i < padding_size; i++) {
     if (**in_out_data_ptr != 0) {
-      LOG(ERROR) << " Idmap padding of " << label << " is non-zero.";
+      LOG(ERROR) << " Idmap padding of " << label << " in " << __func__ << " is non-zero.";
       return {};
     }
     *in_out_data_ptr += sizeof(uint8_t);
@@ -258,12 +254,10 @@
 #endif
 
 LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
-                         const Idmap_data_header* data_header,
-                         const Idmap_target_entry* target_entries,
-                         const Idmap_target_entry_inline* target_inline_entries,
+                         const Idmap_data_header* data_header, Idmap_target_entries target_entries,
+                         Idmap_target_inline_entries target_inline_entries,
                          const Idmap_target_entry_inline_value* inline_entry_values,
-                         const ConfigDescription* configs,
-                         const Idmap_overlay_entry* overlay_entries,
+                         const ConfigDescription* configs, Idmap_overlay_entries overlay_entries,
                          std::unique_ptr<ResStringPool>&& string_pool,
                          std::string_view overlay_apk_path, std::string_view target_apk_path)
     : header_(header),
@@ -274,10 +268,12 @@
       configurations_(configs),
       overlay_entries_(overlay_entries),
       string_pool_(std::move(string_pool)),
-      idmap_fd_(android::base::utf8::open(idmap_path.c_str(), O_RDONLY|O_CLOEXEC|O_BINARY|O_PATH)),
+      idmap_fd_(
+          android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH)),
       overlay_apk_path_(overlay_apk_path),
       target_apk_path_(target_apk_path),
-      idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) {}
+      idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) {
+}
 
 std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
   ATRACE_CALL();
@@ -319,14 +315,21 @@
   if (data_header == nullptr) {
     return {};
   }
-  auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target",
-                                                     dtohl(data_header->target_entry_count));
-  if (target_entries == nullptr) {
+  Idmap_target_entries target_entries{
+      .target_id = ReadType<uint32_t>(&data_ptr, &data_size, "entries.target_id",
+                                      dtohl(data_header->target_entry_count)),
+      .overlay_id = ReadType<uint32_t>(&data_ptr, &data_size, "entries.overlay_id",
+                                       dtohl(data_header->target_entry_count)),
+  };
+  if (!target_entries.target_id || !target_entries.overlay_id) {
     return {};
   }
-  auto target_inline_entries = ReadType<Idmap_target_entry_inline>(
-      &data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count));
-  if (target_inline_entries == nullptr) {
+  Idmap_target_inline_entries target_inline_entries{
+      .target_id = ReadType<uint32_t>(&data_ptr, &data_size, "target inline.target_id",
+                                      dtohl(data_header->target_inline_entry_count)),
+      .entry = ReadType<Idmap_target_entry_inline>(&data_ptr, &data_size, "target inline.entry",
+                                                   dtohl(data_header->target_inline_entry_count))};
+  if (!target_inline_entries.target_id || !target_inline_entries.entry) {
     return {};
   }
 
@@ -344,9 +347,13 @@
     return {};
   }
 
-  auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
-                                                       dtohl(data_header->overlay_entry_count));
-  if (overlay_entries == nullptr) {
+  Idmap_overlay_entries overlay_entries{
+      .overlay_id = ReadType<uint32_t>(&data_ptr, &data_size, "overlay entries.overlay_id",
+                                       dtohl(data_header->overlay_entry_count)),
+      .target_id = ReadType<uint32_t>(&data_ptr, &data_size, "overlay entries.target_id",
+                                      dtohl(data_header->overlay_entry_count)),
+  };
+  if (!overlay_entries.overlay_id || !overlay_entries.target_id) {
     return {};
   }
   std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index 0996355..9ec026a 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -23,13 +23,9 @@
 #include <string>
 
 #include "android-base/stringprintf.h"
-#ifdef __linux__
 #include "binder/Parcel.h"
-#endif
 #include "utils/String8.h"
 
-#include "android-base/mapped_file.h"
-
 #define LOG_WINDOW(...)
 
 namespace android {
@@ -84,11 +80,9 @@
     ~CursorWindow();
 
     static status_t create(const String8& name, size_t size, CursorWindow** outCursorWindow);
-#ifdef __linux__
     static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);
 
     status_t writeToParcel(Parcel* parcel);
-#endif
 
     inline String8 name() { return mName; }
     inline size_t size() { return mSize; }
@@ -155,8 +149,6 @@
     String8 mName;
     int mAshmemFd = -1;
     void* mData = nullptr;
-    std::unique_ptr<android::base::MappedFile> mMappedFile;
-
     /**
      * Pointer to the first FieldSlot, used to optimize the extremely
      * hot code path of getFieldSlot().
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index 64b1f0c..e213fbd 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -40,6 +40,19 @@
 struct Idmap_target_entry_inline_value;
 struct Idmap_overlay_entry;
 
+struct Idmap_target_entries {
+  const uint32_t* target_id = nullptr;
+  const uint32_t* overlay_id = nullptr;
+};
+struct Idmap_target_inline_entries {
+  const uint32_t* target_id = nullptr;
+  const Idmap_target_entry_inline* entry = nullptr;
+};
+struct Idmap_overlay_entries {
+  const uint32_t* overlay_id = nullptr;
+  const uint32_t* target_id = nullptr;
+};
+
 // A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
 // table and additionally allows for loading strings from the idmap string pool. The idmap string
 // pool strings are offset after the end of the overlay resource table string pool entries so
@@ -67,7 +80,7 @@
 
  private:
   explicit OverlayDynamicRefTable(const Idmap_data_header* data_header,
-                                  const Idmap_overlay_entry* entries,
+                                  Idmap_overlay_entries entries,
                                   uint8_t target_assigned_package_id);
 
   // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target
@@ -75,8 +88,8 @@
   status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
 
   const Idmap_data_header* data_header_;
-  const Idmap_overlay_entry* entries_;
-  const int8_t target_assigned_package_id_;
+  Idmap_overlay_entries entries_;
+  uint8_t target_assigned_package_id_;
 
   friend LoadedIdmap;
   friend IdmapResMap;
@@ -131,17 +144,15 @@
   }
 
  private:
-  explicit IdmapResMap(const Idmap_data_header* data_header,
-                       const Idmap_target_entry* entries,
-                       const Idmap_target_entry_inline* inline_entries,
+  explicit IdmapResMap(const Idmap_data_header* data_header, Idmap_target_entries entries,
+                       Idmap_target_inline_entries inline_entries,
                        const Idmap_target_entry_inline_value* inline_entry_values,
-                       const ConfigDescription* configs,
-                       uint8_t target_assigned_package_id,
+                       const ConfigDescription* configs, uint8_t target_assigned_package_id,
                        const OverlayDynamicRefTable* overlay_ref_table);
 
   const Idmap_data_header* data_header_;
-  const Idmap_target_entry* entries_;
-  const Idmap_target_entry_inline* inline_entries_;
+  Idmap_target_entries entries_;
+  Idmap_target_inline_entries inline_entries_;
   const Idmap_target_entry_inline_value* inline_entry_values_;
   const ConfigDescription* configurations_;
   const uint8_t target_assigned_package_id_;
@@ -192,11 +203,11 @@
 
   const Idmap_header* header_;
   const Idmap_data_header* data_header_;
-  const Idmap_target_entry* target_entries_;
-  const Idmap_target_entry_inline* target_inline_entries_;
+  Idmap_target_entries target_entries_;
+  Idmap_target_inline_entries target_inline_entries_;
   const Idmap_target_entry_inline_value* inline_entry_values_;
   const ConfigDescription* configurations_;
-  const Idmap_overlay_entry* overlay_entries_;
+  const Idmap_overlay_entries overlay_entries_;
   const std::unique_ptr<ResStringPool> string_pool_;
 
   android::base::unique_fd idmap_fd_;
@@ -207,17 +218,13 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
 
-  explicit LoadedIdmap(const std::string& idmap_path,
-                       const Idmap_header* header,
-                       const Idmap_data_header* data_header,
-                       const Idmap_target_entry* target_entries,
-                       const Idmap_target_entry_inline* target_inline_entries,
+  explicit LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
+                       const Idmap_data_header* data_header, Idmap_target_entries target_entries,
+                       Idmap_target_inline_entries target_inline_entries,
                        const Idmap_target_entry_inline_value* inline_entry_values_,
-                       const ConfigDescription* configs,
-                       const Idmap_overlay_entry* overlay_entries,
+                       const ConfigDescription* configs, Idmap_overlay_entries overlay_entries,
                        std::unique_ptr<ResStringPool>&& string_pool,
-                       std::string_view overlay_apk_path,
-                       std::string_view target_apk_path);
+                       std::string_view overlay_apk_path, std::string_view target_apk_path);
 
   friend OverlayStringPool;
 };
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index c264890..e330410 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -48,7 +48,7 @@
 namespace android {
 
 constexpr const uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const uint32_t kIdmapCurrentVersion = 0x00000009u;
+constexpr const uint32_t kIdmapCurrentVersion = 0x0000000Au;
 
 // This must never change.
 constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 8e847e8..7e4b261 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index bc269fe..27817e9 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -4,7 +4,6 @@
   public final class AppFunctionManager {
     ctor public AppFunctionManager(android.content.Context);
     method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
-    method @Deprecated public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
     method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
     field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0
@@ -15,8 +14,7 @@
   public abstract class AppFunctionService extends android.app.Service {
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
-    method @Deprecated @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
     field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
index d660926..43377d8 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -126,33 +126,6 @@
     }
 
     /**
-     * Executes the app function.
-     *
-     * <p>Proxies request and response to the underlying {@link
-     * android.app.appfunctions.AppFunctionManager#executeAppFunction}, converting the request and
-     * response in the appropriate type required by the function.
-     *
-     * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor,
-     *     CancellationSignal, Consumer)} instead. This method will be removed once usage references
-     *     are updated.
-     */
-    @Deprecated
-    public void executeAppFunction(
-            @NonNull ExecuteAppFunctionRequest sidecarRequest,
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
-        Objects.requireNonNull(sidecarRequest);
-        Objects.requireNonNull(executor);
-        Objects.requireNonNull(callback);
-
-        executeAppFunction(
-                sidecarRequest,
-                executor,
-                new CancellationSignal(),
-                callback);
-    }
-
-    /**
      * Returns a boolean through a callback, indicating whether the app function is enabled.
      *
      * <p>* This method can only check app functions owned by the caller, or those where the caller
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
index 6e91de6..0dc87e4 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
@@ -24,8 +24,8 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.util.Log;
 
 import java.util.function.Consumer;
@@ -71,18 +71,21 @@
     private final Binder mBinder =
             android.app.appfunctions.AppFunctionService.createBinder(
                     /* context= */ this,
-                    /* onExecuteFunction= */ (platformRequest, cancellationSignal, callback) -> {
+                    /* onExecuteFunction= */ (platformRequest,
+                            callingPackage,
+                            cancellationSignal,
+                            callback) -> {
                         AppFunctionService.this.onExecuteFunction(
                                 SidecarConverter.getSidecarExecuteAppFunctionRequest(
                                         platformRequest),
+                                callingPackage,
                                 cancellationSignal,
                                 (sidecarResponse) -> {
                                     callback.accept(
                                             SidecarConverter.getPlatformExecuteAppFunctionResponse(
                                                     sidecarResponse));
                                 });
-                    }
-            );
+                    });
 
     @NonNull
     @Override
@@ -107,48 +110,18 @@
      * thread and dispatch the result with the given callback. You should always report back the
      * result using the callback, no matter if the execution was successful or not.
      *
+     * <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel
+     * the execution of function if requested by the system.
+     *
      * @param request The function execution request.
-     * @param cancellationSignal A {@link CancellationSignal} to cancel the request.
+     * @param callingPackage The package name of the app that is requesting the execution.
+     * @param cancellationSignal A signal to cancel the execution.
      * @param callback A callback to report back the result.
      */
     @MainThread
-    public void onExecuteFunction(
+    public abstract void onExecuteFunction(
             @NonNull ExecuteAppFunctionRequest request,
+            @NonNull String callingPackage,
             @NonNull CancellationSignal cancellationSignal,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
-        onExecuteFunction(request, callback);
-    }
-
-    /**
-     * Called by the system to execute a specific app function.
-     *
-     * <p>This method is triggered when the system requests your AppFunctionService to handle a
-     * particular function you have registered and made available.
-     *
-     * <p>To ensure proper routing of function requests, assign a unique identifier to each
-     * function. This identifier doesn't need to be globally unique, but it must be unique within
-     * your app. For example, a function to order food could be identified as "orderFood". In most
-     * cases this identifier should come from the ID automatically generated by the AppFunctions
-     * SDK. You can determine the specific function to invoke by calling {@link
-     * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
-     *
-     * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
-     * thread and dispatch the result with the given callback. You should always report back the
-     * result using the callback, no matter if the execution was successful or not.
-     *
-     * @param request The function execution request.
-     * @param callback A callback to report back the result.
-     *
-     * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal,
-     *     Consumer)} instead. This method will be removed once usage references are updated.
-     */
-    @MainThread
-    @Deprecated
-    public void onExecuteFunction(
-            @NonNull ExecuteAppFunctionRequest request,
-            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
-        Log.w(
-                "AppFunctionService",
-                "Calling deprecated default implementation of onExecuteFunction");
-    }
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback);
 }
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
index d87fec79..969e5d5 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
@@ -234,12 +234,13 @@
     @IntDef(
             prefix = {"RESULT_"},
             value = {
-                    RESULT_OK,
-                    RESULT_DENIED,
-                    RESULT_APP_UNKNOWN_ERROR,
-                    RESULT_INTERNAL_ERROR,
-                    RESULT_INVALID_ARGUMENT,
-                    RESULT_DISABLED
+                RESULT_OK,
+                RESULT_DENIED,
+                RESULT_APP_UNKNOWN_ERROR,
+                RESULT_INTERNAL_ERROR,
+                RESULT_INVALID_ARGUMENT,
+                RESULT_DISABLED,
+                RESULT_CANCELLED
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ResultCode {}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 23cd3ce..266c236 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -501,6 +501,13 @@
     ],
 }
 
+genrule {
+    name: "statslog-hwui-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module hwui --javaPackage com.android.os.coregraphics --javaClass HwuiStatsLog",
+    out: ["com/android/os/coregraphics/HwuiStatsLog.java"],
+}
+
 // ------------------------
 // library
 // ------------------------
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 28d85bd..a373713 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -242,45 +242,59 @@
     }
 }
 
-SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const {
-    const DirtyStack* frame = mHead;
-    Matrix4 transform;
-    SkRect pretransformResult = bounds;
-    while (true) {
-        SkRect currentBounds = pretransformResult;
-        pretransformResult.setEmpty();
-        switch (frame->type) {
-            case TransformRenderNode: {
-                const RenderProperties& props = frame->renderNode->properties();
-                // Perform clipping
-                if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) {
-                    if (!currentBounds.intersect(
-                                SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
-                        currentBounds.setEmpty();
-                    }
+static void computeClipAndTransformImpl(const DirtyStack* currentFrame, SkRect* crop,
+                                        Matrix4* outMatrix) {
+    SkRect currentCrop = *crop;
+    switch (currentFrame->type) {
+        case TransformRenderNode: {
+            const RenderProperties& props = currentFrame->renderNode->properties();
+            // Perform clipping
+            if (props.getClipDamageToBounds() && !currentCrop.isEmpty()) {
+                if (!currentCrop.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
+                    currentCrop.setEmpty();
                 }
+            }
 
-                // apply all transforms
-                mapRect(props, currentBounds, &pretransformResult);
-                frame->renderNode->applyViewPropertyTransforms(transform);
-            } break;
-            case TransformMatrix4:
-                mapRect(frame->matrix4, currentBounds, &pretransformResult);
-                transform.multiply(*frame->matrix4);
-                break;
-            default:
-                pretransformResult = currentBounds;
-                break;
-        }
-        if (frame->prev == frame) break;
-        frame = frame->prev;
+            // apply all transforms
+            crop->setEmpty();
+            mapRect(props, currentCrop, crop);
+        } break;
+        case TransformMatrix4:
+            crop->setEmpty();
+            mapRect(currentFrame->matrix4, currentCrop, crop);
+            break;
+        default:
+            break;
     }
-    SkRect result;
+
+    if (currentFrame->prev != currentFrame) {
+        computeClipAndTransformImpl(currentFrame->prev, crop, outMatrix);
+    }
+    switch (currentFrame->type) {
+        case TransformRenderNode:
+            currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
+            break;
+        case TransformMatrix4:
+            outMatrix->multiply(*currentFrame->matrix4);
+            break;
+        case TransformNone:
+            // nothing to be done
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
+                             currentFrame->type);
+    }
+}
+
+SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const {
+    SkRect cropInGlobal = bounds;
+    outMatrix->loadIdentity();
+    computeClipAndTransformImpl(mHead, &cropInGlobal, outMatrix);
+    SkRect cropInLocal;
     Matrix4 globalToLocal;
-    globalToLocal.loadInverse(transform);
-    mapRect(&globalToLocal, pretransformResult, &result);
-    *outMatrix = transform;
-    return result;
+    globalToLocal.loadInverse(*outMatrix);
+    mapRect(&globalToLocal, cropInGlobal, &cropInLocal);
+    return cropInLocal;
 }
 
 void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index b4e6b72..56191c0 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -205,6 +205,13 @@
     jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty",
                                                          "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
 
+    auto formatProperty = (jstring)env->CallStaticObjectMethod(
+            system, getPropertyMethod, env->NewStringUTF("method_binding_format"),
+            env->NewStringUTF(""));
+    const char* methodFormatChars = env->GetStringUTFChars(formatProperty, 0);
+    setJniMethodFormat(string(methodFormatChars));
+    env->ReleaseStringUTFChars(formatProperty, methodFormatChars);
+
     // Get the names of classes that need to register their native methods
     auto nativesClassesJString = (jstring)env->CallStaticObjectMethod(
             system, getPropertyMethod, env->NewStringUTF("graphics_native_classes"),
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 84bd45d..b73380e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -582,20 +582,22 @@
 size_t Bitmap::mTotalBitmapCount = 0;
 
 void Bitmap::traceBitmapCreate() {
+    size_t bytes = getAllocationByteCount();
+    std::lock_guard lock{mLock};
+    mTotalBitmapBytes += bytes;
+    mTotalBitmapCount++;
     if (ATRACE_ENABLED()) {
-        std::lock_guard lock{mLock};
-        mTotalBitmapBytes += getAllocationByteCount();
-        mTotalBitmapCount++;
         ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
         ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
     }
 }
 
 void Bitmap::traceBitmapDelete() {
+    size_t bytes = getAllocationByteCount();
+    std::lock_guard lock{mLock};
+    mTotalBitmapBytes -= getAllocationByteCount();
+    mTotalBitmapCount--;
     if (ATRACE_ENABLED()) {
-        std::lock_guard lock{mLock};
-        mTotalBitmapBytes -= getAllocationByteCount();
-        mTotalBitmapCount--;
         ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
         ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
     }
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 4563386..70e6bed 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -31,12 +31,34 @@
 
 namespace android {
 
+struct FakedFontKey {
+    uint32_t operator()(const minikin::FakedFont& fakedFont) const {
+        return minikin::Hasher()
+                .update(reinterpret_cast<uintptr_t>(fakedFont.font.get()))
+                .update(fakedFont.fakery.bits())
+                .update(fakedFont.fakery.variationSettings())
+                .hash();
+    }
+};
+
 struct LayoutWrapper {
     LayoutWrapper(minikin::Layout&& layout, float ascent, float descent)
         : layout(std::move(layout)), ascent(ascent), descent(descent)  {}
+
+    LayoutWrapper(minikin::Layout&& layout, float ascent, float descent, std::vector<jlong>&& fonts,
+                  std::vector<uint32_t>&& fontIds)
+            : layout(std::move(layout))
+            , ascent(ascent)
+            , descent(descent)
+            , fonts(std::move(fonts))
+            , fontIds(std::move(fontIds)) {}
+
     minikin::Layout layout;
     float ascent;
     float descent;
+
+    std::vector<jlong> fonts;
+    std::vector<uint32_t> fontIds;  // per glyph
 };
 
 static void releaseLayout(jlong ptr) {
@@ -64,6 +86,43 @@
         overallDescent = std::max(overallDescent, extent.descent);
     }
 
+    if (text_feature::typeface_redesign()) {
+        uint32_t runCount = layout.getFontRunCount();
+
+        std::unordered_map<minikin::FakedFont, uint32_t, FakedFontKey> fakedToFontIds;
+        std::vector<jlong> fonts;
+        std::vector<uint32_t> fontIds;
+
+        fontIds.resize(layout.nGlyphs());
+        for (uint32_t ri = 0; ri < runCount; ++ri) {
+            const minikin::FakedFont& fakedFont = layout.getFontRunFont(ri);
+
+            auto it = fakedToFontIds.find(fakedFont);
+            uint32_t fontId;
+            if (it != fakedToFontIds.end()) {
+                fontId = it->second;  // We've seen it.
+            } else {
+                fontId = fonts.size();  // This is new to us. Create new one.
+                std::shared_ptr<minikin::Font> font = std::make_shared<minikin::Font>(
+                        fakedFont.font, fakedFont.fakery.variationSettings());
+                fonts.push_back(reinterpret_cast<jlong>(new FontWrapper(std::move(font))));
+                fakedToFontIds.insert(std::make_pair(fakedFont, fontId));
+            }
+
+            const uint32_t runStart = layout.getFontRunStart(ri);
+            const uint32_t runEnd = layout.getFontRunEnd(ri);
+            for (uint32_t i = runStart; i < runEnd; ++i) {
+                fontIds[i] = fontId;
+            }
+        }
+
+        std::unique_ptr<LayoutWrapper> ptr =
+                std::make_unique<LayoutWrapper>(std::move(layout), overallAscent, overallDescent,
+                                                std::move(fonts), std::move(fontIds));
+
+        return reinterpret_cast<jlong>(ptr.release());
+    }
+
     std::unique_ptr<LayoutWrapper> ptr = std::make_unique<LayoutWrapper>(
         std::move(layout), overallAscent, overallDescent
     );
@@ -156,6 +215,8 @@
     return layout->layout.getFakery(i).isFakeItalic();
 }
 
+constexpr float NO_OVERRIDE = -1;
+
 float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin::AxisTag tag) {
     for (const minikin::FontVariation& fv : fakery.variationSettings()) {
         if (fv.axisTag == tag) {
@@ -171,12 +232,7 @@
     if (text_feature::typeface_redesign()) {
         float value =
                 findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght);
-        if (!std::isnan(value)) {
-            return value;
-        } else {
-            const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i);
-            return font->style().weight();
-        }
+        return std::isnan(value) ? NO_OVERRIDE : value;
     } else {
         return layout->layout.getFakery(i).wghtAdjustment();
     }
@@ -188,12 +244,7 @@
     if (text_feature::typeface_redesign()) {
         float value =
                 findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital);
-        if (!std::isnan(value)) {
-            return value;
-        } else {
-            const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i);
-            return font->style().isItalic();
-        }
+        return std::isnan(value) ? NO_OVERRIDE : value;
     } else {
         return layout->layout.getFakery(i).italAdjustment();
     }
@@ -207,6 +258,24 @@
 }
 
 // CriticalNative
+static jint TextShaper_Result_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+    const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+    return layout->fonts.size();
+}
+
+// CriticalNative
+static jlong TextShaper_Result_getFontRef(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint fontId) {
+    const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+    return layout->fonts[fontId];
+}
+
+// CriticalNative
+static jint TextShaper_Result_getFontId(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint glyphIdx) {
+    const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+    return layout->fontIds[glyphIdx];
+}
+
+// CriticalNative
 static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) {
     return reinterpret_cast<jlong>(releaseLayout);
 }
@@ -250,6 +319,10 @@
         {"nGetWeightOverride", "(JI)F", (void*)TextShaper_Result_getWeightOverride},
         {"nGetItalicOverride", "(JI)F", (void*)TextShaper_Result_getItalicOverride},
         {"nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc},
+
+        {"nGetFontCount", "(J)I", (void*)TextShaper_Result_getFontCount},
+        {"nGetFontRef", "(JI)J", (void*)TextShaper_Result_getFontRef},
+        {"nGetFontId", "(JI)I", (void*)TextShaper_Result_getFontId},
 };
 
 int register_android_graphics_text_TextShaper(JNIEnv* env) {
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 9673c5f..23097f6 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -17,6 +17,7 @@
 #include "Color.h"
 
 #include <Properties.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
 #include <android/hardware_buffer.h>
 #include <android/native_window.h>
 #include <ui/ColorSpace.h>
@@ -25,6 +26,8 @@
 #include <algorithm>
 #include <cmath>
 
+#include "SkColorSpace.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -215,9 +218,13 @@
         return HAL_DATASPACE_ADOBE_RGB;
     }
 
-    if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) &&
-        nearlyEqual(gamut, SkNamedGamut::kRec2020)) {
-        return HAL_DATASPACE_BT2020;
+    if (nearlyEqual(gamut, SkNamedGamut::kRec2020)) {
+        if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) {
+            return HAL_DATASPACE_BT2020;
+        } else if (nearlyEqual(fn, SkNamedTransferFn::kSRGB)) {
+            return static_cast<android_dataspace>(
+                    ::aidl::android::hardware::graphics::common::Dataspace::DISPLAY_BT2020);
+        }
     }
 
     if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) {
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index b10019a..67d5774 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -29,6 +29,10 @@
     libs: [
         "androidx.annotation_annotation",
     ],
+    stub_only_libs: [
+        // Needed for javadoc references.
+        "framework-location.stubs.system",
+    ],
     api_packages: [
         "android.location",
         "com.android.location.provider",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index cdb517b3..536afd4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -51,6 +51,7 @@
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.Overridable;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1918,12 +1919,18 @@
     @Deprecated public void setSpeakerphoneOn(boolean on) {
         final IAudioService service = getService();
         try {
-            service.setSpeakerphoneOn(mICallBack, on);
+            service.setSpeakerphoneOn(mICallBack, on, getAttributionSource());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
+    private AttributionSource getAttributionSource() {
+        Context context = getContext();
+        return (context != null)
+                     ? context.getAttributionSource() : AttributionSource.myAttributionSource();
+    }
+
     /**
      * Checks whether the speakerphone is on or off.
      *
@@ -3089,7 +3096,8 @@
         final IAudioService service = getService();
         try {
             service.startBluetoothSco(mICallBack,
-                    getContext().getApplicationInfo().targetSdkVersion);
+                    getContext().getApplicationInfo().targetSdkVersion,
+                    getAttributionSource());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3114,7 +3122,7 @@
     public void startBluetoothScoVirtualCall() {
         final IAudioService service = getService();
         try {
-            service.startBluetoothScoVirtualCall(mICallBack);
+            service.startBluetoothScoVirtualCall(mICallBack, getAttributionSource());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3134,7 +3142,7 @@
     @Deprecated public void stopBluetoothSco() {
         final IAudioService service = getService();
         try {
-            service.stopBluetoothSco(mICallBack);
+            service.stopBluetoothSco(mICallBack,  getAttributionSource());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -9023,7 +9031,8 @@
                 Log.w(TAG, "setCommunicationDevice: device not found: " + device);
                 return false;
             }
-            return getService().setCommunicationDevice(mICallBack, device.getId());
+            return getService().setCommunicationDevice(mICallBack, device.getId(),
+                    getAttributionSource());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -9035,7 +9044,7 @@
      */
     public void clearCommunicationDevice() {
         try {
-            getService().setCommunicationDevice(mICallBack, 0);
+            getService().setCommunicationDevice(mICallBack, 0, getAttributionSource());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 4d6ddfd..3cd5f52 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -18,7 +18,9 @@
 
 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
+import static android.media.audio.Flags.FLAG_MUTED_BY_PORT_VOLUME_API;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -291,12 +293,24 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public static final int MUTED_BY_VOLUME_SHAPER = (1 << 5);
+    /**
+     * @hide
+     * Flag used when muted by the track's port volume.
+     *
+     * <p>Note: this will replace the stream volume mute when using the AudioFlinger port volume
+     * APIs
+     */
+    @SystemApi
+    @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public static final int MUTED_BY_PORT_VOLUME = (1 << 6);
 
     /** @hide */
     @IntDef(
             flag = true,
             value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED,
-                    MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER})
+                    MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER,
+                    MUTED_BY_PORT_VOLUME})
     @Retention(RetentionPolicy.SOURCE)
     public @interface PlayerMuteEvent {
     }
@@ -858,6 +872,9 @@
                 if ((mMutedState & MUTED_BY_VOLUME_SHAPER) != 0) {
                     apcToString.append("volumeShaper ");
                 }
+                if ((mMutedState & MUTED_BY_PORT_VOLUME) != 0) {
+                    apcToString.append("portVolume ");
+                }
             }
             apcToString.append(" ").append(mFormatInfo);
         }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index a8b863b..bf09cb0 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1698,12 +1698,12 @@
     }
 
     /** @hide Wrapper for native methods called from AudioService */
-    public static int setStreamVolumeIndexAS(int stream, int index, int device) {
+    public static int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) {
         if (DEBUG_VOLUME) {
             Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
-                    + " dev=" + Integer.toHexString(device) + " idx=" + index);
+                    + " dev=" + Integer.toHexString(device) + " idx=" + index + " muted=" + muted);
         }
-        return setStreamVolumeIndex(stream, index, device);
+        return setStreamVolumeIndex(stream, index, muted, device);
     }
 
     // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
@@ -1774,7 +1774,8 @@
     @UnsupportedAppUsage
     public static native int initStreamVolume(int stream, int indexMin, int indexMax);
     @UnsupportedAppUsage
-    private static native int setStreamVolumeIndex(int stream, int index, int device);
+    private static native int setStreamVolumeIndex(int stream, int index, boolean muted,
+            int device);
     /** @hide */
     public static native int getStreamVolumeIndex(int stream, int device);
     /**
@@ -1787,7 +1788,7 @@
      * @return command completion status.
      */
     public static native int setVolumeIndexForAttributes(@NonNull AudioAttributes attributes,
-                                                         int index, int device);
+                                                         int index, boolean muted, int device);
    /**
     * @hide
     * get the volume index for the given {@link AudioAttributes}.
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8394daf..02ca307 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -234,7 +234,7 @@
 
     int getEncodedSurroundMode(int targetSdkVersion);
 
-    void setSpeakerphoneOn(IBinder cb, boolean on);
+    void setSpeakerphoneOn(IBinder cb, boolean on, in AttributionSource attributionSource);
 
     boolean isSpeakerphoneOn();
 
@@ -263,9 +263,10 @@
 
     int getCurrentAudioFocus();
 
-    void startBluetoothSco(IBinder cb, int targetSdkVersion);
-    void startBluetoothScoVirtualCall(IBinder cb);
-    void stopBluetoothSco(IBinder cb);
+    void startBluetoothSco(IBinder cb, int targetSdkVersion,
+            in AttributionSource attributionSource);
+    void startBluetoothScoVirtualCall(IBinder cb, in AttributionSource attributionSource);
+    void stopBluetoothSco(IBinder cb, in AttributionSource attributionSource);
 
     void forceVolumeControlStream(int streamType, IBinder cb);
 
@@ -542,7 +543,7 @@
 
     int[] getAvailableCommunicationDeviceIds();
 
-    boolean setCommunicationDevice(IBinder cb, int portId);
+    boolean setCommunicationDevice(IBinder cb, int portId, in AttributionSource attributionSource);
 
     int getCommunicationDevice();
 
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index eeb4853..961962f 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -85,8 +85,7 @@
     void updateScanningState(IMediaRouter2Manager manager, @JavaPassthrough(annotation="@android.media.MediaRouter2.ScanningState") int scanningState);
 
     void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
-            in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route,
-            in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName);
+            in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
     void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, in MediaRoute2Info route);
     void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 2d7db5e..b022ea1 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -16,6 +16,9 @@
 
 package android.media;
 
+import static com.android.media.flags.Flags.FLAG_UPDATE_CLIENT_PROFILE_PRIORITY;
+
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -131,7 +134,7 @@
     private int mCasSystemId;
     private int mUserId;
     private TunerResourceManager mTunerResourceManager = null;
-    private final Map<Session, Integer> mSessionMap = new HashMap<>();
+    private final Map<Session, Long> mSessionMap = new HashMap<>();
 
     /**
      * Scrambling modes used to open cas sessions.
@@ -970,6 +973,27 @@
         registerClient(context, tvInputServiceSessionId, priorityHint);
     }
 
+    /**
+     * Updates client priority with an arbitrary value along with a nice value.
+     *
+     * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+     * to reclaim insufficient resources from another client.
+     *
+     * <p>The nice value represents how much the client intends to give up the resource when an
+     * insufficient resource situation happens.
+     *
+     * @see <a
+     *     href="https://source.android.com/docs/devices/tv/tuner-framework#priority-nice-value">
+     *     Priority value and nice value</a>
+     * @param priority the new priority. Any negative value would cause no-op on priority setting
+     *     and the API would only process nice value setting in that case.
+     * @param niceValue the nice value.
+     */
+    @FlaggedApi(FLAG_UPDATE_CLIENT_PROFILE_PRIORITY)
+    public boolean updateResourcePriority(int priority, int niceValue) {
+        return mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue);
+    }
+
     IHwBinder getBinder() {
         if (mICas != null) {
             return null; // Return IHwBinder only for HIDL
@@ -1126,10 +1150,10 @@
         }
     }
 
-    private int getSessionResourceHandle() throws MediaCasException {
+    private long getSessionResourceHandle() throws MediaCasException {
         validateInternalStates();
 
-        int[] sessionResourceHandle = new int[1];
+        long[] sessionResourceHandle = new long[1];
         sessionResourceHandle[0] = -1;
         if (mTunerResourceManager != null) {
             CasSessionRequest casSessionRequest = new CasSessionRequest();
@@ -1144,8 +1168,7 @@
         return sessionResourceHandle[0];
     }
 
-    private void addSessionToResourceMap(Session session, int sessionResourceHandle) {
-
+    private void addSessionToResourceMap(Session session, long sessionResourceHandle) {
         if (sessionResourceHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
             synchronized (mSessionMap) {
                 mSessionMap.put(session, sessionResourceHandle);
@@ -1178,13 +1201,14 @@
      * @throws MediaCasStateException for CAS-specific state exceptions.
      */
     public Session openSession() throws MediaCasException {
-        int sessionResourceHandle = getSessionResourceHandle();
+        long sessionResourceHandle = getSessionResourceHandle();
 
         try {
             if (mICas != null) {
                 try {
                     byte[] sessionId = mICas.openSessionDefault();
                     Session session = createFromSessionId(sessionId);
+                    addSessionToResourceMap(session, sessionResourceHandle);
                     Log.d(TAG, "Write Stats Log for succeed to Open Session.");
                     FrameworkStatsLog.write(
                             FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS,
@@ -1238,7 +1262,7 @@
     @Nullable
     public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode)
             throws MediaCasException {
-        int sessionResourceHandle = getSessionResourceHandle();
+        long sessionResourceHandle = getSessionResourceHandle();
 
         if (mICas != null) {
             try {
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 80b606c..5e55f64 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -34,7 +34,7 @@
 
 /**
  * MediaMuxer facilitates muxing elementary streams. Currently MediaMuxer supports MP4, Webm
- * and 3GP file as the output. It also supports muxing B-frames in MP4 since Android Nougat.
+ * and 3GP file as the output. It also supports muxing B-frames in MP4 since Android Nougat MR1.
  * <p>
  * It is generally used like this:
  *
@@ -191,14 +191,14 @@
     <td>&#9675;</td>
     <td>&#9679;</td>
    </tr>
-    <td align="center">Muxing B-Frames(bi-directional predicted frames)</td>
+    <td align="center">Muxing B-Frames (bi-directional predicted frames)</td>
     <td>&#9675;</td>
     <td>&#9675;</td>
     <td>&#9675;</td>
     <td>&#9675;</td>
     <td>&#9675;</td>
     <td>&#9675;</td>
-    <td>&#8277;</td>
+    <td>&#9675;</td>
     <td>&#8277;</td>
     <td>&#8277;</td>
    </tr>
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index b84990b..3499c43 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2770,7 +2770,7 @@
                     || isSystemRouteReselection) {
                 transferToRoute(sessionInfo, route, mClientUser, mClientPackageName);
             } else {
-                requestCreateSession(sessionInfo, route, mClientUser, mClientPackageName);
+                requestCreateSession(sessionInfo, route);
             }
         }
 
@@ -2826,10 +2826,7 @@
          * @param route The {@link MediaRoute2Info route} to transfer to.
          */
         private void requestCreateSession(
-                @NonNull RoutingSessionInfo oldSession,
-                @NonNull MediaRoute2Info route,
-                @NonNull UserHandle transferInitiatorUserHandle,
-                @NonNull String transferInitiatorPackageName) {
+                @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
             if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
                 Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
                 this.onTransferFailed(oldSession, route);
@@ -2840,12 +2837,7 @@
 
             try {
                 mMediaRouterService.requestCreateSessionWithManager(
-                        mClient,
-                        requestId,
-                        oldSession,
-                        route,
-                        transferInitiatorUserHandle,
-                        transferInitiatorPackageName);
+                        mClient, requestId, oldSession, route);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 8fa0e49..7e1dccf 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -524,8 +524,7 @@
             transferToRoute(
                     sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName);
         } else {
-            requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
-                    transferInitiatorPackageName);
+            requestCreateSession(sessionInfo, route);
         }
     }
 
@@ -914,9 +913,7 @@
         }
     }
 
-    private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route,
-            @NonNull UserHandle transferInitiatorUserHandle,
-            @NonNull String transferInitiationPackageName) {
+    private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) {
         if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
             Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
             notifyTransferFailed(oldSession, route);
@@ -927,8 +924,7 @@
 
         try {
             mMediaRouterService.requestCreateSessionWithManager(
-                    mClient, requestId, oldSession, route, transferInitiatorUserHandle,
-                    transferInitiationPackageName);
+                    mClient, requestId, oldSession, route);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 7252135..1ef98f2 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -70,6 +70,13 @@
 }
 
 flag {
+    name: "update_client_profile_priority"
+    namespace: "media"
+    description : "Feature flag to add updateResourcePriority api to MediaCas"
+    bug: "300565729"
+}
+
+flag {
      name: "enable_built_in_speaker_route_suitability_statuses"
      is_exported: true
      namespace: "media_solutions"
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 10423b9..d49f7dd 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -64,3 +64,11 @@
     description: "Media Quality V1.0 APIs for Android W"
     bug: "348412562"
 }
+
+flag {
+    name: "tif_extension_standardization"
+    is_exported: true
+    namespace: "media_tv"
+    description: "Standardize AIDL Extension Interface of TIS"
+    bug: "330366987"
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 92f6eaf..cdf50ec 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -295,13 +295,13 @@
     private EventHandler mHandler;
     @Nullable
     private FrontendInfo mFrontendInfo;
-    private Integer mFrontendHandle;
+    private Long mFrontendHandle;
     private Tuner mFeOwnerTuner = null;
     private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
     private Integer mDesiredFrontendId = null;
     private int mUserId;
     private Lnb mLnb;
-    private Integer mLnbHandle;
+    private Long mLnbHandle;
     @Nullable
     private OnTuneEventListener mOnTuneEventListener;
     @Nullable
@@ -324,10 +324,10 @@
     private final ReentrantLock mDemuxLock = new ReentrantLock();
     private int mRequestedCiCamId;
 
-    private Integer mDemuxHandle;
-    private Integer mFrontendCiCamHandle;
+    private Long mDemuxHandle;
+    private Long mFrontendCiCamHandle;
     private Integer mFrontendCiCamId;
-    private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>();
+    private Map<Long, WeakReference<Descrambler>> mDescramblers = new HashMap<>();
     private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>();
 
     private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
@@ -949,7 +949,7 @@
     private void releaseDescramblers() {
         synchronized (mDescramblers) {
             if (!mDescramblers.isEmpty()) {
-                for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
+                for (Map.Entry<Long, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
                     Descrambler descrambler = d.getValue().get();
                     if (descrambler != null) {
                         descrambler.close();
@@ -1010,7 +1010,7 @@
     /**
      * Native method to open frontend of the given ID.
      */
-    private native Frontend nativeOpenFrontendByHandle(int handle);
+    private native Frontend nativeOpenFrontendByHandle(long handle);
     private native int nativeShareFrontend(int id);
     private native int nativeUnshareFrontend();
     private native void nativeRegisterFeCbListener(long nativeContext);
@@ -1039,21 +1039,21 @@
     private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
     private native int nativeGetMaxNumberOfFrontends(int frontendType);
     private native int nativeRemoveOutputPid(int pid);
-    private native Lnb nativeOpenLnbByHandle(int handle);
+    private native Lnb nativeOpenLnbByHandle(long handle);
     private native Lnb nativeOpenLnbByName(String name);
     private native FrontendStatusReadiness[] nativeGetFrontendStatusReadiness(int[] statusTypes);
 
-    private native Descrambler nativeOpenDescramblerByHandle(int handle);
-    private native int nativeOpenDemuxByhandle(int handle);
+    private native Descrambler nativeOpenDescramblerByHandle(long handle);
+    private native int nativeOpenDemuxByhandle(long handle);
 
     private native DvrRecorder nativeOpenDvrRecorder(long bufferSize);
     private native DvrPlayback nativeOpenDvrPlayback(long bufferSize);
 
     private native DemuxCapabilities nativeGetDemuxCapabilities();
-    private native DemuxInfo nativeGetDemuxInfo(int demuxHandle);
+    private native DemuxInfo nativeGetDemuxInfo(long demuxHandle);
 
-    private native int nativeCloseDemux(int handle);
-    private native int nativeCloseFrontend(int handle);
+    private native int nativeCloseDemux(long handle);
+    private native int nativeCloseFrontend(long handle);
     private native int nativeClose();
 
     private static native SharedFilter nativeOpenSharedFilter(String token);
@@ -1371,7 +1371,7 @@
     }
 
     private boolean requestFrontend() {
-        int[] feHandle = new int[1];
+        long[] feHandle = new long[1];
         boolean granted = false;
         try {
             TunerFrontendRequest request = new TunerFrontendRequest();
@@ -2379,7 +2379,7 @@
     }
 
     private boolean requestLnb() {
-        int[] lnbHandle = new int[1];
+        long[] lnbHandle = new long[1];
         TunerLnbRequest request = new TunerLnbRequest();
         request.clientId = mClientId;
         boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle);
@@ -2709,7 +2709,7 @@
     }
 
     private boolean requestDemux() {
-        int[] demuxHandle = new int[1];
+        long[] demuxHandle = new long[1];
         TunerDemuxRequest request = new TunerDemuxRequest();
         request.clientId = mClientId;
         request.desiredFilterTypes = mDesiredDemuxInfo.getFilterTypes();
@@ -2722,14 +2722,14 @@
     }
 
     private Descrambler requestDescrambler() {
-        int[] descramblerHandle = new int[1];
+        long[] descramblerHandle = new long[1];
         TunerDescramblerRequest request = new TunerDescramblerRequest();
         request.clientId = mClientId;
         boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle);
         if (!granted) {
             return null;
         }
-        int handle = descramblerHandle[0];
+        long handle = descramblerHandle[0];
         Descrambler descrambler = nativeOpenDescramblerByHandle(handle);
         if (descrambler != null) {
             synchronized (mDescramblers) {
@@ -2743,7 +2743,7 @@
     }
 
     private boolean requestFrontendCiCam(int ciCamId) {
-        int[] ciCamHandle = new int[1];
+        long[] ciCamHandle = new long[1];
         TunerCiCamRequest request = new TunerCiCamRequest();
         request.clientId = mClientId;
         request.ciCamId = ciCamId;
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index d268aeb..bb581eb 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -66,7 +66,7 @@
     private static final String TAG = "TunerResourceManager";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    public static final int INVALID_RESOURCE_HANDLE = -1;
+    public static final long INVALID_RESOURCE_HANDLE = -1;
     public static final int INVALID_OWNER_ID = -1;
     /**
      * Tuner resource type to help generate resource handle
@@ -275,7 +275,7 @@
      * Updates the current TRM of the TunerHAL Frontend information.
      *
      * <p><strong>Note:</strong> This update must happen before the first
-     * {@link #requestFrontend(TunerFrontendRequest, int[])} and
+     * {@link #requestFrontend(TunerFrontendRequest, long[])} and
      * {@link #releaseFrontend(int, int)} call.
      *
      * @param infos an array of the available {@link TunerFrontendInfo} information.
@@ -331,7 +331,7 @@
      *
      * @param lnbIds ids of the updating lnbs.
      */
-    public void setLnbInfoList(int[] lnbIds) {
+    public void setLnbInfoList(long[] lnbIds) {
         try {
             mService.setLnbInfoList(lnbIds);
         } catch (RemoteException e) {
@@ -406,8 +406,8 @@
      *
      * @return true if there is frontend granted.
      */
-    public boolean requestFrontend(@NonNull TunerFrontendRequest request,
-                @Nullable int[] frontendHandle) {
+    public boolean requestFrontend(
+            @NonNull TunerFrontendRequest request, @Nullable long[] frontendHandle) {
         boolean result = false;
         try {
             result = mService.requestFrontend(request, frontendHandle);
@@ -511,7 +511,7 @@
      *
      * @return true if there is Demux granted.
      */
-    public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) {
+    public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull long[] demuxHandle) {
         boolean result = false;
         try {
             result = mService.requestDemux(request, demuxHandle);
@@ -544,8 +544,8 @@
      *
      * @return true if there is Descrambler granted.
      */
-    public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
-                @NonNull int[] descramblerHandle) {
+    public boolean requestDescrambler(
+            @NonNull TunerDescramblerRequest request, @NonNull long[] descramblerHandle) {
         boolean result = false;
         try {
             result = mService.requestDescrambler(request, descramblerHandle);
@@ -577,8 +577,8 @@
      *
      * @return true if there is CAS session granted.
      */
-    public boolean requestCasSession(@NonNull CasSessionRequest request,
-                @NonNull int[] casSessionHandle) {
+    public boolean requestCasSession(
+            @NonNull CasSessionRequest request, @NonNull long[] casSessionHandle) {
         boolean result = false;
         try {
             result = mService.requestCasSession(request, casSessionHandle);
@@ -610,7 +610,7 @@
      *
      * @return true if there is ciCam granted.
      */
-    public boolean requestCiCam(TunerCiCamRequest request, int[] ciCamHandle) {
+    public boolean requestCiCam(TunerCiCamRequest request, long[] ciCamHandle) {
         boolean result = false;
         try {
             result = mService.requestCiCam(request, ciCamHandle);
@@ -635,7 +635,7 @@
      * <li>If no Lnb system can be granted, the API would return false.
      * <ul>
      *
-     * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request.
+     * <p><strong>Note:</strong> {@link #setLnbInfoList(long[])} must be called before this request.
      *
      * @param request {@link TunerLnbRequest} information of the current request.
      * @param lnbHandle a one-element array to return the granted Lnb handle.
@@ -643,7 +643,7 @@
      *
      * @return true if there is Lnb granted.
      */
-    public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
+    public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull long[] lnbHandle) {
         boolean result = false;
         try {
             result = mService.requestLnb(request, lnbHandle);
@@ -664,7 +664,7 @@
      * @param frontendHandle the handle of the released frontend.
      * @param clientId the id of the client that is releasing the frontend.
      */
-    public void releaseFrontend(int frontendHandle, int clientId) {
+    public void releaseFrontend(long frontendHandle, int clientId) {
         try {
             mService.releaseFrontend(frontendHandle, clientId);
         } catch (RemoteException e) {
@@ -680,7 +680,7 @@
      * @param demuxHandle the handle of the released Tuner Demux.
      * @param clientId the id of the client that is releasing the demux.
      */
-    public void releaseDemux(int demuxHandle, int clientId) {
+    public void releaseDemux(long demuxHandle, int clientId) {
         try {
             mService.releaseDemux(demuxHandle, clientId);
         } catch (RemoteException e) {
@@ -696,7 +696,7 @@
      * @param descramblerHandle the handle of the released Tuner Descrambler.
      * @param clientId the id of the client that is releasing the descrambler.
      */
-    public void releaseDescrambler(int descramblerHandle, int clientId) {
+    public void releaseDescrambler(long descramblerHandle, int clientId) {
         try {
             mService.releaseDescrambler(descramblerHandle, clientId);
         } catch (RemoteException e) {
@@ -715,7 +715,7 @@
      * @param casSessionHandle the handle of the released CAS session.
      * @param clientId the id of the client that is releasing the cas session.
      */
-    public void releaseCasSession(int casSessionHandle, int clientId) {
+    public void releaseCasSession(long casSessionHandle, int clientId) {
         try {
             mService.releaseCasSession(casSessionHandle, clientId);
         } catch (RemoteException e) {
@@ -734,7 +734,7 @@
      * @param ciCamHandle the handle of the releasing CiCam.
      * @param clientId the id of the client that is releasing the CiCam.
      */
-    public void releaseCiCam(int ciCamHandle, int clientId) {
+    public void releaseCiCam(long ciCamHandle, int clientId) {
         try {
             mService.releaseCiCam(ciCamHandle, clientId);
         } catch (RemoteException e) {
@@ -747,12 +747,12 @@
      *
      * <p>Client must call this whenever it releases an Lnb.
      *
-     * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release.
+     * <p><strong>Note:</strong> {@link #setLnbInfoList(long[])} must be called before this release.
      *
      * @param lnbHandle the handle of the released Tuner Lnb.
      * @param clientId the id of the client that is releasing the lnb.
      */
-    public void releaseLnb(int lnbHandle, int clientId) {
+    public void releaseLnb(long lnbHandle, int clientId) {
         try {
             mService.releaseLnb(lnbHandle, clientId);
         } catch (RemoteException e) {
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 5399697..109c791 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -149,7 +149,7 @@
      *
      * @param lnbIds ids of the updating lnbs.
      */
-    void setLnbInfoList(in int[] lnbIds);
+    void setLnbInfoList(in long[] lnbIds);
 
     /*
      * This API is used by the Tuner framework to request a frontend from the TunerHAL.
@@ -185,7 +185,7 @@
      *
      * @return true if there is frontend granted.
      */
-    boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendHandle);
+    boolean requestFrontend(in TunerFrontendRequest request, out long[] frontendHandle);
 
     /*
      * Sets the maximum usable frontends number of a given frontend type. It is used to enable or
@@ -253,7 +253,7 @@
      *
      * @return true if there is demux granted.
      */
-    boolean requestDemux(in TunerDemuxRequest request, out int[] demuxHandle);
+    boolean requestDemux(in TunerDemuxRequest request, out long[] demuxHandle);
 
     /*
      * This API is used by the Tuner framework to request an available descrambler from the
@@ -277,7 +277,7 @@
      *
      * @return true if there is Descrambler granted.
      */
-    boolean requestDescrambler(in TunerDescramblerRequest request, out int[] descramblerHandle);
+    boolean requestDescrambler(in TunerDescramblerRequest request, out long[] descramblerHandle);
 
     /*
      * This API is used by the Tuner framework to request an available Cas session. This session
@@ -303,7 +303,7 @@
      *
      * @return true if there is CAS session granted.
      */
-    boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle);
+    boolean requestCasSession(in CasSessionRequest request, out long[] casSessionHandle);
 
     /*
      * This API is used by the Tuner framework to request an available CuCam.
@@ -328,7 +328,7 @@
      *
      * @return true if there is CiCam granted.
      */
-    boolean requestCiCam(in TunerCiCamRequest request, out int[] ciCamHandle);
+    boolean requestCiCam(in TunerCiCamRequest request, out long[] ciCamHandle);
 
     /*
      * This API is used by the Tuner framework to request an available Lnb from the TunerHAL.
@@ -352,7 +352,7 @@
      *
      * @return true if there is Lnb granted.
      */
-    boolean requestLnb(in TunerLnbRequest request, out int[] lnbHandle);
+    boolean requestLnb(in TunerLnbRequest request, out long[] lnbHandle);
 
     /*
      * Notifies the TRM that the given frontend has been released.
@@ -365,7 +365,7 @@
      * @param frontendHandle the handle of the released frontend.
      * @param clientId the id of the client that is releasing the frontend.
      */
-    void releaseFrontend(in int frontendHandle, int clientId);
+    void releaseFrontend(in long frontendHandle, int clientId);
 
     /*
      * Notifies the TRM that the Demux with the given handle was released.
@@ -375,7 +375,7 @@
      * @param demuxHandle the handle of the released Tuner Demux.
      * @param clientId the id of the client that is releasing the demux.
      */
-    void releaseDemux(in int demuxHandle, int clientId);
+    void releaseDemux(in long demuxHandle, int clientId);
 
     /*
      * Notifies the TRM that the Descrambler with the given handle was released.
@@ -385,7 +385,7 @@
      * @param descramblerHandle the handle of the released Tuner Descrambler.
      * @param clientId the id of the client that is releasing the descrambler.
      */
-    void releaseDescrambler(in int descramblerHandle, int clientId);
+    void releaseDescrambler(in long descramblerHandle, int clientId);
 
     /*
      * Notifies the TRM that the given Cas session has been released.
@@ -397,7 +397,7 @@
      * @param casSessionHandle the handle of the released CAS session.
      * @param clientId the id of the client that is releasing the cas session.
      */
-    void releaseCasSession(in int casSessionHandle, int clientId);
+    void releaseCasSession(in long casSessionHandle, int clientId);
 
     /**
      * Notifies the TRM that the given CiCam has been released.
@@ -410,7 +410,7 @@
      * @param ciCamHandle the handle of the releasing CiCam.
      * @param clientId the id of the client that is releasing the CiCam.
      */
-    void releaseCiCam(in int ciCamHandle, int clientId);
+    void releaseCiCam(in long ciCamHandle, int clientId);
 
     /*
      * Notifies the TRM that the Lnb with the given handle was released.
@@ -422,7 +422,7 @@
      * @param lnbHandle the handle of the released Tuner Lnb.
      * @param clientId the id of the client that is releasing the lnb.
      */
-    void releaseLnb(in int lnbHandle, int clientId);
+    void releaseLnb(in long lnbHandle, int clientId);
 
     /*
      * Compare two clients' priority.
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
index c14caf5..7984c38 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
@@ -26,7 +26,7 @@
     /**
      * Demux handle
      */
-    int handle;
+    long handle;
 
     /**
      * Supported filter types (defined in {@link android.media.tv.tuner.filter.Filter})
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
index 8981ce0..274367e 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
@@ -26,7 +26,7 @@
     /**
      * Frontend Handle
      */
-    int handle;
+    long handle;
 
     /**
      * Frontend Type
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 49e7941..9e1e2c3 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1452,7 +1452,7 @@
     return obj;
 }
 
-jobject JTuner::openFrontendByHandle(int feHandle) {
+jobject JTuner::openFrontendByHandle(jlong feHandle) {
     // TODO: Handle reopening frontend with different handle
     sp<FrontendClient> feClient = sTunerClient->openFrontend(feHandle);
     if (feClient == nullptr) {
@@ -1828,7 +1828,7 @@
     return valObj;
 }
 
-jobject JTuner::openLnbByHandle(int handle) {
+jobject JTuner::openLnbByHandle(jlong handle) {
     if (sTunerClient == nullptr) {
         return nullptr;
     }
@@ -1837,7 +1837,7 @@
     sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
     lnbClient = sTunerClient->openLnb(handle);
     if (lnbClient == nullptr) {
-        ALOGD("Failed to open lnb, handle = %d", handle);
+        ALOGD("Failed to open lnb, handle = %s", std::to_string(handle).c_str());
         return nullptr;
     }
 
@@ -1951,7 +1951,7 @@
     return (int)result;
 }
 
-Result JTuner::openDemux(int handle) {
+Result JTuner::openDemux(jlong handle) {
     if (sTunerClient == nullptr) {
         return Result::NOT_INITIALIZED;
     }
@@ -2219,7 +2219,7 @@
             numBytesInSectionFilter, filterCaps, filterCapsList, linkCaps, bTimeFilter);
 }
 
-jobject JTuner::getDemuxInfo(int handle) {
+jobject JTuner::getDemuxInfo(jlong handle) {
     if (sTunerClient == nullptr) {
         ALOGE("tuner is not initialized");
         return nullptr;
@@ -3772,8 +3772,8 @@
     return tuner->getFrontendIds();
 }
 
-static jobject android_media_tv_Tuner_open_frontend_by_handle(
-        JNIEnv *env, jobject thiz, jint handle) {
+static jobject android_media_tv_Tuner_open_frontend_by_handle(JNIEnv *env, jobject thiz,
+                                                              jlong handle) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->openFrontendByHandle(handle);
 }
@@ -3905,7 +3905,7 @@
     return tuner->getFrontendInfo(id);
 }
 
-static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) {
+static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jlong handle) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->openLnbByHandle(handle);
 }
@@ -4626,7 +4626,7 @@
     return (int)r;
 }
 
-static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jint) {
+static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jlong) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->openDescrambler();
 }
@@ -4694,12 +4694,12 @@
     return tuner->getDemuxCaps();
 }
 
-static jobject android_media_tv_Tuner_get_demux_info(JNIEnv* env, jobject thiz, jint handle) {
+static jobject android_media_tv_Tuner_get_demux_info(JNIEnv *env, jobject thiz, jlong handle) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->getDemuxInfo(handle);
 }
 
-static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) {
+static jint android_media_tv_Tuner_open_demux(JNIEnv *env, jobject thiz, jlong handle) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return (jint)tuner->openDemux(handle);
 }
@@ -4710,7 +4710,7 @@
     return (jint)tuner->close();
 }
 
-static jint android_media_tv_Tuner_close_demux(JNIEnv* env, jobject thiz, jint /* handle */) {
+static jint android_media_tv_Tuner_close_demux(JNIEnv *env, jobject thiz, jlong /* handle */) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->closeDemux();
 }
@@ -4770,7 +4770,7 @@
     return tuner->getFrontendStatusReadiness(types);
 }
 
-static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
+static jint android_media_tv_Tuner_close_frontend(JNIEnv *env, jobject thiz, jlong /* handle */) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->closeFrontend();
 }
@@ -5039,7 +5039,7 @@
     { "nativeGetTunerVersion", "()I", (void *)android_media_tv_Tuner_native_get_tuner_version },
     { "nativeGetFrontendIds", "()Ljava/util/List;",
             (void *)android_media_tv_Tuner_get_frontend_ids },
-    { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
+    { "nativeOpenFrontendByHandle", "(J)Landroid/media/tv/tuner/Tuner$Frontend;",
             (void *)android_media_tv_Tuner_open_frontend_by_handle },
     { "nativeShareFrontend", "(I)I",
             (void *)android_media_tv_Tuner_share_frontend },
@@ -5078,11 +5078,11 @@
             (void *)android_media_tv_Tuner_open_filter },
     { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;",
             (void *)android_media_tv_Tuner_open_time_filter },
-    { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;",
+    { "nativeOpenLnbByHandle", "(J)Landroid/media/tv/tuner/Lnb;",
             (void *)android_media_tv_Tuner_open_lnb_by_handle },
     { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
             (void *)android_media_tv_Tuner_open_lnb_by_name },
-    { "nativeOpenDescramblerByHandle", "(I)Landroid/media/tv/tuner/Descrambler;",
+    { "nativeOpenDescramblerByHandle", "(J)Landroid/media/tv/tuner/Descrambler;",
             (void *)android_media_tv_Tuner_open_descrambler },
     { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;",
             (void *)android_media_tv_Tuner_open_dvr_recorder },
@@ -5090,12 +5090,12 @@
             (void *)android_media_tv_Tuner_open_dvr_playback },
     { "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
             (void *)android_media_tv_Tuner_get_demux_caps },
-    { "nativeGetDemuxInfo", "(I)Landroid/media/tv/tuner/DemuxInfo;",
+    { "nativeGetDemuxInfo", "(J)Landroid/media/tv/tuner/DemuxInfo;",
             (void *)android_media_tv_Tuner_get_demux_info },
-    { "nativeOpenDemuxByhandle", "(I)I", (void *)android_media_tv_Tuner_open_demux },
+    { "nativeOpenDemuxByhandle", "(J)I", (void *)android_media_tv_Tuner_open_demux },
     { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
-    { "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend },
-    { "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux },
+    { "nativeCloseFrontend", "(J)I", (void *)android_media_tv_Tuner_close_frontend },
+    { "nativeCloseDemux", "(J)I", (void *)android_media_tv_Tuner_close_demux },
     { "nativeOpenSharedFilter",
             "(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;",
             (void *)android_media_tv_Tuner_open_shared_filter},
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 3de3ab9..7af2cd7 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -206,7 +206,7 @@
     int disconnectCiCam();
     int unlinkCiCam(jint id);
     jobject getFrontendIds();
-    jobject openFrontendByHandle(int feHandle);
+    jobject openFrontendByHandle(jlong feHandle);
     int shareFrontend(int feId);
     int unshareFrontend();
     void registerFeCbListener(JTuner* jtuner);
@@ -221,16 +221,16 @@
     int setLnb(sp<LnbClient> lnbClient);
     bool isLnaSupported();
     int setLna(bool enable);
-    jobject openLnbByHandle(int handle);
+    jobject openLnbByHandle(jlong handle);
     jobject openLnbByName(jstring name);
     jobject openFilter(DemuxFilterType type, int bufferSize);
     jobject openTimeFilter();
     jobject openDescrambler();
     jobject openDvr(DvrType type, jlong bufferSize);
     jobject getDemuxCaps();
-    jobject getDemuxInfo(int handle);
+    jobject getDemuxInfo(jlong handle);
     jobject getFrontendStatus(jintArray types);
-    Result openDemux(int handle);
+    Result openDemux(jlong handle);
     jint close();
     jint closeFrontend();
     jint closeDemux();
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index ea623d9..0097710 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -56,7 +56,7 @@
     return ids;
 }
 
-sp<FrontendClient> TunerClient::openFrontend(int32_t frontendHandle) {
+sp<FrontendClient> TunerClient::openFrontend(int64_t frontendHandle) {
     if (mTunerService != nullptr) {
         shared_ptr<ITunerFrontend> tunerFrontend;
         Status s = mTunerService->openFrontend(frontendHandle, &tunerFrontend);
@@ -94,7 +94,7 @@
     return nullptr;
 }
 
-sp<DemuxClient> TunerClient::openDemux(int32_t demuxHandle) {
+sp<DemuxClient> TunerClient::openDemux(int64_t demuxHandle) {
     if (mTunerService != nullptr) {
         shared_ptr<ITunerDemux> tunerDemux;
         Status s = mTunerService->openDemux(demuxHandle, &tunerDemux);
@@ -107,7 +107,7 @@
     return nullptr;
 }
 
-shared_ptr<DemuxInfo> TunerClient::getDemuxInfo(int32_t demuxHandle) {
+shared_ptr<DemuxInfo> TunerClient::getDemuxInfo(int64_t demuxHandle) {
     if (mTunerService != nullptr) {
         DemuxInfo aidlDemuxInfo;
         Status s = mTunerService->getDemuxInfo(demuxHandle, &aidlDemuxInfo);
@@ -141,7 +141,7 @@
     return nullptr;
 }
 
-sp<DescramblerClient> TunerClient::openDescrambler(int32_t descramblerHandle) {
+sp<DescramblerClient> TunerClient::openDescrambler(int64_t descramblerHandle) {
     if (mTunerService != nullptr) {
         shared_ptr<ITunerDescrambler> tunerDescrambler;
         Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler);
@@ -154,7 +154,7 @@
     return nullptr;
 }
 
-sp<LnbClient> TunerClient::openLnb(int32_t lnbHandle) {
+sp<LnbClient> TunerClient::openLnb(int64_t lnbHandle) {
     if (mTunerService != nullptr) {
         shared_ptr<ITunerLnb> tunerLnb;
         Status s = mTunerService->openLnb(lnbHandle, &tunerLnb);
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 6ab120b..a348586 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -65,7 +65,7 @@
      * @param frontendHandle the handle of the frontend granted by TRM.
      * @return a newly created FrontendClient interface.
      */
-    sp<FrontendClient> openFrontend(int32_t frontendHandle);
+    sp<FrontendClient> openFrontend(int64_t frontendHandle);
 
     /**
      * Retrieve the granted frontend's information.
@@ -81,7 +81,7 @@
      * @param demuxHandle the handle of the demux granted by TRM.
      * @return a newly created DemuxClient interface.
      */
-    sp<DemuxClient> openDemux(int32_t demuxHandle);
+    sp<DemuxClient> openDemux(int64_t demuxHandle);
 
     /**
      * Retrieve the DemuxInfo of a specific demux
@@ -89,7 +89,7 @@
      * @param demuxHandle the handle of the demux to query demux info for
      * @return the demux info
      */
-    shared_ptr<DemuxInfo> getDemuxInfo(int32_t demuxHandle);
+    shared_ptr<DemuxInfo> getDemuxInfo(int64_t demuxHandle);
 
     /**
      * Retrieve a list of demux info
@@ -111,7 +111,7 @@
      * @param descramblerHandle the handle of the descrambler granted by TRM.
      * @return a newly created DescramblerClient interface.
      */
-    sp<DescramblerClient> openDescrambler(int32_t descramblerHandle);
+    sp<DescramblerClient> openDescrambler(int64_t descramblerHandle);
 
     /**
      * Open a new interface of LnbClient given an lnbHandle.
@@ -119,7 +119,7 @@
      * @param lnbHandle the handle of the LNB granted by TRM.
      * @return a newly created LnbClient interface.
      */
-    sp<LnbClient> openLnb(int32_t lnbHandle);
+    sp<LnbClient> openLnb(int64_t lnbHandle);
 
     /**
      * Open a new interface of LnbClient given a LNB name.
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 4428ade..24e14e6 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -63,7 +63,7 @@
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAutoChangeEnabled();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagPresent();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
-    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overwriteRoutingTable(int, int, int);
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overwriteRoutingTable(int, int, int, int);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void pausePolling(int);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void resumePolling();
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 1eae3c6..8535e4a 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -54,5 +54,5 @@
     void setAutoChangeStatus(boolean state);
     boolean isAutoChangeEnabled();
     List<String> getRoutingStatus();
-    void overwriteRoutingTable(int userHandle, String emptyAid, String protocol, String tech);
+    void overwriteRoutingTable(int userHandle, String emptyAid, String protocol, String tech, String sc);
 }
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 951702c..d9fd42f 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1150,8 +1150,9 @@
     }
 
     /**
-     * Pauses polling for a {@code timeoutInMs} millis. If polling must be resumed before timeout,
-     * use {@link #resumePolling()}.
+     * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond.
+     * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely
+     * use {@link #resumePolling() to resume the polling.
      * @hide
      */
     public void pausePolling(int timeoutInMs) {
@@ -1210,9 +1211,8 @@
     }
 
     /**
-     * Resumes default polling for the current device state if polling is paused. Calling
-     * this while polling is not paused is a no-op.
-     *
+     * Resumes default NFC tag reader mode polling for the current device state if polling is
+     * paused. Calling this while already in polling is a no-op.
      * @hide
      */
     public void resumePolling() {
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index fb63b5c..905d6f6 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -569,8 +569,9 @@
     }
 
     /**
-     * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. If polling must be
-     * resumed before timeout, use {@link #resumePolling()}.
+     * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond.
+     * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely
+     * use {@link #resumePolling() to resume the polling.
      * @param timeoutInMs the pause polling duration in millisecond
      */
     @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
@@ -581,7 +582,7 @@
 
     /**
      * Resumes default NFC tag reader mode polling for the current device state if polling is
-     * paused. Calling this while polling is not paused is a no-op.
+     * paused. Calling this while already in polling is a no-op.
      */
     @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -647,24 +648,29 @@
      *                   {@link ProtocolAndTechnologyRoute}
      * @param emptyAid Zero-length AID route destination, where the possible inputs are defined in
      *                 {@link ProtocolAndTechnologyRoute}
+     * @param systemCode System Code route destination, where the possible inputs are defined in
+     *                   {@link ProtocolAndTechnologyRoute}
      */
     @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
     public void overwriteRoutingTable(
             @CardEmulation.ProtocolAndTechnologyRoute int protocol,
             @CardEmulation.ProtocolAndTechnologyRoute int technology,
-            @CardEmulation.ProtocolAndTechnologyRoute int emptyAid) {
+            @CardEmulation.ProtocolAndTechnologyRoute int emptyAid,
+            @CardEmulation.ProtocolAndTechnologyRoute int systemCode) {
 
         String protocolRoute = routeIntToString(protocol);
         String technologyRoute = routeIntToString(technology);
         String emptyAidRoute = routeIntToString(emptyAid);
+        String systemCodeRoute = routeIntToString(systemCode);
 
         NfcAdapter.callService(() ->
                 NfcAdapter.sCardEmulationService.overwriteRoutingTable(
                         mContext.getUser().getIdentifier(),
                         emptyAidRoute,
                         protocolRoute,
-                        technologyRoute
+                        technologyRoute,
+                        systemCodeRoute
                 ));
     }
 
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 2983875..d75318f 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -324,7 +324,7 @@
                 mOffHostName = sa.getString(
                         com.android.internal.R.styleable.OffHostApduService_secureElementName);
                 mShouldDefaultToObserveMode = sa.getBoolean(
-                        R.styleable.HostApduService_shouldDefaultToObserveMode,
+                        R.styleable.OffHostApduService_shouldDefaultToObserveMode,
                         false);
                 if (mOffHostName != null) {
                     if (mOffHostName.equals("eSE")) {
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index 08155dd..5805332 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -31,6 +31,12 @@
 
             <!-- A header for selfManaged devices only. -->
             <include layout="@layout/vendor_header" />
+            <!-- A device icon for selfManaged devices only. -->
+            <ImageView
+                android:id="@+id/device_icon"
+                android:visibility="gone"
+                android:contentDescription="@null"
+                style="@style/DeviceIcon" />
 
             <!-- Do NOT change the ID of the root LinearLayout above:
             it's referenced in CTS tests. -->
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index 6a241b7..2a8f17b 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"toestel"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Hierdie app sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> hê"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Gee &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestemming om jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> se apps en stelselkenmerke na &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; te stroom?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sal toegang hê tot enigiets wat sigbaar is of gespeel word op jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, insluitend oudio, foto’s, betaalinligting, wagwoorde en boodskappe.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> sal apps na <xliff:g id="DEVICE_NAME">%3$s</xliff:g> kan stroom totdat jy toegang tot hierdie toestemming verwyder."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en stelselkenmerke vanaf jou <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> te stroom"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Gee &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang tot hierdie inligting op jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toegang tot jou <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> se foto’s, media en kennisgewings"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Gee &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestemming om jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> se apps na &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&amp;gt te stroom?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sal toegang hê tot enigiets wat sigbaar is of gespeel word op <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, insluitend oudio, foto’s, wagwoorde en boodskappe.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> sal apps na <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> kan stroom totdat jy toegang tot hierdie toestemming verwyder."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps vanaf jou <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> te stroom"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Hierdie app sal inligting kan sinkroniseer, soos die naam van iemand wat bel, tussen jou foon en die gekose toestel"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index a9f5ed2..b66860e 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ን እንዲያስተዳድር ይፈቅዳሉ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"መሣሪያ"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"ይህ መተግበሪያ በእርስዎ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ላይ እነዚህን ፈቃዶች እንዲደርስ ይፈቀድለታል"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> መተግበሪያዎች እና የሥርዓት ባህሪያት ወደ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;? በዥረት እንዲለቅ ይፍቀዱ"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ኦዲዮ፣ ፎቶዎች፣ የክፍያ መረጃ፣ የይለፍ ቃላት እና መልዕክቶችን ጨምሮ በእርስዎ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ላይ የሚታየውን ወይም የሚጫወተውን የማንኛውም ነገር መዳረሻ ይኖረዋል።&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> የዚህን መዳረሻ እስኪያስወግዱ ድረስ መተግበሪያዎችን ወደ <xliff:g id="DEVICE_NAME">%3$s</xliff:g> በዥረት መልቀቅ ይችላል።"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ን በመወከል ከእርስዎ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>የመጡ መተግበሪያዎችን እና የሥርዓት ባህሪያትን በዥረት ለመልቀቅ ፈቃድ እየጠየቀ ነው"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ይህን መረጃ ከእርስዎ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> እንዲደርስ ይፍቀዱ"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ፎቶዎች፣ ሚዲያ እና ማሳወቂያዎች ለመድረስ የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> መተግበሪያዎች ወደ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;? በዥረት እንዲለቅ ይፍቀዱ"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ኦዲዮ፣ ፎቶዎች፣ የክፍያ መረጃ፣ የይለፍ ቃላት እና መልዕክቶችን ጨምሮ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ላይ የሚታየውን ወይም የሚጫወተውን የማንኛውም ነገር መዳረሻ ይኖረዋል።&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> የዚህን መዳረሻ እስኪያስወግዱ ድረስ መተግበሪያዎችን ወደ <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> በዥረት መልቀቅ ይችላል።"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> መተግበሪያዎችን ከእርስዎ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> በዥረት ለመልቀቅ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ን በመወከል ፈቃድ እየጠየቀ ነው"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
     <string name="summary_generic" msgid="1761976003668044801">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን በስልክዎ እና በተመረጠው መሣሪያ መካከል ማስመር ይችላል"</string>
     <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index ce68ee1..6f17676 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"‏هل تريد السماح لتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;؟"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"جهاز"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"سيتم السماح لهذا التطبيق باستخدام هذه الأذونات على <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"هل تريد السماح لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ببث التطبيقات وميزات النظام من <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> إلى <xliff:g id="DEVICE_NAME">%3$s</xliff:g>؟"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"‏سيتمكّن \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>، بما في ذلك الملفات الصوتية والصور ومعلومات الدفع وكلمات المرور والرسائل.&lt;br/&gt;&lt;br/&gt;سيتمكّن \"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" من بثّ التطبيقات إلى <xliff:g id="DEVICE_NAME">%3$s</xliff:g> إلى أن توقِف إمكانية استخدام هذا الإذن."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_NAME">%2$s</xliff:g> لبثّ التطبيقات وميزات النظام من <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"‏هل تريد السماح لتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بالوصول إلى هذه المعلومات من <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>؟"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_NAME">%2$s</xliff:g> للوصول إلى الصور والوسائط والإشعارات في <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"هل تريد السماح لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ببث التطبيقات من <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> إلى <xliff:g id="DEVICE_NAME">%3$s</xliff:g>؟"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"‏سيتمكّن \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>، بما في ذلك الملفات الصوتية والصور ومعلومات الدفع وكلمات المرور والرسائل.&lt;br/&gt;&lt;br/&gt;سيتمكّن \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" من بثّ التطبيقات إلى <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> إلى أن توقِف إمكانية استخدام هذا الإذن."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_NAME">%2$s</xliff:g> لبثّ التطبيقات من <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
     <string name="summary_generic" msgid="1761976003668044801">"سيتمكّن هذا التطبيق من مزامنة المعلومات، مثل اسم المتصل، بين هاتفك والجهاز المحدّد."</string>
     <string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 7376cd06..8b001d1 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; পৰিচালনা কৰিবলৈ দিবনে?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ডিভাইচ"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"এই এপ্‌টোক আপোনাৰ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ এপ্‌ আৰু ছিষ্টেমৰ সুবিধাসমূহ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ত ষ্ট্ৰীম কৰিবলৈ দিবনে?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ত দৃশ্যমান বা প্লে’ কৰা অডিঅ’, ফট’ পৰিশোধৰ তথ্য, পাছৱৰ্ড আৰু বাৰ্তাকে ধৰি যিকোনো বস্তু এক্সেছ কৰিব পাৰিব।&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g>এ আপুনি এই অনুমতিৰ এক্সেছ আঁতৰাই নিদিয়া পৰ্যন্ত <xliff:g id="DEVICE_NAME">%3$s</xliff:g>ত এপ্‌সমূহ ষ্ট্ৰীম কৰিব পাৰিব।"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ৰ পৰা এপ্‌ আৰু ছিষ্টেমৰ সুবিধাসমূহ ষ্ট্ৰীম কৰিবলৈ অনুমতি বিচাৰি অনুৰোধ জনাইছে"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ পৰা এই তথ্যখিনি এক্সেছ কৰিবলৈ দিয়ক"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ৰ ফট’, মিডিয়া আৰু জাননী এক্সেছ কৰাৰ বাবে অনুমতি বিচাৰি অনুৰোধ জনাইছে"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ এপ্‌সমূহ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ত ষ্ট্ৰীম কৰিবলৈ দিবনে?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>এ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>ত দৃশ্যমান হোৱা বা প্লে’ কৰা অডিঅ’, ফট’ পাছৱৰ্ড আৰু বাৰ্তাকে ধৰি যিকোনো বস্তু এক্সেছ কৰিব পাৰিব।&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g>এ আপুনি এই অনুমতিৰ এক্সেছ আঁতৰাই নিদিয়া পৰ্যন্ত <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>ত এপ্‌সমূহ ষ্ট্ৰীম কৰিব পাৰিব।"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ৰ পৰা এপ্‌সমূহ ষ্ট্ৰীম কৰিবলৈ অনুমতি বিচাৰি অনুৰোধ জনাইছে"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
     <string name="summary_generic" msgid="1761976003668044801">"এই এপ্‌টোৱে আপোনাৰ ফ’ন আৰু বাছনি কৰা ডিভাইচটোৰ মাজত কল কৰোঁতাৰ নামৰ দৰে তথ্য ছিংক কৰিব পাৰিব"</string>
     <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index dd72093..b4fdcec 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazını idarə etmək icazəsi verilsin?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"cihazda"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Bu tətbiq <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazında bu icazələrə daxil ola biləcək"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazının tətbiq və sistem funksiyalarını &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazında yayımlamaq üçün &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə icazə verilsin?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> audio, foto, ödəniş məlumatı, parol və mesajlar daxil olmaqla <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazında görünən və ya oxudulan kontentə giriş əldə edəcək.&lt;br/&gt;&lt;br/&gt;Siz bu icazəyə girişi silənə qədər <xliff:g id="APP_NAME_1">%1$s</xliff:g> tətbiqləri <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cihazında yayımlaya biləcək."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazından tətbiqləri və sistem fuksiyalarını yayımlamaq üçün <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından icazə tələb edir"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazından əldə edilən bu məlumata giriş icazəsi verin"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> üzrə foto, media və bildirişlərə daxil olmaq üçün <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından icazə tələb edir"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazının tətbiqlərini &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazına yayımlamaq üçün &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə icazə verilsin?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> audio, foto, ödəniş məlumatı, parol və mesajlar daxil olmaqla <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> cihazında görünən və ya oxudulan kontentə giriş əldə edəcək.&lt;br/&gt;&lt;br/&gt;Siz bu icazəyə girişi silənə qədər <xliff:g id="APP_NAME_2">%1$s</xliff:g> tətbiqləri <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> cihazında yayımlaya biləcək."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazından tətbiqləri yayımlamaq üçün <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından icazə tələb edir"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Tətbiq zəng edənin adı kimi məlumatları telefon ilə seçilmiş cihaz arasında sinxronlaşdıracaq"</string>
     <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index 2200cec..ef97da9 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Želite li da dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Ovoj aplikaciji će biti dozvoljeno da pristupa ovim dozvolama na uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Želite da dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; strimuje aplikacije i sistemske funkcije uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što se vidi ili pušta na uređaju <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, uključujući zvuk, slike, informacije o plaćanju, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> će moći da strimuje aplikacije na <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup ovoj dozvoli."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> traži dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da strimuje aplikacije i sistemske funkcije sa uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama sa uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> traži dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da pristupa slikama, medijskom sadržaju i obaveštenjima sa uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Želite da dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; strimuje aplikacije uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što se vidi ili pušta na uređaju <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, uključujući zvuk, slike, informacije o plaćanju, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> će moći da strimuje aplikacije na <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> dok ne uklonite pristup ovoj dozvoli."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> traži dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da strimuje aplikacije sa uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ova aplikacija će moći da sinhronizuje podatke, poput imena osobe koja upućuje poziv, između telefona i odabranog uređaja"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 7eca403..c0aaac9 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Дазволіць праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"прылада"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Гэта праграма будзе мець на вашай прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>\" наступныя дазволы"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Дазволіць прыладзе &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; трансліраваць праграмы і сістэмныя функцыі прылады тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на прыладу &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будзе мець доступ да ўсяго, што паказваецца ці прайграецца на прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\", у тым ліку да аўдыя, фота, плацежнай інфармацыі, пароляў і паведамленняў.&lt;br/&gt;&lt;br/&gt;Праграма \"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" зможа трансліраваць праграмы на прыладу \"<xliff:g id="DEVICE_NAME">%3$s</xliff:g>\", пакуль вы не адклічаце гэты дазвол."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на трансляцыю праграмы і сістэмных функцый з прылады тыпу \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; мець доступ да гэтай інфармацыі з прылады тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\""</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на доступ да фота, медыяфайлаў і апавяшчэнняў на прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Дазволіць праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; трансліраваць праграмы прылады тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на прыладу &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будзе мець доступ да ўсяго, што паказваецца ці прайграецца на прыладзе \"<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>\", у тым ліку да аўдыя, фота, плацежнай інфармацыі, пароляў і паведамленняў.&lt;br/&gt;&lt;br/&gt;Праграма \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" зможа трансліраваць праграмы на прыладу \"<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>\", пакуль вы не адклічаце гэты дазвол."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на трансляцыю праграм з прылады тыпу \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Гэта праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) паміж тэлефонам і выбранай прыладай"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 3ebf375..0fa98ef 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Разрешавате ли на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"устройство"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Това приложение ще има достъп до следните разрешения за вашите <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Да се разреши ли на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да предава поточно към &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; приложенията и системните функции на устройството ви от тип <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ще има достъп до всичко, което се показва или възпроизвежда на устройството ви от тип <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, включително аудио, снимки, данни за плащане, пароли и съобщения.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> ще може да предава поточно приложения към устройството ви <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, докато не премахнете това разрешение."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да предава поточно приложения и системни функции от устройството ви от тип <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Разрешете на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да осъществява достъп до тази информация от вашия <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на ваше устройство (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) за достъп до снимките, мултимедията и известията на вашия <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Да се разреши ли на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да предава поточно към &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; приложенията на устройството ви от тип <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ще има достъп до всичко, което се показва или възпроизвежда на устройството ви <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, включително аудио, снимки, пароли и съобщения.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> ще може да предава поточно приложения към устройството ви <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, докато не премахнете това разрешение."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да предава поточно приложения от устройството ви от тип <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Това приложение ще може да синхронизира различна информация, като например името на обаждащия се, между телефона ви и избраното устройство"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index d2a0353..032eedb 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"আপনি কি &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-কে অনুমতি দেবেন?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ডিভাইস"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করার জন্য এই অ্যাপকে অনুমতি দেওয়া হবে"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর অ্যাপ ও সিস্টেমের ফিচার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;-এ স্ট্রিম করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-কে অনুমতি দেবেন?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"অডিও, ফটো, পেমেন্টের তথ্য, পাসওয়ার্ড ও মেসেজ সহ আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এ দেখা ও চালানো যায় এমন সব কিছু <xliff:g id="APP_NAME_0">%1$s</xliff:g> অ্যাক্সেস করতে পারবে।&lt;br/&gt;&lt;br/&gt;আপনি এই অনুমতি না সরানো পর্যন্ত <xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%3$s</xliff:g>-এ অ্যাপের ফিচার স্ট্রিম করতে পারবে।"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"আপনার <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> থেকে অ্যাপ ও সিস্টেমের ফিচার স্ট্রিম করার জন্য <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চাইছে"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> থেকে এই তথ্য অ্যাক্সেস করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-কে অনুমতি দিন"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"আপনার <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-এর ফটো, মিডিয়া ও বিজ্ঞপ্তি অ্যাক্সেস করার জন্য, আপনার <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চাইছে"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর অ্যাপ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?-এ স্ট্রিম করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-কে অনুমতি দেবেন?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"অডিও, ফটো, পাসওয়ার্ড ও মেসেজ সহ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>-এ দেখা ও চালানো যায় এমন সব কিছু <xliff:g id="APP_NAME_0">%1$s</xliff:g> অ্যাক্সেস করতে পারবে।&lt;br/&gt;&lt;br/&gt;আপনি এই অনুমতি না সরানো পর্যন্ত <xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>-এ অ্যাপ স্ট্রিম করতে পারবে।"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"আপনার <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> থেকে অ্যাপ স্ট্রিম করার জন্য <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চাইছে"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
     <string name="summary_generic" msgid="1761976003668044801">"এই অ্যাপ, আপনার ফোন এবং বেছে নেওয়া ডিভাইসের মধ্যে তথ্য সিঙ্ক করতে পারবে, যেমন কোনও কলারের নাম"</string>
     <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 84316f2..183bdc8 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Dozvoliti aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Aplikaciji će biti dozvoljen pristup ovim odobrenjima koje sadržava vaš <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Dozvoliti aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da prenosi aplikacije i funkcije sistema koje sadržava vaš <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> reproducira ili je vidljivo na njemu, uključujući zvukove, fotografije, podatke o plaćanju, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> će moći prenositi aplikacije na uređaju <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup ovom odobrenju."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> traži odobrenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da prenosi aplikacije i funkcije sistema s uređaja vrste \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa ovim informacijama koje sadržava vaš <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> traži odobrenje u ime vašeg uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da pristupa fotografijama, medijima i obavještenjima koje sadržava vaš <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Dozvoliti aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da prenosi aplikacije koje sadržava vaš <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> reproducira ili je vidljivo na njemu, uključujući zvukove, fotografije, podatke o plaćanju, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> će moći prenositi aplikacije na uređaju <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> dok ne uklonite pristup ovom odobrenju."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> traži odobrenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da prenosi aplikacije s uređaja vrste \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ova aplikacija će moći sinhronizirati informacije, kao što je ime osobe koja upućuje poziv, između vašeg telefona i odabranog uređaja"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index 8b115f7..0dc7001 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositiu"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Aquesta aplicació podrà accedir a aquests permisos del <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Vols permetre que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; reprodueixi en continu les aplicacions i les funcions del sistema del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al teu dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), inclosos àudios, fotos, informació de pagament, contrasenyes i missatges.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> podrà reproduir en continu aplicacions al dispositiu <xliff:g id="DEVICE_NAME">%3$s</xliff:g> fins que suprimeixis l\'accés a aquest permís."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per reproduir en continu aplicacions i funcions del sistema del teu dispositiu (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; accedeixi a aquesta informació del <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per accedir a les fotos, el contingut multimèdia i les notificacions del <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vols permetre que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; reprodueixi en continu les aplicacions del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al dispositiu <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inclosos àudios, fotos, informació de pagament, contrasenyes i missatges.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> podrà reproduir en continu aplicacions al dispositiu <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> fins que suprimeixis l\'accés a aquest permís."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per reproduir en continu aplicacions del teu dispositiu (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, entre el teu telèfon i el dispositiu triat"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 7bd6e38..b08081b 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovat zařízení &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"zařízení"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Tato aplikace bude mít na zařízení typu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> přístup k následujícím oprávněním"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovat aplikace a systémové funkce ze zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zařízení &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mít přístup ke všemu, co na zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zobrazíte nebo přehrajete, včetně zvuku, fotek, platebních údajů, hesel a zpráv.&lt;br/&gt;&lt;br/&gt;Aplikace <xliff:g id="APP_NAME_1">%1$s</xliff:g> bude moct streamovat aplikace do zařízení typu <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, dokud přístup k tomuto oprávnění neodeberete."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádá jménem zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o oprávnění streamovat aplikace a systémové funkce z vašeho zařízení typu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Povolte aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; přístup k těmto informacím z vašeho zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> oprávnění k přístupu k fotkám, médiím a oznámením na zařízení typu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovat aplikace na zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zařízení &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mít přístup ke všemu, co na zařízení typu <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> zobrazíte nebo přehrajete, včetně zvuku, fotek, platebních údajů, hesel a zpráv.&lt;br/&gt;&lt;br/&gt;Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> bude moct streamovat aplikace do zařízení typu <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, dokud přístup k tomuto oprávnění neodeberete."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádá jménem zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o oprávnění streamovat aplikace z vašeho zařízení typu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, mezi vaším telefonem a vybraným zařízením"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 4180ef5b..da3b261 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vil du tillade, at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrerer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"enhed"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Denne app får adgang til disse tilladelser på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Vil du give &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilladelse til at streame apps og systemfunktioner fra din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får adgang til alt, der er synligt eller afspilles på din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, herunder lyd, billeder, betalingsoplysninger, adgangskoder og beskeder.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> kan streame apps til <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, indtil du fjerner adgangen til denne tilladelse."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til at streame apps og systemfunktioner fra din <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Giv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; adgang til disse oplysninger fra din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til at få adgang til billeder, medier og notifikationer på din <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vil du give &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilladelse til at streame apps fra din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får adgang til alt, der er synligt eller afspilles på <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, herunder lyd, billeder, betalingsoplysninger, adgangskoder og beskeder.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> kan streame apps til <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, indtil du fjerner adgangen til denne tilladelse."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til at streame apps fra din <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Denne app vil kunne synkronisere oplysninger som f.eks. navnet på en person, der ringer, mellem din telefon og den valgte enhed"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 725a42d..c39145e 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Zulassen, dass &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; das Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; verwalten darf?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"Gerät"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Diese App darf dann auf diese Berechtigungen auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>) zugreifen:"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, die Apps und Systemfunktionen auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) auf &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; zu streamen?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hat dann Zugriff auf alle Inhalte, die auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) sichtbar sind oder abgespielt werden, einschließlich Audioinhalten, Fotos, Zahlungsinformationen, Passwörtern und Nachrichten.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> kann so lange Apps auf „<xliff:g id="DEVICE_NAME">%3$s</xliff:g>“ streamen, bis du diese Berechtigung entfernst."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein Gerät <xliff:g id="DEVICE_NAME">%2$s</xliff:g> um die Berechtigung, Apps und Systemfunktionen von deinem Gerät (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) zu streamen"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; Zugriff auf diese Informationen von deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) gewähren"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen von „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ um die Berechtigung, auf die Fotos, Medien und Benachrichtigungen auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) zuzugreifen"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, die Apps auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) auf &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; zu streamen?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hat dann Zugriff auf alle Inhalte, die auf deinem Gerät (<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>) sichtbar sind oder abgespielt werden, einschließlich Audioinhalten, Fotos, Zahlungsinformationen, Passwörtern und Nachrichten.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> kann so lange Apps auf „<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>“ streamen, bis du diese Berechtigung entfernst."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein Gerät <xliff:g id="DEVICE_NAME">%2$s</xliff:g> um die Berechtigung, Apps von deinem Gerät (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) zu streamen"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Diese App kann dann Daten wie den Namen eines Anrufers zwischen deinem Smartphone und dem ausgewählten Gerät synchronisieren"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 57aebc3..e465a38 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Να επιτρέπεται στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να διαχειρίζεται τη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ;"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"συσκευή"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Αυτή η εφαρμογή θα μπορεί να έχει πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Να επιτρέπεται στο &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; η δυνατότητα ροής εφαρμογών και λειτουργιών συστήματος του <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> στο &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;;"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Το <xliff:g id="APP_NAME_0">%1$s</xliff:g> θα έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στο <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> σας, συμπεριλαμβανομένων ήχων, φωτογραφιών, στοιχείων πληρωμής, κωδικών πρόσβασης και μηνυμάτων.&lt;br/&gt;&lt;br/&gt;Το <xliff:g id="APP_NAME_1">%1$s</xliff:g> θα έχει τη δυνατότητα ροής εφαρμογών στο <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, μέχρι να καταργήσετε την πρόσβαση σε αυτή την άδεια."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Το <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους του <xliff:g id="DEVICE_NAME">%2$s</xliff:g> για τη ροή εφαρμογών και λειτουργιών συστήματος από το <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> σας"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Να επιτρέπεται στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; η πρόσβαση σε αυτές τις πληροφορίες από τη συσκευή σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>."</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους της συσκευής σας <xliff:g id="DEVICE_NAME">%2$s</xliff:g>, για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις της συσκευής <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Να επιτρέπεται στο &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; η δυνατότητα ροής εφαρμογών του <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> σας στο &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;;"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Το <xliff:g id="APP_NAME_0">%1$s</xliff:g> θα έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στο <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, συμπεριλαμβανομένων ήχων, φωτογραφιών, στοιχείων πληρωμής, κωδικών πρόσβασης και μηνυμάτων.&lt;br/&gt;&lt;br/&gt;Το <xliff:g id="APP_NAME_2">%1$s</xliff:g> θα έχει τη δυνατότητα ροής εφαρμογών στο <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, μέχρι να καταργήσετε την πρόσβαση σε αυτή την άδεια."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Το <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους του <xliff:g id="DEVICE_NAME">%2$s</xliff:g> για τη ροή εφαρμογών από το <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> σας"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες μεταξύ του τηλεφώνου και της επιλεγμένης συσκευής σας, όπως το όνομα ενός ατόμου που σας καλεί."</string>
     <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index 0a85428..92f0a1b 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps and system features to &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and system features from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to access your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\'s photos, media and notifications"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps to &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index 0a85428..92f0a1b 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps and system features to &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and system features from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to access your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\'s photos, media and notifications"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps to &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index 0a85428..92f0a1b 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps and system features to &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and system features from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to access your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\'s photos, media and notifications"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps to &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 9f62192..a7a4086 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Esta app podrá acceder a los siguientes permisos en tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"¿Quieres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; transmita las apps y las funciones del sistema de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo el contenido visible o que se reproduzca en tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, lo que incluye audio, fotos, información de pago, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> podrá transmitir apps a <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hasta que se quite el acceso a este permiso."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para transmitir apps y funciones del sistema desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acceder a las fotos, el contenido multimedia y las notificaciones de tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"¿Quieres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; transmita las apps de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo el contenido visible o que se reproduzca en <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, lo que incluye audio, fotos, información de pago, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> podrá transmitir apps a <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hasta que se quite el acceso a este permiso."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para transmitir apps desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Esta app podrá sincronizar información, como el nombre de la persona que llama, entre el teléfono y el dispositivo elegido"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 44474b9..8816e6d 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"¿Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Esta aplicación podrá acceder a estos permisos de tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"¿Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; emita las aplicaciones y funciones del sistema de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> en &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo lo que se vea o se reproduzca en tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluidos audio, fotos, información para pagos, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> podrá emitir aplicaciones en <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hasta que quites el acceso a este permiso."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para emitir aplicaciones y funciones del sistema desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acceder a las fotos, los archivos multimedia y las notificaciones de tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"¿Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; emita las aplicaciones de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> en &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tendrá acceso a todo lo que se vea o se reproduzca en <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluidos audio, fotos, información para pagos, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> podrá emitir aplicaciones en <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hasta que quites el acceso a este permiso."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para emitir aplicaciones desde tu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Esta aplicación podrá sincronizar información (por ejemplo, el nombre de la persona que te llama) entre tu teléfono y el dispositivo que elijas"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index a1cb0c6..8099537 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallata seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"seade"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Sellel rakendusel lubatakse juurde pääseda nendele lubadele, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Kas lubada rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; teie seadme &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; rakendusi ja süsteemifunktsioone seadmesse <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> voogesitada?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saab juurdepääsu kõigele, mida teie seadmes <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> saab kuvada või esitada, sh helile, fotodele, makseteabele, paroolidele ja sõnumitele.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> saab rakendusi seadmesse <xliff:g id="DEVICE_NAME">%3$s</xliff:g> voogesitada seni, kuni juurdepääsu sellele loale eemaldate."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba rakenduste ja süsteemifunktsioonide voogesitamiseks teie seadmest <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pääseda juurde sellele teabele, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba pääseda juurde fotodele, meediale ja märguannetele, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Kas lubada rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; teie seadme &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; rakendusi seadmesse <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> voogesitada?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saab juurdepääsu kõigele, mida teie seadmes <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> saab kuvada või esitada, sh helile, fotodele, makseteabele, paroolidele ja sõnumitele.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> saab rakendusi seadmesse <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> voogesitada seni, kuni juurdepääsu sellele loale eemaldate."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba rakenduste voogesitamiseks teie seadmest <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
     <string name="summary_generic" msgid="1761976003668044801">"See rakendus saab sünkroonida teavet, näiteks helistaja nime, teie telefoni ja valitud seadme vahel"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index f88af3c..dd9b47c 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena eman nahi diozu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"gailua"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Baimen hauek izango ditu aplikazioak <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> erabiltzean:"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari zure <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuko aplikazioak eta sistemaren eginbideak <xliff:g id="DEVICE_NAME">%3$s</xliff:g> gailura zuzenean igortzeko baimena eman nahi diozu?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, ordainketa-informazioa, pasahitzak eta mezuak barne.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g> gailura aplikazioak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> gailutik aplikazioak eta sistemaren eginbideak zuzenean igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Eman informazioa <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailutik hartzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> gailuko argazkiak, multimedia-edukia eta jakinarazpenak atzitzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari zure <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuko aplikazioak <xliff:g id="DEVICE_NAME">%3$s</xliff:g> gailura zuzenean igortzeko baimena eman nahi diozu?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> gailuan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, ordainketa-informazioa, pasahitzak eta mezuak barne.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> gailura aplikazioak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> gailutik aplikazioak zuzenean igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Telefonoaren eta hautatutako gailuaren artean informazioa sinkronizatzeko gai izango da aplikazioa (esate baterako, deitzaileen izenak)"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 9066c6a..7b013bf 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"‏به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; اجازه داده شود &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; را مدیریت کند؟"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"دستگاه"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"این برنامه قادر خواهد بود به این اجازه‌ها در <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> شما دسترسی پیدا کند"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"‏به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; اجازه می‌دهید برنامه‌ها و ویژگی‌های سیستم <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> را در &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; جاری‌سازی کند؟"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"‏‫<xliff:g id="APP_NAME_0">%1$s</xliff:g> به هرچیزی که در <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> شما نمایان است یا پخش می‌شود، ازجمله صداها، عکس‌ها، اطلاعات پرداخت، گذرواژه‌ها، و پیام‌ها دسترسی خواهد داشت.&lt;br/&gt;&lt;br/&gt;تا زمانی‌که دسترسی به این اجازه را حذف نکنید، <xliff:g id="APP_NAME_1">%1$s</xliff:g> می‌تواند برنامه‌ها را در <xliff:g id="DEVICE_NAME">%3$s</xliff:g> جاری‌سازی کند."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه می‌خواهد برنامه‌ها و ویژگی‌های سیستم را از <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> شما جاری‌سازی کند"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"‏اجازه دادن به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای دسترسی به این اطلاعات در <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> شما"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه می‌خواهد به عکس‌ها، رسانه‌ها، و اعلان‌های <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> شما دسترسی پیدا کند"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"‏به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; اجازه می‌دهید برنامه‌های <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> را در &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; جاری‌سازی کند؟"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"‏‫<xliff:g id="APP_NAME_0">%1$s</xliff:g> به هرچیزی که در <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> شما نمایان است یا پخش می‌شود، ازجمله صداها، عکس‌ها، اطلاعات پرداخت، گذرواژه‌ها، و پیام‌ها دسترسی خواهد داشت.&lt;br/&gt;&lt;br/&gt;تا زمانی‌که دسترسی به این اجازه را حذف نکنید، <xliff:g id="APP_NAME_2">%1$s</xliff:g> می‌تواند برنامه‌ها را در <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> جاری‌سازی کند."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه می‌خواهد برنامه‌ها را از <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> شما جاری‌سازی کند"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
     <string name="summary_generic" msgid="1761976003668044801">"این برنامه مجاز می‌شود اطلاعتی مثل نام شخصی را که تماس می‌گیرد بین تلفن شما و دستگاه انتخاب‌شده همگام‌سازی کند"</string>
     <string name="consent_yes" msgid="8344487259618762872">"اجازه دادن"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index e155077..f20b71b 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; saa ylläpitää laitetta: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"laite"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Sovellus saa käyttää näitä lupia <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Saako &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; striimata <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> olevia sovelluksia ja järjestelmäominaisuuksia laitteelle (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saa pääsyn kaikkeen <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> näkyvään tai pelattavaan sisältöön, mukaan lukien audioon, kuviin, maksutietoihin, salasanoihin ja viesteihin.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> voi striimata sovelluksia laitteelle (<xliff:g id="DEVICE_NAME">%3$s</xliff:g>), kunnes poistat luvan."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteelta (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) lupaa striimata sovelluksia ja järjestelmän ominaisuuksia laitteeltasi (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; saa pääsyn näihin <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> oleviin tietoihin"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa päästä <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> oleviin kuviin, mediaan ja ilmoituksiin"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Saako &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; striimata <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> olevia sovelluksia laitteelle (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saa pääsyn kaikkeen <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> näkyvään tai pelattavaan sisältöön, mukaan lukien audioon, kuviin, salasanoihin ja viesteihin.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> voi striimata sovelluksia laitteelle (<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>), kunnes poistat luvan."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää lapsen (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa striimata sovelluksia laitteeltasi (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Sovellus voi synkronoida tietoja (esimerkiksi soittajan nimen) puhelimesi ja valitun laitteen välillä"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index b5de650..c4a8447 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"appareil"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Cette appli pourra accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à diffuser les applis et les fonctionnalités du système de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> vers &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, y compris le contenu audio, les photos, les infos de paiement, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> pourra diffuser des applis vers <xliff:g id="DEVICE_NAME">%3$s</xliff:g> jusqu\'à ce que vous retiriez l\'accès à cette autorisation."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de diffuser des applis et des fonctionnalités système à partir de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations à partir de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> pour accéder aux photos, aux fichiers multimédias et aux notifications de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à diffuser les applis de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> vers &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, y compris le contenu audio, les photos, les infos de paiement, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> pourra diffuser des applis vers <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> jusqu\'à ce que vous retiriez l\'accès à cette autorisation."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de diffuser des applis à partir de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Cette appli pourra synchroniser des informations, comme le nom de l\'appelant, entre votre téléphone et l\'appareil sélectionné"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index b4933ee2..88627e5 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"appareil"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Cette appli sera autorisée à accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à caster les applis et les fonctionnalités système de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> sur &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, y compris les contenus audio, les photos, les infos de paiement, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> pourra caster des applis sur <xliff:g id="DEVICE_NAME">%3$s</xliff:g> jusqu\'à ce que vous supprimiez l\'accès à cette autorisation."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande, au nom de l\'appareil <xliff:g id="DEVICE_NAME">%2$s</xliff:g>, l\'autorisation de caster des applis et des fonctionnalités système depuis votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations depuis votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> pour accéder aux photos, multimédias et notifications de votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à caster les applis de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> sur &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aura accès à tout ce qui est visible ou lu sur votre <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, y compris les contenus audio, les photos, les infos de paiement, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> pourra caster des applis sur <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> jusqu\'à ce que vous supprimiez l\'accès à cette autorisation."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande, au nom de l\'appareil <xliff:g id="DEVICE_NAME">%2$s</xliff:g>, l\'autorisation de caster des applis depuis votre <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Cette appli pourra synchroniser des infos, comme le nom de l\'appelant, entre votre téléphone et l\'appareil choisi"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index 85bfdc0..a2bd0f8 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Queres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Esta aplicación poderá acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Queres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; emita as aplicacións e as funcións do sistema do dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) en &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acceso a todo o que se vexa ou reproduza no teu dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), como audio, fotos, información de pago, contrasinais e mensaxes.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> poderá emitir aplicacións en <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ata que quites o acceso a este permiso."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome dun dispositivo (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) para emitir aplicacións e funcións do sistema do seguinte aparello: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información do dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) para acceder ás fotos, ao contido multimedia e ás notificacións do seguinte aparello: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Queres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; emita as aplicacións do dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) en &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acceso a todo o que se vexa ou reproduza no teu dispositivo (<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>), como audio, fotos, información de pago, contrasinais e mensaxes.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> poderá emitir aplicacións en <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> ata que quites o acceso a este permiso."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome dun dispositivo (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) para emitir aplicacións do seguinte aparello: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) entre o teléfono e o dispositivo escollido"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 9effe2c..c18ebc0 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; મેનેજ કરવા માટે મંજૂરી આપીએ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ડિવાઇસ"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"આ ઍપને તમારા <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી મળશે"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"શું &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ની ઍપ અને સિસ્ટમની સુવિધાઓને &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; પર સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>ની પાસે એવી બધી બાબતોનો ઍક્સેસ રહેશે જે <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> પર જોઈ શકાતી કે ચલાવી શકાતી હોય, જેમાં ઑડિયો, ફોટા, પાસવર્ડ અને મેસેજ શામેલ છે.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> ત્યાં સુધી ઍપ અને સિસ્ટમની સુવિધાઓને <xliff:g id="DEVICE_NAME">%3$s</xliff:g> પર સ્ટ્રીમ કરી શકશે, જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢી નહીં નાખો."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>માંથી ઍપ અને સિસ્ટમની સુવિધાઓ સ્ટ્રીમ કરવા માટે <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી પરવાનગીની વિનંતી કરી રહી છે"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"તમારા <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>માંથી આ માહિતી ઍક્સેસ કરવા માટે, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂરી આપો"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ના ફોટા, મીડિયા અને નોટિફિકેશન ઍક્સેસ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"શું &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ની ઍપને &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; પર સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>ની પાસે એવી બધી બાબતોનો ઍક્સેસ રહેશે જે <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> પર જોઈ શકાતી કે ચલાવી શકાતી હોય, જેમાં ઑડિયો, ફોટા, ચુકવણીની માહિતી, પાસવર્ડ અને મેસેજ શામેલ છે.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> ત્યાં સુધી ઍપને <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> પર સ્ટ્રીમ કરી શકશે, જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢી નહીં નાખો."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>માંથી ઍપ સ્ટ્રીમ કરવા માટે <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી પરવાનગીની વિનંતી કરી રહી છે"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
     <string name="summary_generic" msgid="1761976003668044801">"આ ઍપ તમારા ફોન અને પસંદ કરેલા ડિવાઇસ વચ્ચે, કૉલ કરનાર કોઈ વ્યક્તિનું નામ જેવી માહિતી સિંક કરી શકશે"</string>
     <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 2a08e00..562f762 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"क्या &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मैनेज करने की अनुमति देनी है?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"डिवाइस"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस कर पाएगा"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"क्या &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> में मौजूद ऐप्लिकेशन और सिस्टम की सुविधाओं को &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; पर स्ट्रीम करने की अनुमति देनी है?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> के पास ऐसे किसी भी कॉन्टेंट का ऐक्सेस होगा जो आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> पर दिखता है या चलाया जाता है. इसमें ऑडियो, फ़ोटो, पेमेंट संबंधी जानकारी, पासवर्ड, और मैसेज शामिल हैं.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%3$s</xliff:g> पर तब ऐप्लिकेशन को स्ट्रीम कर सकेगा, जब तक आप यह अनुमति हटा न दें."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> को <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से, आपके <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> में मौजूद ऐप्लिकेशन और सिस्टम की सुविधाओं को स्ट्रीम करने की अनुमति चाहिए"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> की यह जानकारी ऐक्सेस करने दें"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"आपके <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से <xliff:g id="APP_NAME">%1$s</xliff:g>, आपके <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> में मौजूद फ़ोटो, मीडिया, और सूचनाओं को ऐक्सेस करने की अनुमति मांग रहा है"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"क्या &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> में मौजूद ऐप्लिकेशन को &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; पर स्ट्रीम करने की अनुमति देनी है?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> के पास ऐसे किसी भी कॉन्टेंट का ऐक्सेस होगा जो आपके <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> पर दिखता है या चलाया जाता है. इसमें ऑडियो, फ़ोटो, पेमेंट संबंधी जानकारी, पासवर्ड, और मैसेज शामिल हैं.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> पर तब ऐप्लिकेशन को स्ट्रीम कर सकेगा, जब तक आप यह अनुमति हटा न दें."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> को <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से, आपके <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> में मौजूद ऐप्लिकेशन को स्ट्रीम करने की अनुमति चाहिए"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
     <string name="summary_generic" msgid="1761976003668044801">"यह ऐप्लिकेशन, आपके फ़ोन और चुने हुए डिवाइस के बीच जानकारी सिंक करेगा. जैसे, कॉल करने वाले व्यक्ति का नाम"</string>
     <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index 6b3e204..17b4538 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Dopustiti aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Aplikacija će moći pristupati ovim dopuštenjima na vašem uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Želite li dopustiti aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da streama aplikacije i značajke sustava uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaj &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> imat će pristup svemu što je vidljivo ili se reproducira na uređaju <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, uključujući zvuk, fotografije, podatke o plaćanju, zaporke i poruke.&lt;br/&gt;&lt;br/&gt;Aplikacija <xliff:g id="APP_NAME_1">%1$s</xliff:g> moći će streamati aplikacije na uređaj <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup za to dopuštenje."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za streaming aplikacija i značajki sustava s vašeg uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Omogućite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa informacijama s vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za pristup fotografijama, medijskim sadržajima i obavijestima na uređaju <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Želite li dopustiti aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da streama aplikacije uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaj &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> imat će pristup svemu što je vidljivo ili se reproducira na uređaju <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, uključujući zvuk, fotografije, podatke o plaćanju, zaporke i poruke.&lt;br/&gt;&lt;br/&gt;Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> moći će streamati aplikacije na uređaj <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> dok ne uklonite pristup za to dopuštenje."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za streaming aplikacija s vašeg uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ta će aplikacija moći sinkronizirati podatke između vašeg telefona i odabranog uređaja, primjerice ime pozivatelja"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index 31d9828..4b0dd49 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Engedélyezi, hogy a(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; kezelje a következő eszközt: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"eszköz"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Az alkalmazás hozzáférhet majd ezekhez az engedélyekhez a következőn: <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Engedélyezi a(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; számára a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> alkalmazásainak és rendszerfunkcióinak streamelését a következőre: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> hozzáférhet a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> minden látható vagy lejátszható tartalmához, így az audiotartalmakhoz, fényképekhez, fizetési adatokhoz, jelszavakhoz és üzenetekhez is.&lt;br/&gt;&lt;br/&gt;Amíg Ön el nem távolítja az ehhez az engedélyhez való hozzáférést, a(z) <xliff:g id="APP_NAME_1">%1$s</xliff:g> képes lesz majd az alkalmazások <xliff:g id="DEVICE_NAME">%3$s</xliff:g> eszközre való streamelésére."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nevében az alkalmazások és rendszerfunkciók következőről való streameléséhez: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Engedélyezi a(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazás számára az ehhez az információhoz való hozzáférést a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> esetén"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nevében a(z) <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> fotóihoz, médiatartalmaihoz és értesítéseihez való hozzáféréshez"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Engedélyezi a(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; számára a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> alkalmazásainak streamelését a következőre: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> hozzáférhet a(z) <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> minden látható vagy lejátszható tartalmához, így az audiotartalmakhoz, fényképekhez, fizetési adatokhoz, jelszavakhoz és üzenetekhez is.&lt;br/&gt;&lt;br/&gt;Amíg Ön el nem távolítja az ehhez az engedélyhez való hozzáférést, a(z) <xliff:g id="APP_NAME_2">%1$s</xliff:g> képes lesz majd az alkalmazások <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> eszközre való streamelésére."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nevében az alkalmazások következőről való streameléséhez: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ez az alkalmazás képes lesz szinkronizálni az olyan információkat a telefon és a kiválasztott eszköz között, mint például a hívó fél neve."</string>
     <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index a4238c0..744168b 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Թույլատրե՞լ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կառավարել &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքը"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"սարք"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Այս հավելվածը կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ում"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Թույլատրե՞լ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին հեռարձակել ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ի հավելվածները և համակարգի գործառույթները &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; սարքին։"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածին հասանելի կլինի ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, վճարային տեղեկությունները, գաղտնաբառերը և հաղորդագրությունները։&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> հավելվածը կկարողանա հավելվածներ հեռարձակել <xliff:g id="DEVICE_NAME">%3$s</xliff:g> սարքին, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ից հավելվածներ և համակարգի գործառույթներ հեռարձակելու համար"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին օգտագործել այս տեղեկությունները ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ից"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ի լուսանկարները, մեդիաֆայլերն ու ծանուցումները տեսնելու համար"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Թույլատրե՞լ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին հեռարձակել ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ի հավելվածները &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; սարքին։"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածին հասանելի կլինի ձեր <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>-ում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, վճարային տեղեկությունները, գաղտնաբառերը և հաղորդագրությունները։&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> հավելվածը կկարողանա հավելվածներ հեռարձակել <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> սարքին, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ից հավելվածներ հեռարձակելու համար"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Այս հավելվածը կկարողանա համաժամացնել ձեր հեռախոսի և ընտրված սարքի տվյալները, օր․՝ զանգողի անունը"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 2ee4e89..abb02992 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"perangkat"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Aplikasi ini akan diizinkan mengakses izin ini di <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> Anda"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; melakukan streaming aplikasi dan fitur sistem <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ke &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan memiliki akses ke apa pun yang ditampilkan atau diputar di <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, termasuk audio, foto, info pembayaran, sandi, dan pesan.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> akan dapat melakukan streaming aplikasi ke <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hingga Anda menghapus izin ini."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk melakukan streaming aplikasi dan fitur sistem dari <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> Anda"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengakses informasi ini dari <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> Anda"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk mengakses foto, media, dan notifikasi <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> Anda"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; melakukan streaming aplikasi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ke &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan memiliki akses ke apa pun yang ditampilkan atau diputar di <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, termasuk audio, foto, info pembayaran, sandi, dan pesan.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> akan dapat melakukan streaming aplikasi ke <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hingga Anda menghapus izin ini."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk melakukan streaming aplikasi dari <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> Anda"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Aplikasi ini akan dapat menyinkronkan info, seperti nama penelepon, antara ponsel dan perangkat yang dipilih"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index d86f5da..7294e16 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Leyfa &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; að stjórna &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"tæki"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Þetta forrit fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Leyfa &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; að streyma forritum og kerfiseiginleikum <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> í &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> fær aðgang að öllu sem er sýnilegt eða spilað í <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, þ.m.t. hljóði, myndum, greiðsluupplýsingum, aðgangsorðum og skilaboðum.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> getur streymt forritum í <xliff:g id="DEVICE_NAME">%3$s</xliff:g> þar til þú fjarlægir þessa heimild."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir hönd <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að streyma forritum og kerfiseiginleikum úr <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aðgang að þessum upplýsingum úr <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DEVICE_NAME">%2$s</xliff:g> vegna aðgangs að myndum, margmiðlunarefni og tilkynningum í <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Leyfa &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; að streyma forritum <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> í &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> fær aðgang að öllu sem er sýnilegt eða spilað í <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, þ.m.t. hljóði, myndum, greiðsluupplýsingum, aðgangsorðum og skilaboðum.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> getur streymt forritum í <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> þar til þú fjarlægir þessa heimild."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir hönd <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að streyma forritum úr <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Þetta forrit mun geta samstillt upplýsingar, t.d. nafn þess sem hringir, á milli símans og valins tækis"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 2fdcaf0..fe4cc15 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vuoi consentire all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di gestire &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Questa app potrà accedere alle seguenti autorizzazioni <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>:"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Consentire all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di riprodurre in streaming le app e le funzionalità di sistema <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> su &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> avrà accesso a tutti i contenuti visibili o riprodotti dal tuo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inclusi audio, foto, dati di pagamento, password e messaggi.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> sarà in grado di riprodurre in streaming le app su <xliff:g id="DEVICE_NAME">%3$s</xliff:g> finché non rimuoverai l\'accesso a questa autorizzazione."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede l\'autorizzazione per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per riprodurre in streaming app e funzionalità di sistema dal tuo <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Consenti all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di accedere a queste informazioni <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> l\'autorizzazione ad accedere a foto, contenuti multimediali e notifiche <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Consentire all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di riprodurre in streaming le app <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> su &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> avrà accesso a tutti i contenuti visibili o riprodotti dal tuo <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inclusi audio, foto, dati di pagamento, password e messaggi.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> sarà in grado di riprodurre in streaming le app su <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> finché non rimuoverai l\'accesso a questa autorizzazione."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede l\'autorizzazione per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per riprodurre in streaming le app dal tuo <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, tra il telefono e il dispositivo scelto"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 2efb77e..1600031 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"‏מתן הרשאה לאפליקציה ‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&amp;g;‎‏ לנהל את ‎&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;‎‏"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"מכשיר"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"האפליקציה הזו תוכל לגשת להרשאות האלה ב<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> שלך"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"‏לאשר לאפליקציית &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לשדר את האפליקציות ותכונות המערכת של ה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> אל &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"‏לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> תהיה גישה לכל מה שרואים או מפעילים ב<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, כולל אודיו, תמונות, פרטי תשלום, סיסמאות והודעות.&lt;br/&gt;&lt;br/&gt;לאפליקציה <xliff:g id="APP_NAME_1">%1$s</xliff:g> תהיה אפשרות לשדר אפליקציות ל-<xliff:g id="DEVICE_NAME">%3$s</xliff:g> עד שהגישה להרשאה הזו תוסר."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה ל-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי לשדר אפליקציות ותכונות מערכת מה<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"‏מתן אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לגשת למידע הזה מה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> שלך"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה ל-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי לגשת לתמונות, למדיה ולהתראות ב<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"‏לאשר לאפליקציית &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לשדר את האפליקציות של ה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ל-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"‏לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> תהיה גישה לכל מה שרואים או מפעילים ב-<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, כולל אודיו, תמונות, פרטי תשלום, סיסמאות והודעות.&lt;br/&gt;&lt;br/&gt;לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> תהיה אפשרות לשדר אפליקציות ל-<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> עד שהגישה להרשאה הזו תוסר."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה ל-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי לשדר אפליקציות מה<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
     <string name="summary_generic" msgid="1761976003668044801">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, מהטלפון שלך למכשיר שבחרת"</string>
     <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index f392c10..8351542 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беру керек пе?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"құрылғы"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Бұл қолданба құрылғыда (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>) осы рұқсаттарды пайдалана алады."</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) қолданбалар мен жүйе функцияларын &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; құрылғысына трансляциялауға рұқсат берілсін бе?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасы құрылғыңызда (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) көрінетін не ойнатылатын барлық контентті, соның ішінде аудиофайлдарды, фотосуреттерді, төлем туралы ақпаратты, құпия сөздер мен хабарларды пайдалана алады.&lt;br/&gt;&lt;br/&gt;Осы рұқсатты өшірмесеңіз, <xliff:g id="APP_NAME_1">%1$s</xliff:g> қолданбасы құрылғыға (<xliff:g id="DEVICE_NAME">%3$s</xliff:g>) қолданбаларды трансляциялай алады."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан құрылғыдағы (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) қолданбалар мен жүйе функцияларын трансляциялауға рұқсат сұрайды."</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) осы ақпаратты пайдалануға рұқсат беріңіз."</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) фотосуреттерді, медиафайлдар мен хабарландыруларды пайдалануға рұқсат сұрайды."</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) қолданбаларды &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; құрылғысына трансляциялауға рұқсат берілсін бе?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасы құрылғыда (<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>) көрінетін не ойнатылатын барлық контентті, соның ішінде аудиофайлдарды, фотосуреттерді, төлем туралы ақпаратты, құпия сөздер мен хабарларды пайдалана алады.&lt;br/&gt;&lt;br/&gt;Осы рұқсатты өшірмесеңіз, <xliff:g id="APP_NAME_2">%1$s</xliff:g> қолданбасы құрылғыға (<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>) қолданбаларды трансляциялай алады."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан құрылғыдағы (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) қолданбаларды трансляциялауға рұқсат сұрайды."</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Бұл қолданба телефон мен таңдалған құрылғы арасында деректі (мысалы, қоңырау шалушының атын) синхрондай алады."</string>
     <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 149c624..79ec5f1 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; គ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ឬ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ឧបករណ៍"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"កម្មវិធីនេះ​នឹងត្រូវបានអនុញ្ញាតឱ្យ​ចូលប្រើការអនុញ្ញាតទាំងនេះ​នៅលើ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> របស់អ្នក"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធលើ<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>របស់អ្នកទៅ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ឬ?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> នឹងមានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញ ឬត្រូវបានចាក់នៅលើ<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>របស់អ្នក រួមទាំងសំឡេង រូបថត ព័ត៌មាននៃការទូទាត់ប្រាក់ ពាក្យសម្ងាត់ និងសារ។&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> នឹងអាចផ្សាយកម្មវិធីទៅ <xliff:g id="DEVICE_NAME">%3$s</xliff:g> រហូតទាល់តែអ្នកដកសិទ្ធិចូលប្រើការអនុញ្ញាតនេះចេញ។"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ដើម្បីផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធពី<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>របស់អ្នក"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ចូលប្រើព័ត៌មាននេះពី <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> របស់អ្នក"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីចូលប្រើរូបថត មេឌៀ និងការជូនដំណឹងរបស់ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> អ្នក"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ផ្សាយកម្មវិធីលើ<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>របស់អ្នកទៅ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ឬ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> នឹងមានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញ ឬត្រូវបានចាក់នៅលើ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> រួមទាំងសំឡេង រូបថត ព័ត៌មាននៃការទូទាត់ប្រាក់ ពាក្យសម្ងាត់ និងសារ។&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> នឹងអាចផ្សាយកម្មវិធីទៅ <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> រហូតទាល់តែអ្នកដកសិទ្ធិចូលប្រើការអនុញ្ញាតនេះចេញ។"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ដើម្បីផ្សាយកម្មវិធីពី<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>របស់អ្នក"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
     <string name="summary_generic" msgid="1761976003668044801">"កម្មវិធីនេះនឹងអាច​ធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម​ រវាងឧបករណ៍ដែលបានជ្រើសរើស និងទូរសព្ទរបស់អ្នក"</string>
     <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index df7f4f4..7cf4069 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;? ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ಸಾಧನ"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್‌ ಮಾಡಲು ಈ ಆ್ಯಪ್‌ ಅನ್ನು ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಸಿಸ್ಟಮ್ ಫೀಚರ್‌ಗಳನ್ನು <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ಗೆ ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"ಆಡಿಯೋ, ಫೋಟೋಗಳು, ಪಾವತಿ ಮಾಹಿತಿ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಸಂದೇಶಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನಲ್ಲಿ ಗೋಚರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲದಕ್ಕೂ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ನೀವು ಈ ಅನುಮತಿಗೆ ಆ್ಯಕ್ಸೆಸ್‌ ಅನ್ನು ತೆಗೆದುಹಾಕುವವರೆಗೆ <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ಗೆ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME_1">%1$s</xliff:g> ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ನಿಂದ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಸಿಸ್ಟಮ್ ಫೀಚರ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಗಾಗಿ ವಿನಂತಿಸುತ್ತಿದೆ"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನಿಂದ ಈ ಮಾಹಿತಿಯನ್ನು ಆ್ಯಕ್ಸೆಸ್‌ ಮಾಡಲು <xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಿ"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ನ ಫೋಟೋಗಳು, ಮೀಡಿಯಾ ಮತ್ತು ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್‌ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಆ್ಯಪ್‌ಗಳನ್ನು <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ಗೆ ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ಆಡಿಯೋ, ಫೋಟೋಗಳು, ಪಾವತಿ ಮಾಹಿತಿ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಸಂದೇಶಗಳು ಸೇರಿದಂತೆ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ನಲ್ಲಿ ಗೋಚರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲದಕ್ಕೂ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ನೀವು ಈ ಅನುಮತಿಗೆ ಆ್ಯಕ್ಸೆಸ್‌ ಅನ್ನು ತೆಗೆದುಹಾಕುವವರೆಗೆ <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> ಗೆ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME_2">%1$s</xliff:g> ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ನಿಂದ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಗಾಗಿ ವಿನಂತಿಸುತ್ತಿದೆ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
     <string name="summary_generic" msgid="1761976003668044801">"ಮೊಬೈಲ್ ಫೋನ್ ಮತ್ತು ಆಯ್ಕೆಮಾಡಿದ ಸಾಧನದ ನಡುವೆ, ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಈ ಆ್ಯಪ್‌ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ"</string>
     <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 0192eea..c384363 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 허용하시겠습니까?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"기기"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"앱이 <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>의 앱 및 시스템 기능을 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; 기기로 스트리밍하도록 허용하시겠습니까?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>에서 오디오, 사진, 결제 정보, 비밀번호, 메시지 등 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>에 표시되거나 해당 기기에서 재생되는 모든 항목에 액세스할 수 있습니다.&lt;br/&gt;&lt;br/&gt;이 권한에 대한 액세스를 삭제할 때까지 <xliff:g id="APP_NAME_1">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%3$s</xliff:g> 기기로 앱을 스트리밍할 수 있습니다."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>의 앱 및 시스템 기능을 스트리밍할 권한을 요청하고 있습니다."</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 앱이 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>에서 이 정보에 액세스하도록 허용합니다."</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>의 사진, 미디어, 알림에 액세스할 수 있는 권한을 요청하고 있습니다."</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>의 앱을 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; 기기로 스트리밍하도록 허용하시겠습니까?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>에서 오디오, 사진, 결제 정보, 비밀번호, 메시지 등 <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>에 표시되거나 해당 기기에서 재생되는 모든 항목에 액세스할 수 있습니다.&lt;br/&gt;&lt;br/&gt;이 권한에 대한 액세스를 삭제할 때까지 <xliff:g id="APP_NAME_2">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> 기기로 앱을 스트리밍할 수 있습니다."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>의 앱을 스트리밍할 권한을 요청하고 있습니다."</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
     <string name="summary_generic" msgid="1761976003668044801">"이 앱에서 휴대전화와 선택한 기기 간에 정보(예: 발신자 이름)를 동기화할 수 있게 됩니다."</string>
     <string name="consent_yes" msgid="8344487259618762872">"허용"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index d74436f..70bdf1f 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүн тескөөгө уруксат бересизби?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"түзмөк"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Бул колдонмого <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; түзмөгүнө <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүндөгү колдонмолорду жана тутумдун функцияларын &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; түзмөгүндө алып ойнотууга уруксат бересизби?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздө көрүнгөн же ойнотулган бардык нерселерге, анын ичинде аудио, сүрөттөр, төлөм маалыматы, сырсөздөр жана билдирүүлөргө кире алат.&lt;br/&gt;&lt;br/&gt;Бул уруксатты алып салмайынча, <xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g> түзмөгүндөгү колдонмолорду алып ойното алат."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> түзмөгүңүздөн колдонмолорду жана тутум функцияларын алып ойнотуу үчүн <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүнүн атынан уруксат сурап жатат"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздөгү ушул маалыматты көрүүгө уруксат бериңиз"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> сүрөттөрүн, медиа файлдарын жана билдирмелерин колдонууга уруксат сурап жатат"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздөгү колдонмолорду &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; түзмөгүнө алып ойнотууга уруксат бересизби?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> түзмөгүңүздө көрүнгөн же ойнотулган бардык нерселерге, анын ичинде аудио, сүрөттөр, төлөм маалыматы, сырсөздөр жана билдирүүлөргө кире алат.&lt;br/&gt;&lt;br/&gt;Бул уруксатты алып салмайынча, <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> түзмөгүндөгү колдонмолорду алып ойното алат."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> түзмөгүңүздөн колдонмолорду алып ойнотуу үчүн <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүнүн атынан уруксат сурап жатат"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Бул колдонмо маалыматты шайкештире алат, мисалы, чалып жаткан кишинин атын телефон жана тандалган түзмөк менен шайкештирет"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Ооба"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 8a6a564..7a5f347 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; valdyti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"įrenginio"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Šiai programai bus leidžiama pasiekti toliau nurodytus leidimus jūsų <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Leisti programai &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; srautu perduoti <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> programas ir sistemos funkcijas į &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ galės pasiekti visą jūsų <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> matomą ar leidžiamą turinį, įskaitant garso įrašus, nuotraukas, mokėjimo informaciją, slaptažodžius ir pranešimus.&lt;br/&gt;&lt;br/&gt;Programa „<xliff:g id="APP_NAME_1">%1$s</xliff:g>“ galės perduoti srautu programas į „<xliff:g id="DEVICE_NAME">%3$s</xliff:g>“, kol pašalinsite prieigą prie šio leidimo."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas ir sistemos funkcijas iš jūsų <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Leisti programai &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pasiekti šią informaciją iš jūsų <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ vardu, kad galėtų pasiekti <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> nuotraukas, mediją ir pranešimus"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Leisti programai &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; srautu perduoti <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> programas į &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ galės pasiekti visą „<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>“ matomą ar leidžiamą turinį, įskaitant garso įrašus, nuotraukas, mokėjimo informaciją, slaptažodžius ir pranešimus.&lt;br/&gt;&lt;br/&gt;Programa „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ galės perduoti srautu programas į „<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>“, kol pašalinsite prieigą prie šio leidimo."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas iš jūsų <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ši programa galės sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, su jūsų telefonu ir pasirinktu įrenginiu"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index 26f0968..2d79d53 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vai atļaut lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; piekļūt ierīcei &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ierīce"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Šī lietotne drīkstēs piekļūt norādītajām <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> atļaujām."</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Vai atļaut lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; straumēt <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> lietotnes un sistēmas funkcijas ierīcē &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> varēs piekļūt visam <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ekrānā parādītajam vai atskaņotajam saturam, tostarp audio, fotoattēliem, maksājumu informācijai, parolēm un ziņojumiem.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> varēs straumēt lietotnes ierīcē <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, līdz noņemsiet piekļuvi šai atļaujai."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju <xliff:g id="DEVICE_NAME">%2$s</xliff:g> vārdā straumēt lietotnes un sistēmas funkcijas no jūsu ierīces <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>."</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Vai atļaut lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; piekļūt šai informācijai no jūsu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju piekļūt jūsu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> fotoattēliem, multivides saturam un paziņojumiem šīs ierīces vārdā: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>."</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vai atļaut lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; straumēt <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> lietotnes ierīcē &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> varēs piekļūt visam <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ekrānā parādītajam vai atskaņotajam saturam, tostarp audio, fotoattēliem, maksājumu informācijai, parolēm un ziņojumiem.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> varēs straumēt lietotnes ierīcē <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, līdz noņemsiet piekļuvi šai atļaujai."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju <xliff:g id="DEVICE_NAME">%2$s</xliff:g> vārdā straumēt lietotnes no jūsu ierīces <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>."</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Šī lietotne varēs sinhronizēt informāciju (piemēram, zvanītāja vārdu) starp jūsu tālruni un izvēlēto ierīci"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index c0e48b3..a69a12e 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Ќе дозволите &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"уред"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Апликацијава ќе може да пристапува до овие дозволи на <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Да се дозволи &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да ги стримува апликациите и системските функции од <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ќе има пристап до сè што е видливо или репродуцирано на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, вклучувајќи ги и аудиото, фотографиите, податоците за плаќање, лозинките и пораките.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> ќе може да стримува апликации на <xliff:g id="DEVICE_NAME">%3$s</xliff:g> сѐ додека не ја отстраните дозволава."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да стримува апликации и системски функции од вашиот <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да пристапува до овие податоци на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да пристапува до фотографиите, аудиовизуелните содржини и известувањата на <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Да се дозволи &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да ги стримува апликациите од <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ќе има пристап до сè што е видливо или репродуцирано на <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, вклучувајќи ги и аудиото, фотографиите, податоците за плаќање, лозинките и пораките.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> ќе може да стримува апликации на <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> сѐ додека не ја отстраните дозволава."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да стримува апликации од вашиот <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Оваа апликација ќе може да ги синхронизира податоците како што се имињата на јавувачите помеѓу вашиот телефон и избраниот уред"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index 2439adc..1a19f22 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;? മാനേജ് ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ഉപകരണം"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> എന്നതിൽ ഇനിപ്പറയുന്ന അനുമതികൾ ആക്‌സസ് ചെയ്യാൻ ഈ ആപ്പിനെ അനുവദിക്കും"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിന്റെ ആപ്പുകളും സിസ്റ്റം ഫീച്ചറുകളും &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; എന്നതിലേക്ക് സ്ട്രീം ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"ഓഡിയോ, ഫോട്ടോകൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിൽ ദൃശ്യമാകുന്നതോ പ്ലേ ചെയ്യുന്നതോ എല്ലാ എല്ലാത്തിലേക്കും <xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്നതിന് ആക്സസ് ഉണ്ടായിരിക്കും.&lt;br/&gt;&lt;br/&gt;നിങ്ങൾ ഈ അനുമതിയിലേക്കുള്ള ആക്സസ് നീക്കം ചെയ്യുന്നത് വരെ <xliff:g id="APP_NAME_1">%1$s</xliff:g> എന്നതിന് <xliff:g id="DEVICE_NAME">%3$s</xliff:g> എന്നതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാനാകും."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> എന്നതിൽ നിന്ന് ആപ്പുകളും സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിന്റെ പേരിൽ <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്‌സസ് ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> എന്നതിലെ ഫോട്ടോകൾ, മീഡിയ, അറിയിപ്പുകൾ എന്നിവ ആക്സസ് ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്ന ഉപകരണത്തിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിന്റെ ആപ്പുകൾ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; എന്നതിലേക്ക് സ്ട്രീം ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ഓഡിയോ, ഫോട്ടോകൾ, പേയ്മെന്റ് വിവരങ്ങൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> എന്നതിൽ ദൃശ്യമാകുന്നതോ പ്ലേ ചെയ്യുന്നതോ എല്ലാ എല്ലാത്തിലേക്കും <xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്നതിന് ആക്സസ് ഉണ്ടായിരിക്കും.&lt;br/&gt;&lt;br/&gt;നിങ്ങൾ ഈ അനുമതിയിലേക്കുള്ള ആക്സസ് നീക്കം ചെയ്യുന്നത് വരെ <xliff:g id="APP_NAME_2">%1$s</xliff:g> എന്നതിന് <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> എന്നതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാനാകും."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> എന്നതിൽ നിന്ന് ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിന്റെ പേരിൽ <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
     <string name="summary_generic" msgid="1761976003668044801">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ നിങ്ങളുടെ ഫോണിനും തിരഞ്ഞെടുത്ത ഉപകരണത്തിനും ഇടയിൽ സമന്വയിപ്പിക്കുന്നതിന് ഈ ആപ്പിന് കഴിയും"</string>
     <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 543bdfa..96951ad 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахыг зөвшөөрөх үү?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"төхөөрөмж"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Энэ аппад таны <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> дээрх эдгээр зөвшөөрөлд хандахыг зөвшөөрнө"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н апп болон системийн онцлогийг &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;-д дамжуулахыг зөвшөөрөх үү?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> аудио, зураг, төлбөрийн мэдээлэл, нууц үг, мессеж зэрэг таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> дээр харагдаж, тоглуулж буй аливаа зүйлд хандах эрхтэй байх болно.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> таныг энэ зөвшөөрөлд хандах эрхийг нь хасах хүртэл <xliff:g id="DEVICE_NAME">%3$s</xliff:g>-д апп дамжуулах боломжтой байх болно."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс таны <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-с апп болон системийн онцлог дамжуулах зөвшөөрлийг хүсэж байна"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-н зураг, медиа, мэдэгдэлд хандах зөвшөөрлийг хүсэж байна"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н аппыг &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;-д дамжуулахыг зөвшөөрөх үү?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> аудио, зураг, төлбөрийн мэдээлэл, нууц үг, мессеж зэрэг <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> дээр харагдаж, тоглуулж буй аливаа зүйлд хандах эрхтэй болно.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> таныг энэ зөвшөөрөлд хандах эрхийг нь хасах хүртэл <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>-д апп дамжуулах боломжтой байх болно."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс таны <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>-с апп дамжуулах зөвшөөрлийг хүсэж байна"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Энэ апп залгаж буй хүний нэр зэрэг мэдээллийг таны утас болон сонгосон төхөөрөмжийн хооронд синк хийх боломжтой болно"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 6a7e10e..5ac1e56 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्याची अनुमती द्यायची आहे?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"डिव्हाइस"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"या अ‍ॅपला तुमच्या <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> वर या परवानग्या अ‍ॅक्सेस करण्याची अनुमती दिली जाईल"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> अ‍ॅप्स आणि सिस्टीमची वैशिष्ट्ये &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;वर स्ट्रीम करण्याची अनुमती द्यायची आहे का?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ला ऑडिओ, फोटो, पेमेंट माहिती, पासवर्ड आणि मेसेज यांसह तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> वर दिसणाऱ्या किंवा प्ले होणाऱ्या सर्व गोष्टींचा अ‍ॅक्सेस असेल.&lt;br/&gt;&lt;br/&gt;तुम्ही या परवानगीचा अ‍ॅक्सेस काढून टाकेपर्यंत <xliff:g id="APP_NAME_1">%1$s</xliff:g> हे ॲप्स <xliff:g id="DEVICE_NAME">%3$s</xliff:g> वर स्ट्रीम करू शकेल."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> वरून अ‍ॅप्स आणि सिस्टीम वैशिष्ट्ये स्ट्रीम करण्यासाठी <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला ही माहिती तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> वरून अ‍ॅक्सेस करण्यासाठी अनुमती द्या"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"तुमच्या <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> मधील फोटो, मीडिया आणि नोटिफिकेशन अ‍ॅक्सेस करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ला अ‍ॅप्स &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;वर स्ट्रीम करण्याची अनुमती द्यायची आहे का?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ला ऑडिओ, फोटो, पेमेंट माहिती, पासवर्ड आणि मेसेज यांसह तुमच्या <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> वर दिसणाऱ्या किंवा प्ले होणाऱ्या सर्व गोष्टींचा अ‍ॅक्सेस असेल.&lt;br/&gt;&lt;br/&gt;तुम्ही या परवानगीचा अ‍ॅक्सेस काढून टाकेपर्यंत <xliff:g id="APP_NAME_2">%1$s</xliff:g> हे <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> वर ॲप्स स्ट्रीम करू शकेल."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> वरून अ‍ॅप्स आणि सिस्टीम वैशिष्ट्ये स्ट्रीम करण्यासाठी <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
     <string name="summary_generic" msgid="1761976003668044801">"हे ॲप तुमचा फोन आणि निवडलेल्या डिव्‍हाइसदरम्यान कॉल करत असलेल्‍या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करू शकेल"</string>
     <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 13916b7..b3c8bd0 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -26,7 +26,7 @@
     <string name="profile_name_glasses" msgid="3506504967216601277">"peranti"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Apl ini akan dibenarkan untuk mengakses kebenaran yang berikut pada <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> anda"</string>
     <string name="title_app_streaming" msgid="1047090167914857893">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk menstrim apl dan ciri sistem <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda kepada &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
-    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua perkara yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua kandungan yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string>
     <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menstrim apl dan ciri sistem daripada <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> anda"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
@@ -34,7 +34,7 @@
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> anda untuk mengakses foto, media dan pemberitahuan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> anda"</string>
     <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk menstrim apl <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda kepada &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
-    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua perkara yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan mendapat akses kepada semua kandungan yang dipaparkan atau dimainkan pada <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> akan dapat menstrim apl kepada <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string>
     <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menstrim apl daripada <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> anda"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Apl ini akan dapat menyegerakkan maklumat seperti nama individu yang memanggil, antara telefon anda dengan peranti yang dipilih"</string>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index dea62f6..bb4e7c5 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; အား စီမံခွင့်ပြုမလား။"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"စက်"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"သင့် <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; အား သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ၏ အက်ပ်နှင့် စနစ်တူးလ်များကို &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; တွင် တိုက်ရိုက်ဖွင့်ခွင့်ပြုမလား။"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> သည် အသံ၊ ဓာတ်ပုံ၊ ငွေချေအချက်အလက်၊ စကားဝှက်နှင့် မက်ဆေ့ဂျ်များအပါအဝင် သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> တွင် မြင်နိုင်သော (သို့) ဖွင့်ထားသော အရာအားလုံးကို သုံးခွင့်ရှိပါမည်။&lt;br/&gt;&lt;br/&gt;ဤခွင့်ပြုချက်သုံးခွင့်ကို သင်မဖယ်ရှားမချင်း <xliff:g id="APP_NAME_1">%1$s</xliff:g> သည် <xliff:g id="DEVICE_NAME">%3$s</xliff:g> တွင် အက်ပ်များကို တိုက်ရိုက်ဖွင့်နိုင်ပါမည်။"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့် <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> မှ အက်ပ်များနှင့် စနစ်တူးလ်များကို တိုက်ရိုက်ဖွင့်ရန် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; အား သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> မှ ဤအချက်အလက်ကို သုံးခွင့်ပြုမည်"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့် <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ၏ ဓာတ်ပုံ၊ မီဒီယာနှင့် အကြောင်းကြားချက်များသုံးရန် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; အား သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ၏ အက်ပ်များကို &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; တွင် တိုက်ရိုက်ဖွင့်ခွင့်ပြုမလား။"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> သည် အသံ၊ ဓာတ်ပုံ၊ စကားဝှက်နှင့် မက်ဆေ့ဂျ်များအပါအဝင် <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> တွင် မြင်နိုင်သော (သို့) ဖွင့်ထားသော အရာအားလုံးကို သုံးခွင့်ရှိပါမည်။&lt;br/&gt;&lt;br/&gt;ဤခွင့်ပြုချက်သုံးခွင့်ကို သင်မဖယ်ရှားမချင်း <xliff:g id="APP_NAME_2">%1$s</xliff:g> သည် <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> တွင် အက်ပ်များကို တိုက်ရိုက်ဖွင့်နိုင်ပါမည်။"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့် <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> မှ အက်ပ်များကို တိုက်ရိုက်ဖွင့်ရန် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
     <string name="summary_generic" msgid="1761976003668044801">"ဤအက်ပ်သည် သင့်ဖုန်းနှင့် ရွေးထားသောစက်အကြား ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်နိုင်ပါမည်"</string>
     <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 9a40b6b..e8adbcd 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vil du la &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrere &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"enheten"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Denne appen får disse tillatelsene på <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Vil du la &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; strømme apper og systemfunksjoner fra <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> kan se alt som vises eller spilles av på <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inkludert lyd, bilder, betalingsopplysninger, passord og meldinger.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> kan strømme apper til <xliff:g id="DEVICE_NAME">%3$s</xliff:g> frem til du fjerner tilgangen til denne tillatelsen."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å strømme apper og systemfunksjoner fra <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> på vegne av <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Vil du gi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilgang til denne informasjonen fra <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tilgang til bilder, medieinnhold og varsler fra <xliff:g id="DEVICE_NAME">%2$s</xliff:g> på vegne av <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vil du la &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; strømme apper fra <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> kan se som vises eller spilles av på <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inkludert lyd, bilder, passord og meldinger.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> kan strømme apper til <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> frem til du fjerner tilgangen til denne tillatelsen."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å strømme apper fra <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> på vegne av <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Denne appen kan synkronisere informasjon som navnet til noen som ringer, mellom telefonen og den valgte enheten"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index fdd011b..6386057 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्ने अनुमति दिने हो?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"डिभाइस"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"तपाईंको <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> मा यो एपलाई निम्न अनुमति दिइने छ:"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा भएका एप तथा सिस्टमका सुविधाहरू &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; मा स्ट्रिम गर्न दिने हो?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा देखिने वा प्ले गरिने अडियो, फोटो, भुक्तानीसम्बन्धी जानकारी, पासवर्ड र म्यासेजलगायतका सबै कुरा एक्सेस गर्न सक्ने छ।&lt;br/&gt;&lt;br/&gt;तपाईंले यो अनुमति रद्द नगरेसम्म <xliff:g id="APP_NAME_1">%1$s</xliff:g> ले एपहरू <xliff:g id="DEVICE_NAME">%3$s</xliff:g> मा स्ट्रिम गर्न पाइराख्ने छ।"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट तपाईंको <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> बाट एप र सिस्टमका अन्य सुविधाहरू स्ट्रिम गर्ने अनुमति माग्दै छ"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा भएको यो जानकारी एक्सेस गर्ने अनुमति दिनुहोस्"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट तपाईंको <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> मा भएका फोटो, मिडिया र सूचनाहरू एक्सेस गर्ने अनुमति माग्दै छ"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा भएका एपहरू &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; मा स्ट्रिम गर्न दिने हो?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> मा देखिने वा प्ले गरिने अडियो, फोटो, भुक्तानीसम्बन्धी जानकारी, पासवर्ड र म्यासेजलगायतका सबै कुरा एक्सेस गर्न सक्ने छ।&lt;br/&gt;&lt;br/&gt;तपाईंले यो अनुमति रद्द नगरेसम्म <xliff:g id="APP_NAME_2">%1$s</xliff:g> ले एपहरू <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> मा स्ट्रिम गर्न पाइराख्ने छ।"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट तपाईंको <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> बाट एपहरू स्ट्रिम गर्ने अनुमति माग्दै छ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
     <string name="summary_generic" msgid="1761976003668044801">"यो एपले तपाईंको फोन र तपाईंले छनौट गर्ने डिभाइसका बिचमा कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्न सक्ने छ।"</string>
     <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index d71e8c1..58c7d4f 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"apparaat"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Deze app krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan om apps en systeemfuncties van je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> naar &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; te streamen?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> krijgt toegang tot alles wat zichtbaar is of wordt afgespeeld op je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, waaronder audio, foto\'s, betalingsgegevens, wachtwoorden en berichten.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> kan apps naar <xliff:g id="DEVICE_NAME">%3$s</xliff:g> streamen totdat je dit recht verwijdert."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en systeemfuncties te streamen vanaf je <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang geven tot deze informatie op je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toegang tot de foto\'s, media en meldingen van je <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan om apps van je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> naar &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; te streamen?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> krijgt toegang tot alles wat zichtbaar is of wordt afgespeeld op <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, waaronder audio, foto\'s, wachtwoorden en berichten.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> kan apps naar <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> streamen totdat je dit recht verwijdert."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps te streamen vanaf je <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Deze app kan informatie, zoals de naam van iemand die belt, synchroniseren tussen je telefoon en het gekozen apparaat"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 1e8ed0b..ffba68f 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ଡିଭାଇସ"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ର ଆପ୍ସ ଏବଂ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ରେ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"ଅଡିଓ, ଫଟୋ, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ର ଆକ୍ସେସ ରହିବ।&lt;br/&gt;&lt;br/&gt;ଆପଣ ଏହି ଅନୁମତିକୁ ଆକ୍ସେସ କାଢ଼ି ନଦେବା ପର୍ଯ୍ୟନ୍ତ <xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g>ରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ ସକ୍ଷମ ହେବ।"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ରୁ ଆପ୍ସ ଏବଂ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ଅନୁରୋଧ କରୁଛି"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ର ଫଟୋ, ମିଡିଆ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କର <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ର ଆପ୍ସକୁ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ରେ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ଅଡିଓ, ଫଟୋ, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>ରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ର ଆକ୍ସେସ ରହିବ।&lt;br/&gt;&lt;br/&gt;ଆପଣ ଏହି ଅନୁମତିକୁ ଆକ୍ସେସ କାଢ଼ି ନଦେବା ପର୍ଯ୍ୟନ୍ତ <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>ରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ ସକ୍ଷମ ହେବ।"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ରୁ ଆପ୍ସ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ଅନୁରୋଧ କରୁଛି"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
     <string name="summary_generic" msgid="1761976003668044801">"ଆପଣଙ୍କ ଫୋନ ଏବଂ ବଛାଯାଇଥିବା ଡିଭାଇସ ମଧ୍ୟରେ, କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏହି ଆପ ସକ୍ଷମ ହେବ"</string>
     <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 94a8584..463a02b 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"ਕੀ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"ਡੀਵਾਈਸ"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> \'ਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"ਕੀ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਦੀਆਂ ਐਪਾਂ ਨੂੰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; \'ਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।&lt;br/&gt;&lt;br/&gt;ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨੂੰ ਹਟਾ ਨਹੀਂ ਦਿੰਦੇ, ਉਦੋਂ ਤੱਕ <xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%3$s</xliff:g> \'ਤੇ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰ ਸਕੇਗੀ।"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ਤੋਂ ਐਪਾਂ ਅਤੇ ਹੋਰ ਸਿਸਟਮ ਸੰਬੰਧੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ਦੀਆਂ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ਕੀ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ਦੀਆਂ ਐਪਾਂ ਨੂੰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; \'ਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।&lt;br/&gt;&lt;br/&gt;ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨੂੰ ਹਟਾ ਨਹੀਂ ਦਿੰਦੇ, ਉਦੋਂ ਤੱਕ <xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> \'ਤੇ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰ ਸਕੇਗੀ।"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ਤੋਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
     <string name="summary_generic" msgid="1761976003668044801">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਫ਼ੋਨ ਅਤੇ ਚੁਣੇ ਗਏ ਡੀਵਾਈਸ ਵਿਚਕਾਰ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰ ਸਕੇਗੀ"</string>
     <string name="consent_yes" msgid="8344487259618762872">"ਆਗਿਆ ਦਿਓ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index 949957d..dc7977d 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Zezwolić na dostęp aplikacji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; do urządzenia &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"urządzenie"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Aplikacja będzie miała dostęp do tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Zezwolić aplikacji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na strumieniowanie aplikacji i funkcji systemowych na <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na urządzenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> będzie miała dostęp do wszystkiego, co jest widoczne i odtwarzane na <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, w tym do dźwięku, zdjęć, danych do płatności, haseł i wiadomości.&lt;br/&gt;&lt;br/&gt;Aplikacja <xliff:g id="APP_NAME_1">%1$s</xliff:g> będzie mogła strumieniować aplikacje na urządzenie <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, dopóki nie usuniesz dostępu do tego uprawnienia."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o pozwolenie na strumieniowanie aplikacji i funkcji systemowych z urządzenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Zezwól aplikacji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na dostęp do tych informacji na Twoim <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o uprawnienia dotyczące dostępu do zdjęć, multimediów i powiadomień na <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Zezwolić aplikacji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na strumieniowanie aplikacji na <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na urządzenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> będzie miała dostęp do wszystkiego, co jest widoczne i odtwarzane na <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, w tym do dźwięku, zdjęć, haseł i wiadomości.&lt;br/&gt;&lt;br/&gt;Aplikacja <xliff:g id="APP_NAME_2">%1$s</xliff:g> będzie mogła strumieniować aplikacje na urządzenie <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, dopóki nie usuniesz dostępu do tego uprawnienia."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o pozwolenie na strumieniowanie aplikacji z urządzenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak imię i nazwisko osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index cce0968..88cf563 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming dos apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.&lt;br/&gt;&lt;br/&gt;O app <xliff:g id="APP_NAME_1">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse essas informações do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming dos aplicativos do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.&lt;br/&gt;&lt;br/&gt;O app <xliff:g id="APP_NAME_2">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 1443b13..34034f0 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça a gestão do dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Esta app vai poder aceder a estas autorizações no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Permitir que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça stream das apps e funcionalidades do sistema do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> vai ter acesso a tudo o que seja visível ou reproduzido no seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluindo áudio, fotos, informações de pagamento, palavras-passe e mensagens.&lt;br/&gt;&lt;br/&gt;A app <xliff:g id="APP_NAME_1">%1$s</xliff:g> vai poder fazer stream de apps para o dispositivo <xliff:g id="DEVICE_NAME">%3$s</xliff:g> até remover o acesso a esta autorização."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer stream de apps e funcionalidades do sistema a partir do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aceda a estas informações do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para aceder às fotos, ao conteúdo multimédia e às notificações do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permitir que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça stream das apps do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> vai ter acesso a tudo o que seja visível ou reproduzido no dispositivo <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluindo áudio, fotos, informações de pagamento, palavras-passe e mensagens.&lt;br/&gt;&lt;br/&gt;A app <xliff:g id="APP_NAME_2">%1$s</xliff:g> vai poder fazer stream de apps para o dispositivo <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> até remover o acesso a esta autorização."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer stream de apps do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, entre o telemóvel e o dispositivo escolhido"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index cce0968..88cf563 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming dos apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.&lt;br/&gt;&lt;br/&gt;O app <xliff:g id="APP_NAME_1">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse essas informações do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming dos aplicativos do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.&lt;br/&gt;&lt;br/&gt;O app <xliff:g id="APP_NAME_2">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
     <string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 528a73f..002c552 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permiți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să gestioneze &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"dispozitiv"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Aplicația va putea să acceseze următoarele permisiuni pe <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Permiți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să redea în stream aplicații și funcții de sistem de pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pe &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> va avea acces la tot conținutul vizibil sau redat pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inclusiv conținut audio, fotografii, informații de plată, parole și mesaje.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> va putea să redea în stream aplicații pe <xliff:g id="DEVICE_NAME">%3$s</xliff:g> până când elimini accesul la această permisiune."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a reda în stream aplicații și funcții de sistem de pe <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Permite ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permiți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să redea în stream aplicații de pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pe &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> va avea acces la tot conținutul vizibil sau redat pe <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inclusiv conținut audio, fotografii, informații de plată, parole și mesaje.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> va putea să redea în stream aplicații pe <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> până când elimini accesul la această permisiune."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a reda în stream aplicații de pe <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, între telefonul tău și dispozitivul ales"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Permite"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 6d21beb..6f06a2a 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Разрешить приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять устройством &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"устройстве"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Это приложение получит указанные разрешения на вашем устройстве (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)."</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Разрешить устройству &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; транслировать приложения и системные функции с вашего устройства (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) на устройство &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"У приложения \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будет доступ ко всему, что показывается или воспроизводится на устройстве (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), включая аудиофайлы, фотографии, платежные данные, пароли и сообщения.&lt;br/&gt;&lt;br/&gt;Приложение \"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" сможет транслировать приложения на устройство \"<xliff:g id="DEVICE_NAME">%3$s</xliff:g>\", пока вы не отзовете разрешение."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение транслировать приложения и системные функции с устройства (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)."</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Разрешить приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; доступ к этой информации с вашего устройства (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)?"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение на доступ к фотографиям, медиаконтенту и уведомлениям на устройстве (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)."</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Разрешить приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; транслировать приложения с вашего устройства (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) на устройство &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"У приложения \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будет доступ ко всему, что показывается или воспроизводится на устройстве \"<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>\", включая аудиофайлы, фотографии, платежные данные, пароли и сообщения.&lt;br/&gt;&lt;br/&gt;Приложение \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" сможет транслировать приложения на устройство \"<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>\", пока вы не отзовете разрешение."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение транслировать приложения с устройства (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)."</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Приложение сможет синхронизировать информацию между телефоном и выбранным устройством, например данные из журнала звонков."</string>
     <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index e8b1197..c4b2966 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනා කිරීමට ඉඩ දෙන්න ද?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"උපාංගය"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"මෙම යෙදුමට ඔබේ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> මත මෙම අවසර වෙත ප්‍රවේශ වීමට ඉඩ දෙනු ඇත"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> හි යෙදුම් සහ පද්ධති විශේෂාංග &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; වෙත ප්‍රවාහ කිරීමට &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඉඩ දෙන්න ද?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> හට ශ්‍රව්‍ය, ඡායාරූප, ගෙවීම් තොරතුරු, මුරපද සහ පණිවිඩ ඇතුළුව ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> හි දෘශ්‍යමාන හෝ වාදනය වන ඕනෑම දෙයකට ප්‍රවේශය ඇත.&lt;br/&gt;&lt;br/&gt;ඔබ මෙම අවසරයට ප්‍රවේශය ඉවත් කරන තෙක් <xliff:g id="APP_NAME_1">%1$s</xliff:g> හට <xliff:g id="DEVICE_NAME">%3$s</xliff:g> වෙත යෙදුම් ප්‍රවාහ කිරීමට හැකි වනු ඇත."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> වෙතින් යෙදුම් සහ පද්ධති විශේෂාංග ප්‍රවාහ කිරීමට අවසර ඉල්ලා සිටියි"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙතින් මෙම තොරතුරු වෙත ප්‍රවේශ වීමට ඉඩ දෙන්න"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> හි ඡායාරූප, මාධ්‍ය, සහ දැනුම්දීම් වෙත ප්‍රවේශ වීමට අවසරය ඉල්ලමින් සිටියි"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> හි යෙදුම් &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; වෙත ප්‍රවාහ කිරීමට &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඉඩ දෙන්න ද?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> හට ශ්‍රව්‍ය, ඡායාරූප, ගෙවීම් තොරතුරු, මුරපද සහ පණිවිඩ ඇතුළුව <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> හි දෘශ්‍යමාන හෝ වාදනය වන ඕනෑම දෙයකට ප්‍රවේශය ඇත.&lt;br/&gt;&lt;br/&gt;ඔබ මෙම අවසරයට ප්‍රවේශය ඉවත් කරන තෙක් <xliff:g id="APP_NAME_2">%1$s</xliff:g> හට <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> වෙත යෙදුම් ප්‍රවාහ කිරීමට හැකි වනු ඇත."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> වෙතින් යෙදුම් ප්‍රවාහ කිරීමට අවසර ඉල්ලා සිටියි"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
     <string name="summary_generic" msgid="1761976003668044801">"මෙම යෙදුමට ඔබේ දුරකථනය සහ තෝරා ගත් උපාංගය අතර, අමතන කෙනෙකුගේ නම වැනි, තතු සමමුහුර්ත කිරීමට හැකි වනු ඇත"</string>
     <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 41afcd5..bf96c5c 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Chcete povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovať zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"zariadenie"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Táto aplikácia bude mať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Chcete povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovať aplikácie a systémové funkcie zo zariadenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zariadenia &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mať prístup k všetkému, čo sa zobrazuje alebo prehráva v zariadení <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> vrátane zvuku, fotiek, platobných údajov, hesiel a správ.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> bude môcť streamovať aplikácie do zariadenia <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, kým prístup k tomuto povoleniu neodstránite."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje v mene zariadenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie streamovať aplikácie a systémové funkcie z vášho zariadenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Povoľte aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; prístup k týmto informáciám zo zariadenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie na prístup k fotkám, médiám a upozorneniam zariadenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Chcete povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovať aplikácie zo zariadenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zariadenia &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mať prístup k všetkému, čo sa zobrazuje alebo prehráva v zariadení <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> vrátane zvuku, fotiek, platobných údajov, hesiel a správ.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> bude môcť streamovať aplikácie do zariadenia <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, kým prístup k tomuto povoleniu neodstránite."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje v mene zariadenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie streamovať aplikácie z vášho zariadenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, medzi telefónom a vybraným zariadením"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 14acdf0..33d2430 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Të lejohet që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të menaxhojë &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"pajisje"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Këtij aplikacioni do t\'i lejohet qasja te këto leje te <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Të lejohet që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të transmetojë aplikacionet dhe veçoritë e sistemit nga <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> te &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> do të ketë qasje te çdo gjë që është e dukshme ose që luhet te <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, duke përfshirë audion, fotografitë, informacionet për pagesën, fjalëkalimet dhe mesazhet.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> do të mund t\'i transmetojë aplikacionet në <xliff:g id="DEVICE_NAME">%3$s</xliff:g> derisa ta heqësh qasjen për këtë leje."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_NAME">%2$s</xliff:g> për të transmetuar aplikacione dhe veçori të sistemit nga <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të ketë qasje në këto informacione te <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_NAME">%2$s</xliff:g> për të marrë qasje te fotografitë, media dhe njoftimet te <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Të lejohet që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të transmetojë aplikacionet nga <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> te &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> do të ketë qasje te çdo gjë që është e dukshme ose që luhet te <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, duke përfshirë audion, fotografitë, informacionet për pagesën, fjalëkalimet dhe mesazhet.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> do të mund t\'i transmetojë aplikacionet në <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> derisa ta heqësh qasjen për këtë leje."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_NAME">%2$s</xliff:g> për të transmetuar aplikacione nga <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ky aplikacion do të mund të sinkronizojë informacione, si p.sh emrin e dikujt që po telefonon, mes telefonit tënd dhe pajisjes së zgjedhur."</string>
     <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 3f1420b..582e832 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Желите ли да дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"уређај"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Овој апликацији ће бити дозвољено да приступа овим дозволама на уређају <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Желите да дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; стримује апликације и системске функције уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ће имати приступ свему што се види или пушта на уређају <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, укључујући звук, слике, информације о плаћању, лозинке и поруке.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> ће моћи да стримује апликације на <xliff:g id="DEVICE_NAME">%3$s</xliff:g> док не уклоните приступ овој дозволи."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> тражи дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да стримује апликације и системске функције са уређаја <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; приступа овим информацијама са уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> тражи дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да приступа сликама, медијском садржају и обавештењима са уређаја <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Желите да дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; стримује апликације уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ће имати приступ свему што се види или пушта на уређају <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, укључујући звук, слике, информације о плаћању, лозинке и поруке.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> ће моћи да стримује апликације на <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> док не уклоните приступ овој дозволи."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> тражи дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да стримује апликације са уређаја <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ова апликација ће моћи да синхронизује податке, попут имена особе која упућује позив, између телефона и одабраног уређаја"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index ca8fd22..6d623e5 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Ungependa kuruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; idhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"kifaa"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Programu hii itaruhusiwa kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yako"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Ungependa kuruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; itiririshe programu na vipengele vya mfumo vya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako kwenye &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Programu ya <xliff:g id="APP_NAME_0">%1$s</xliff:g> itafikia chochote kinachoonekana au kuchezwa kwenye <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako, ikiwa ni pamoja na sauti, picha, maelezo ya malipo, manenosiri na ujumbe.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> itaweza kutiririsha programu kwenye <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hadi utakapoondoa ruhusa hii."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ili itiririshe programu na vipengele vya mfumo kwenye <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yako"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifikie maelezo haya kutoka kwenye <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yako"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Ungependa kuruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; itiririshe programu za <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako kwenye &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Programu ya <xliff:g id="APP_NAME_0">%1$s</xliff:g> itafikia chochote kinachoonekana au kuchezwa kwenye <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, ikiwa ni pamoja na sauti, picha, maelezo ya malipo, manenosiri na ujumbe.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> itaweza kutiririsha programu kwenye <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hadi utakapoondoa ruhusa hii."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ili itiririshe programu kwenye <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yako"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Programu hii itaweza kusawazisha maelezo, kama vile jina la mtu anayepiga simu, kati ya simu yako na kifaa ulichochagua"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 76e6410..202ec79 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&amp;gt சாதனத்தை நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"சாதனம்"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"உங்கள் <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுக இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தின் ஆப்ஸையும் சிஸ்டம் அம்சங்களையும் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; சாதனத்தில் ஸ்ட்ரீம் செய்ய &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"ஆடியோ, படங்கள், பேமெண்ட் தகவல்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தில் காட்டப்படுகின்ற/பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_NAME_0">%1$s</xliff:g> அணுகும்.&lt;br/&gt;&lt;br/&gt;இந்த அனுமதிக்கான அணுகலை நீங்கள் அகற்றும் வரை <xliff:g id="DEVICE_NAME">%3$s</xliff:g> சாதனத்தில் ஆப்ஸை <xliff:g id="APP_NAME_1">%1$s</xliff:g> ஸ்ட்ரீம் செய்ய முடியும்."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"உங்கள் <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> சாதனத்தில் இருந்து ஆப்ஸையும் சிஸ்டம் அம்சங்களையும் ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கேட்கிறது"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தில் உள்ள இந்தத் தகவல்களை அணுக, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவும்"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"உங்கள் <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> சாதனத்தில் உள்ள படங்கள், மீடியா, அறிவிப்புகள் ஆகியவற்றை அணுக உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கேட்கிறது"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தின் ஆப்ஸை &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; சாதனத்தில் ஸ்ட்ரீம் செய்ய &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ஆடியோ, படங்கள், பேமெண்ட் தகவல்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> சாதனத்தில் காட்டப்படுகின்ற/பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_NAME_0">%1$s</xliff:g> அணுகும்.&lt;br/&gt;&lt;br/&gt;இந்த அனுமதிக்கான அணுகலை நீங்கள் அகற்றும் வரை <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> சாதனத்தில் ஆப்ஸை <xliff:g id="APP_NAME_2">%1$s</xliff:g> ஸ்ட்ரீம் செய்ய முடியும்."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"உங்கள் <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> சாதனத்தில் இருந்து ஆப்ஸை ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கேட்கிறது"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
     <string name="summary_generic" msgid="1761976003668044801">"அழைப்பவரின் பெயர் போன்ற தகவலை உங்கள் மொபைல் மற்றும் தேர்வுசெய்த சாதனத்திற்கு இடையில் இந்த ஆப்ஸால் ஒத்திசைக்க முடியும்"</string>
     <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 2d3f2f3..34671f9 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;‌ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‌ను అనుమతించాలా?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"పరికరం"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"మీ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>‌లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> యాప్‌లను, సిస్టమ్ ఫీచర్‌లను &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;‌కు స్ట్రీమ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‌ను అనుమతించాలా?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"ఆడియో, ఫోటోలు, పేమెంట్ సమాచారం, పాస్‌వర్డ్‌లతో సహా మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‌లో కనిపించే లేదా ప్లే అయ్యే దేనికైనా <xliff:g id="APP_NAME_0">%1$s</xliff:g>‌కు యాక్సెస్ ఉంటుంది.&lt;br/&gt;&lt;br/&gt;మీరు ఈ అనుమతికి యాక్సెస్‌ను తీసివేసే వరకు <xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g>‌కు యాప్‌లను స్ట్రీమ్ చేయగలదు."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"మీ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> నుండి యాప్‌లను, సిస్టమ్ ఫీచర్‌లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‌ను అనుమతించండి"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"మీ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ఫోటోలను, మీడియాను, ఇంకా నోటిఫికేషన్‌లను యాక్సెస్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> యాప్‌లను &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;‌కు స్ట్రీమ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‌ను అనుమతించాలా?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ఆడియో, ఫోటోలు, పేమెంట్ సమాచారం, పాస్‌వర్డ్‌లతో సహా <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>‌లో కనిపించే లేదా ప్లే అయ్యే దేనికైనా <xliff:g id="APP_NAME_0">%1$s</xliff:g>‌కు యాక్సెస్ ఉంటుంది.&lt;br/&gt;&lt;br/&gt;మీరు ఈ అనుమతికి యాక్సెస్‌ను తీసివేసే వరకు <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>‌కు యాప్‌లను స్ట్రీమ్ చేయగలదు."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"మీ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> నుండి యాప్‌లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
     <string name="summary_generic" msgid="1761976003668044801">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని ఈ యాప్ మీ ఫోన్ కు, ఎంచుకున్న పరికరానికీ మధ్య సింక్ చేయగలుగుతుంది"</string>
     <string name="consent_yes" msgid="8344487259618762872">"అనుమతించండి"</string>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index c157186..e74f96c 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ไหม"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"อุปกรณ์"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"แอปนี้จะได้รับสิทธิ์ดังต่อไปนี้ใน<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ของคุณ"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; สตรีมแอปและฟีเจอร์ของระบบใน<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ของคุณไปยัง &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ไหม"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> จะมีสิทธิ์เข้าถึงทุกอย่างที่ปรากฏหรือเล่นบน<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ซึ่งรวมถึงเสียง รูปภาพ ข้อมูลการชำระเงิน รหัสผ่าน และข้อความ&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> จะสามารถสตรีมแอปไปยัง <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ได้จนกว่าคุณจะนำการให้สิทธิ์นี้ออก"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อสตรีมแอปและฟีเจอร์ของระบบจาก<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ของคุณ"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; เข้าถึงข้อมูลนี้จาก<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ของคุณ"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อเข้าถึงรูปภาพ สื่อ และการแจ้งเตือนใน<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ของคุณ"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; สตรีมแอปใน<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ของคุณไปยัง &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ไหม"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> จะมีสิทธิ์เข้าถึงทุกอย่างที่ปรากฏหรือเล่นบน <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ซึ่งรวมถึงเสียง รูปภาพ ข้อมูลการชำระเงิน รหัสผ่าน และข้อความ&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> จะสามารถสตรีมแอปไปยัง <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> ได้จนกว่าคุณจะนำการให้สิทธิ์นี้ออก"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อสตรีมแอปจาก<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ของคุณ"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
     <string name="summary_generic" msgid="1761976003668044801">"แอปนี้จะสามารถซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา ระหว่างโทรศัพท์ของคุณและอุปกรณ์ที่เลือกไว้ได้"</string>
     <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index ffa91b1..ce907f7 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na pamahalaan ang &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Papayagan ang app na ito na ma-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-stream ang mga app at feature ng system ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>’ sa &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Magkakaroon ng access ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> sa kahit anong nakikita o pine-play sa iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, kasama ang audio, mga larawan, impormasyon sa pagbabayad, mga password, at mga mensahe.&lt;br/&gt;&lt;br/&gt;Magagawa ng<xliff:g id="APP_NAME_1">%1$s</xliff:g> na mag-stream ng mga app sa <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hanggang sa alisin mo ang access sa pahintulot na ito."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Humihingi ang <xliff:g id="APP_NAME">%1$s</xliff:g> ng pahintulot para sa <xliff:g id="DEVICE_NAME">%2$s</xliff:g> na mag-stream ng mga app at feature ng system mula sa iyong <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-access ang impormasyong ito sa iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Humihiling ng pahintulot ang <xliff:g id="APP_NAME">%1$s</xliff:g> para sa iyong <xliff:g id="DEVICE_NAME">%2$s</xliff:g> na ma-access ang mga larawan, media, at notification ng <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> mo"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-stream ang mga app ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> sa &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Magkakaroon ng access ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> sa kahit anong nakikita o pine-play sa <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, kasama ang audio, mga larawan, impormasyon sa pagbabayad, mga password, at mga mensahe.&lt;br/&gt;&lt;br/&gt;Magagawa ng <xliff:g id="APP_NAME_2">%1$s</xliff:g> na mag-stream ng mga app sa <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hanggang sa alisin mo ang access sa pahintulot na ito."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Humihingi ang <xliff:g id="APP_NAME">%1$s</xliff:g> ng pahintulot para sa <xliff:g id="DEVICE_NAME">%2$s</xliff:g> na mag-stream ng mga app mula sa iyong <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Magagawa ng app na ito na mag-sync ng impormasyon, tulad ng pangalan ng isang taong tumatawag, sa pagitan ng iyong telepono at ng napiling device"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 44d6bf7..7acad64 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasına &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazını yönetmesi için izin verilsin mi?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"Cihaz"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Bu uygulamanın <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazınızda şu izinlere erişmesine izin verilecek:"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; adlı uygulamanın <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızdaki uygulamaları ve sistem özelliklerini &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazına aktarmasına izin verilsin mi?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>; ses, fotoğraflar, ödeme bilgileri, şifreler ve mesajlar da dahil olmak üzere <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızda görünen veya oynatılan her şeye erişebilecek.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> siz bu iznin erişimini kaldırana kadar uygulamaları <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cihazına aktarabilecek."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adına uygulamaları ve sistem özelliklerini <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazınızdan aktarmak için izin istiyor"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasının, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızdaki bu bilgilere erişmesine izin verin"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> içindeki fotoğraf, medya ve bildirimlere erişmek için <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; adlı uygulamanın <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınızdaki uygulamaları &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazına aktarmasına izin verilsin mi?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>; ses, fotoğraflar, ödeme bilgileri, şifreler ve mesajlar da dahil olmak üzere <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> cihazında görünen veya oynatılan her şeye erişebilecek.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> siz bu iznin erişimini kaldırana kadar uygulamaları <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> cihazına aktarabilecek."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adına uygulamaları <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazınızdan aktarmak için izin istiyor"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Bu uygulama, arayan kişinin adı gibi bilgileri telefonunuz ve seçili cihaz arasında senkronize edebilir"</string>
     <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 1d248b6..50f93d5 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; керувати пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"пристрій"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Цей додаток матиме доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Дозволити пристрою &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; транслювати додатки й системні функції на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на пристрій &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> матиме доступ до контенту, що відображається чи відтворюється на вашому <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, зокрема до аудіо, фото, платіжної інформації, паролів і повідомлень.&lt;br/&gt;&lt;br/&gt;Додаток <xliff:g id="APP_NAME_1">%1$s</xliff:g> зможе транслювати додатки на пристрій \"<xliff:g id="DEVICE_NAME">%3$s</xliff:g>\", поки ви не скасуєте цей дозвіл."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені пристрою \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запитує дозвіл на трансляцію додатків і системних функцій на вашому <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Дозвольте додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; доступ до цієї інформації на вашому <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запитує дозвіл на доступ до фотографій, медіафайлів і сповіщень на вашому <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; транслювати додатки на вашому <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на пристрій &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> матиме доступ до контенту, що відображається чи відтворюється на пристрої \"<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>\", зокрема до аудіо, фото, платіжної інформації, паролів і повідомлень.&lt;br/&gt;&lt;br/&gt;Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> зможе транслювати додатки на пристрій \"<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>\", поки ви не скасуєте цей дозвіл."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені пристрою \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запитує дозвіл на трансляцію додатків на вашому <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) між телефоном і вибраним пристроєм"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 64732e8..24fd827 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; کا نظم کرنے کی اجازت دیں؟"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"آلہ"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"اس ایپ کو آپ کے <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> پر ان اجازتوں تک رسائی کی اجازت ہوگی"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"‏‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‎ کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی ایپس اور سسٹم کی خصوصیات کو ‎&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;‎ پر سلسلہ بندی کرنے کی اجازت دیں؟"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"‏<xliff:g id="APP_NAME_0">%1$s</xliff:g> کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، ادائیگی کی معلومات، پاس ورڈز اور پیغامات۔;lt;br/&gt;&lt;br/&amp;gt&amp;<xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g> پر اس وقت تک ایپس کی سلسلہ بندی کر سکے گی جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> سے ایپس اور سسٹم کی خصوصیات کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"‏‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‎ کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> سے ان معلومات تک رسائی حاصل کرنے کی اجازت دیں"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> کی تصاویر، میڈیا اور اطلاعات تک رسائی کی اجازت کی درخواست کر رہی ہے"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی ایپس کو <xliff:g id="DEVICE_NAME">%3$s</xliff:g> پر سلسلہ بندی کرنے کی اجازت دیں؟"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"‏<xliff:g id="APP_NAME_0">%1$s</xliff:g> کو <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، ادائیگی کی معلومات، پاس ورڈز اور پیغامات۔&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> پر اس وقت تک ایپس کی سلسلہ بندی کر سکے گی جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> سے ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
     <string name="summary_generic" msgid="1761976003668044801">"یہ ایپ آپ کے فون اور منتخب کردہ آلے کے درمیان معلومات، جیسے کسی کال کرنے والے کے نام، کی مطابقت پذیری کر سکے گی"</string>
     <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 4b0c48a..3335551 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun ruxsat berilsinmi?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"qurilma"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Bu ilova <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> qurilmasida quyidagi ruxsatlarni oladi"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>dagi ilovalar va tizim funksiyalarini &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmasiga striming qilishiga ruxsat berasizmi?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>da koʻrinadigan yoki ijro etiladigan hamma narsaga, jumladan, audio, rasmlar, toʻlov axboroti, parollar va xabarlarga kirish huquqini oladi.&lt;br/&gt;&lt;br/&gt;Bu ruxsatni olib tashlamaguningizcha, <xliff:g id="APP_NAME_1">%1$s</xliff:g> ilovalarni <xliff:g id="DEVICE_NAME">%3$s</xliff:g> qurilmasiga striming qila oladi."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> orqali ilovalar va tizim funksiyalarini uzatish uchun ruxsat olmoqchi"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>dagi ushbu maʼlumot uchun ruxsat bering"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>dagi suratlar, media va bildirishnomalarga kirish uchun ruxsat soʻramoqda"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>dagi ilovalarni &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmasiga striming qilishiga ruxsat berasizmi?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>da koʻrinadigan yoki ijro etiladigan hamma narsaga, jumladan, audio, rasmlar, parollar va xabarlarga kirish huquqini oladi.&lt;br/&gt;&lt;br/&gt;Bu ruxsatni olib tashlamaguningizcha, <xliff:g id="APP_NAME_2">%1$s</xliff:g> ilovalarni <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> qurilmasiga striming qila oladi."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> orqali ilovalarni uzatish uchun ruxsat olmoqchi"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Bu ilova telefoningiz va tanlangan qurilmada chaqiruvchining ismi kabi maʼlumotlarni sinxronlay oladi"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 0b65172..7f6d5b1 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"thiết bị"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Ứng dụng này sẽ được phép dùng những quyền sau trên <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> của bạn"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; truyền trực tuyến các ứng dụng và tính năng của hệ thống trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn đến &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sẽ có quyền truy cập vào mọi nội dung hiển thị hoặc phát trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn, bao gồm cả âm thanh, ảnh, thông tin thanh toán, mật khẩu và tin nhắn.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> sẽ có thể truyền trực tuyến các ứng dụng đến <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cho đến khi bạn thu hồi quyền này."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_NAME">%2$s</xliff:g> để truyền trực tuyến các ứng dụng và tính năng của hệ thống từ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> của bạn"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; truy cập vào thông tin này trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_NAME">%2$s</xliff:g> để truy cập vào ảnh, nội dung nghe nhìn và thông báo trên <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> của bạn"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; truyền trực tuyến các ứng dụng trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn đến &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sẽ có quyền truy cập vào mọi nội dung hiển thị hoặc phát trên <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, bao gồm cả âm thanh, ảnh, thông tin thanh toán, mật khẩu và tin nhắn.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> sẽ có thể truyền trực tuyến các ứng dụng đến <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> cho đến khi bạn thu hồi quyền này."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_NAME">%2$s</xliff:g> để truyền trực tuyến các ứng dụng từ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> của bạn"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ứng dụng này sẽ đồng bộ hoá thông tin (ví dụ: tên người gọi) giữa điện thoại của bạn và thiết bị bạn chọn"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 80e2d50..c54c452 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"设备"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"该应用将能获得您<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的以下权限"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;将您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上的应用和系统功能流式传输到&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;吗?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"“<xliff:g id="APP_NAME_0">%1$s</xliff:g>”将能够访问您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上显示或播放的任何内容,包括音频、照片、付款信息、密码和消息。&lt;br/&gt;&lt;br/&gt;“<xliff:g id="APP_NAME_1">%1$s</xliff:g>”可将应用流式传输到“<xliff:g id="DEVICE_NAME">%3$s</xliff:g>”,除非您撤消此访问权限。"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”请求获得从您的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>流式传输应用和系统功能的权限"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;访问您<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>中的这项信息"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”请求访问您<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>上的照片、媒体内容和通知"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;将您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上的应用流式传输到&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;吗?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"“<xliff:g id="APP_NAME_0">%1$s</xliff:g>”将能够访问“<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>”上显示或播放的任何内容,包括音频、照片、付款信息、密码和消息。&lt;br/&gt;&lt;br/&gt;“<xliff:g id="APP_NAME_2">%1$s</xliff:g>”可将应用流式传输到“<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>”,除非您撤消此访问权限。"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”请求获得从您的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>流式传输应用的权限"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
     <string name="summary_generic" msgid="1761976003668044801">"此应用将能在您的手机和所选设备之间同步信息,例如来电者的姓名"</string>
     <string name="consent_yes" msgid="8344487259618762872">"允许"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index cfb1422..e47dfa0 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;嗎?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"裝置"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"此應用程式將可在<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上取得以下權限"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」串流<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>應用程式內容和系統功能至 <xliff:g id="DEVICE_NAME">%3$s</xliff:g> 嗎?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將能存取<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上顯示或播放的任何內容,包括音訊、相片、付款資料、密碼和訊息。&lt;br/&gt;&lt;br/&gt;「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」將能串流應用程式內容至 <xliff:g id="DEVICE_NAME">%3$s</xliff:g>,直至你移除此存取權限為止。"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求權限,以便從你的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流應用程式內容和系統功能"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」在<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上存取這項資料"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」要求權限,以便存取<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>上的相片、媒體和通知"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」串流<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>應用程式內容至 <xliff:g id="DEVICE_NAME">%3$s</xliff:g> 嗎?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將能存取「<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>」上顯示或播放的任何內容,包括音訊、相片、付款資料、密碼和訊息。&lt;br/&gt;&lt;br/&gt;「<xliff:g id="APP_NAME_2">%1$s</xliff:g>」將能串流應用程式內容至 <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>,直至你移除此存取權限為止。"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求權限,以便從你的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流應用程式內容"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
     <string name="summary_generic" msgid="1761976003668044801">"此應用程式將可同步手機和所選裝置的資訊,例如來電者的名稱"</string>
     <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 490d1bd..b91024a 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;嗎?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"裝置"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"這個應用程式將可取得<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的這些權限"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;將<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>的應用程式和系統功能串流傳輸到 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; 嗎?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將可存取<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>顯示或播放的所有內容,包括音訊、相片、付款資訊、密碼和訊息。&lt;br/&gt;&lt;br/&gt;「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」可將應用程式串流傳輸到 <xliff:g id="DEVICE_NAME">%3$s</xliff:g>,直到你移除這個權限為止。"</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求必要權限,以便從<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流傳輸應用程式和系統功能"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>中的這項資訊"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表你的 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求必要權限,以便存取<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>上的相片、媒體和通知"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;將<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>的應用程式串流傳輸到 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; 嗎?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將可存取 <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> 顯示或播放的所有內容,包括音訊、相片、付款資訊、密碼和訊息。&lt;br/&gt;&lt;br/&gt;「<xliff:g id="APP_NAME_2">%1$s</xliff:g>」可將應用程式串流傳輸到 <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>,直到你移除這個權限為止。"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求必要權限,以便從<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流傳輸應用程式"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
     <string name="summary_generic" msgid="1761976003668044801">"這個應用程式將可在手機和指定裝置間同步資訊,例如來電者名稱"</string>
     <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 3003fb8..160332e 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -25,23 +25,17 @@
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuthi ifinyelele i-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"idivayisi"</string>
     <string name="summary_glasses" msgid="5469208629679579157">"Le-app izovunyelwa ukufinyelela lezi zimvume ku-<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yakho"</string>
-    <!-- no translation found for title_app_streaming (1047090167914857893) -->
-    <skip />
-    <!-- no translation found for summary_app_streaming (7990244299655610920) -->
-    <skip />
-    <!-- no translation found for helper_summary_app_streaming (1872657107404139828) -->
-    <skip />
+    <string name="title_app_streaming" msgid="1047090167914857893">"Vumela &lt;strong&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuba isakaze ama-app e-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho nezakhi zesistimu &lt;strong&gt;ku-<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="7990244299655610920">"I-<xliff:g id="APP_NAME_0">%1$s</xliff:g> izokwazi ukufinyelela kunoma yini ebonakalayo noma edlalwayo ku-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho, okuhlanganisa umsindo, izithombe, ulwazi lokukhokha, amaphasiwedi, nemilayezo.&lt;br/&gt;&lt;br/&gt;I-<xliff:g id="APP_NAME_1">%1$s</xliff:g> izokwazi ukusakaza ama-app ku-<xliff:g id="DEVICE_NAME">%3$s</xliff:g> uze ususe ukufinyelela kule mvume."</string>
+    <string name="helper_summary_app_streaming" msgid="1872657107404139828">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ukuze isakaze ama-app nezakhi zesistimu ukusuka ku-<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yakho"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
     <string name="title_computer" msgid="4782923323932440751">"Vumela &lt;strong&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuze ifinyelele lolu lwazi ukusuka ku-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
     <string name="helper_summary_computer" msgid="2298803016482139668">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> yakho ukuze ifinyelele izithombe ze-<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yakho, imidiya nezaziso"</string>
-    <!-- no translation found for title_nearby_device_streaming (2727103756701741359) -->
-    <skip />
-    <!-- no translation found for summary_nearby_device_streaming (70434958004946884) -->
-    <skip />
-    <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) -->
-    <skip />
+    <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vumela &lt;strong&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuze isakaze ama-app e-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho &lt;strong&gt;ku-<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="70434958004946884">"I-<xliff:g id="APP_NAME_0">%1$s</xliff:g> izokwazi ukufinyelela kunoma yini ebonakalayo noma edlalwayo ku-<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, okuhlanganisa umsindo, izithombe, ulwazi lokukhokha, amaphasiwedi, nemilayezo.&lt;br/&gt;&lt;br/&gt;I-<xliff:g id="APP_NAME_2">%1$s</xliff:g> izokwazi ukusakaza ama-app ku-<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> uze ususe ukufinyelela kule mvume."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ukuze isakaze ama-app nezakhi ukusuka ku-<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yakho"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Le app izokwazi ukuvumelanisa ulwazi, njengegama lomuntu othile ofonayo, phakathi kwefoni yakho nedivayisi ekhethiwe"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index fe7cfc6..a161a50 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -137,4 +137,10 @@
         <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
     </style>
 
+    <style name="DeviceIcon">
+        <item name="android:layout_width">24dp</item>
+        <item name="android:layout_height">24dp</item>
+        <item name="android:layout_gravity">center</item>
+    </style>
+
 </resources>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index 7974a37..50419f7 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -61,6 +61,7 @@
 import android.graphics.BlendModeColorFilter;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.MacAddress;
 import android.os.Bundle;
 import android.os.Handler;
@@ -130,6 +131,8 @@
 
     // Present for single device and multiple device only.
     private ImageView mProfileIcon;
+    // Present for self managed association only;
+    private ImageView mDeviceIcon;
 
     // Only present for selfManaged devices.
     private ImageView mVendorHeaderImage;
@@ -260,8 +263,8 @@
     }
 
     @Override
-    protected void onStop() {
-        super.onStop();
+    protected void onDestroy() {
+        super.onDestroy();
 
         // TODO: handle config changes without cancelling.
         if (!isDone()) {
@@ -306,6 +309,8 @@
         mVendorHeaderName = findViewById(R.id.vendor_header_name);
         mVendorHeaderButton = findViewById(R.id.vendor_header_button);
 
+        mDeviceIcon = findViewById(R.id.device_icon);
+
         mDeviceListRecyclerView = findViewById(R.id.device_list);
 
         mMultipleDeviceSpinner = findViewById(R.id.spinner_multiple_device);
@@ -430,6 +435,7 @@
         final Drawable vendorIcon;
         final CharSequence vendorName;
         final Spanned title;
+        final Icon deviceIcon = mRequest.getDeviceIcon();
 
         if (!SUPPORTED_SELF_MANAGED_PROFILES.contains(deviceProfile)) {
             throw new RuntimeException("Unsupported profile " + deviceProfile);
@@ -452,6 +458,11 @@
         title = getHtmlFromResources(this, PROFILE_TITLES.get(deviceProfile), mAppLabel,
                 getString(R.string.device_type), deviceName);
 
+        if (deviceIcon != null) {
+            mDeviceIcon.setImageIcon(deviceIcon);
+            mDeviceIcon.setVisibility(View.VISIBLE);
+        }
+
         if (PROFILE_SUMMARIES.containsKey(deviceProfile)) {
             final int summaryResourceId = PROFILE_SUMMARIES.get(deviceProfile);
             final Spanned summary = getHtmlFromResources(this, summaryResourceId,
diff --git a/packages/CrashRecovery/adaptor/Android.bp b/packages/CrashRecovery/adaptor/Android.bp
new file mode 100644
index 0000000..df7c3dd
--- /dev/null
+++ b/packages/CrashRecovery/adaptor/Android.bp
@@ -0,0 +1,12 @@
+filegroup {
+    name: "crashrecovery-platform-adaptor-srcs",
+    srcs: select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [
+            "postModularization/java/**/*.java",
+        ],
+        default: [
+            "preModularization/java/**/*.java",
+        ],
+    }),
+    visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/packages/CrashRecovery/adaptor/postModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java b/packages/CrashRecovery/adaptor/postModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java
new file mode 100644
index 0000000..b2d798e2
--- /dev/null
+++ b/packages/CrashRecovery/adaptor/postModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.crashrecovery;
+
+import android.content.Context;
+
+import com.android.server.PackageWatchdog;
+import com.android.server.SystemServiceManager;
+
+import java.util.List;
+
+/**
+ * This class mediates calls to hidden APIs in CrashRecovery module.
+ * This class is used when the CrashRecovery classes are moved to separate module.
+ *
+ * @hide
+ */
+public class CrashRecoveryAdaptor {
+    private static final String TAG = "CrashRecoveryAdaptor";
+    private static final String CRASHRECOVERY_MODULE_LIFECYCLE_CLASS =
+            "com.android.server.crashrecovery.CrashRecoveryModule$Lifecycle";
+
+    /**  Start CrashRecoveryModule LifeCycleService */
+    public static void initializeCrashrecoveryModuleService(
+            SystemServiceManager mSystemServiceManager) {
+        mSystemServiceManager.startService(CRASHRECOVERY_MODULE_LIFECYCLE_CLASS);
+    }
+
+    /**  Does Nothing */
+    public static void packageWatchdogNoteBoot(Context mSystemContext) {
+        // do nothing
+    }
+
+    /**  Does Nothing */
+    public static void packageWatchdogWriteNow(Context mContext) {
+        // do nothing
+    }
+
+    /**  Does Nothing */
+    public static void packageWatchdogOnPackagesReady(PackageWatchdog mPackageWatchdog) {
+        // do nothing
+    }
+
+    /**  Does Nothing */
+    public static void rescuePartyRegisterHealthObserver(Context mSystemContext) {
+        // do nothing
+    }
+
+    /**  Does Nothing */
+    public static void rescuePartyOnSettingsProviderPublished(Context mContext) {
+        // do nothing
+    }
+
+    /**  Does Nothing */
+    public static void rescuePartyResetDeviceConfigForPackages(List<String> packageNames) {
+        // do nothing
+    }
+}
diff --git a/packages/CrashRecovery/adaptor/preModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java b/packages/CrashRecovery/adaptor/preModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java
new file mode 100644
index 0000000..74c647c
--- /dev/null
+++ b/packages/CrashRecovery/adaptor/preModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.crashrecovery;
+
+import android.content.Context;
+
+import com.android.server.PackageWatchdog;
+import com.android.server.RescueParty;
+import com.android.server.SystemServiceManager;
+
+import java.util.List;
+
+/**
+ * This class mediates calls to hidden APIs in CrashRecovery module.
+ * This class is used when CrashRecovery classes are still in platform.
+ *
+ * @hide
+ */
+public class CrashRecoveryAdaptor {
+    private static final String TAG = "CrashRecoveryAdaptor";
+
+    /**  Start CrashRecoveryModule LifeCycleService */
+    public static void initializeCrashrecoveryModuleService(
+            SystemServiceManager mSystemServiceManager) {
+        mSystemServiceManager.startService(CrashRecoveryModule.Lifecycle.class);
+    }
+
+    /**  Forward calls to PackageWatchdog noteboot  */
+    public static void packageWatchdogNoteBoot(Context mSystemContext) {
+        PackageWatchdog.getInstance(mSystemContext).noteBoot();
+    }
+
+    /**  Forward calls to PackageWatchdog writeNow */
+    public static void packageWatchdogWriteNow(Context mContext) {
+        PackageWatchdog.getInstance(mContext).writeNow();
+    }
+
+    /**  Forward calls to PackageWatchdog OnPackagesReady */
+    public static void packageWatchdogOnPackagesReady(PackageWatchdog mPackageWatchdog) {
+        mPackageWatchdog.onPackagesReady();
+    }
+
+    /**  Forward calls to RescueParty RegisterHealthObserver */
+    public static void rescuePartyRegisterHealthObserver(Context mSystemContext) {
+        RescueParty.registerHealthObserver(mSystemContext);
+    }
+
+    /**  Forward calls to RescueParty OnSettingsProviderPublished */
+    public static void rescuePartyOnSettingsProviderPublished(Context mContext) {
+        RescueParty.onSettingsProviderPublished(mContext);
+    }
+
+    /**  Forward calls to RescueParty ResetDeviceConfigForPackages */
+    public static void rescuePartyResetDeviceConfigForPackages(List<String> packageNames) {
+        RescueParty.resetDeviceConfigForPackages(packageNames);
+    }
+}
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
index 9480327..1be776d 100644
--- a/packages/CrashRecovery/framework/Android.bp
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -1,53 +1,12 @@
-soong_config_module_type {
-    name: "platform_filegroup",
-    module_type: "filegroup",
-    config_namespace: "ANDROID",
-    bool_variables: [
-        "crashrecovery_files_in_platform",
-    ],
-    properties: [
-        "srcs",
-    ],
-}
-
-platform_filegroup {
+filegroup {
     name: "framework-crashrecovery-sources",
-    soong_config_variables: {
-        // if this flag is enabled, then files are part of platform
-        crashrecovery_files_in_platform: {
-            srcs: [
-                "java/**/*.java",
-                "java/**/*.aidl",
-            ],
-        },
-    },
-    path: "java",
-    visibility: ["//frameworks/base:__subpackages__"],
-}
-
-soong_config_module_type {
-    name: "module_filegroup",
-    module_type: "filegroup",
-    config_namespace: "ANDROID",
-    bool_variables: [
-        "crashrecovery_files_in_module",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.aidl",
     ],
-    properties: [
-        "srcs",
-    ],
-}
-
-module_filegroup {
-    name: "framework-crashrecovery-module-sources",
-    soong_config_variables: {
-        // if this flag is enabled, then files are part of module
-        crashrecovery_files_in_module: {
-            srcs: [
-                "java/**/*.java",
-                "java/**/*.aidl",
-            ],
-        },
-    },
     path: "java",
-    visibility: ["//packages/modules/CrashRecovery/framework"],
+    visibility: [
+        "//frameworks/base:__subpackages__",
+        "//packages/modules/CrashRecovery/framework",
+    ],
 }
diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp
index 961b41f..1c84402 100644
--- a/packages/CrashRecovery/services/Android.bp
+++ b/packages/CrashRecovery/services/Android.bp
@@ -1,54 +1,18 @@
-soong_config_module_type {
-    name: "platform_filegroup",
-    module_type: "filegroup",
-    config_namespace: "ANDROID",
-    bool_variables: [
-        "crashrecovery_files_in_platform",
-    ],
-    properties: [
-        "srcs",
-    ],
-}
-
-platform_filegroup {
+filegroup {
     name: "services-crashrecovery-sources",
-    soong_config_variables: {
-        // if this flag is enabled, then files are part of platform
-        crashrecovery_files_in_platform: {
-            srcs: [
-                "java/**/*.java",
-                "java/**/*.aidl",
-                ":statslog-crashrecovery-java-gen",
-            ],
-        },
-    },
+    srcs: [
+        ":crashrecovery-platform-adaptor-srcs",
+        ":statslog-crashrecovery-java-gen",
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [],
+        default: ["platform/java/**/*.java"],
+    }),
     visibility: ["//frameworks/base:__subpackages__"],
 }
 
-soong_config_module_type {
-    name: "module_filegroup",
-    module_type: "filegroup",
-    config_namespace: "ANDROID",
-    bool_variables: [
-        "crashrecovery_files_in_module",
-    ],
-    properties: [
-        "srcs",
-    ],
-}
-
-module_filegroup {
+filegroup {
     name: "services-crashrecovery-module-sources",
-    soong_config_variables: {
-        // if this flag is enabled, then files are part of module
-        crashrecovery_files_in_module: {
-            srcs: [
-                "java/**/*.java",
-                "java/**/*.aidl",
-                ":statslog-crashrecovery-java-gen",
-            ],
-        },
-    },
+    srcs: ["module/java/**/*.java"],
     visibility: ["//packages/modules/CrashRecovery/service"],
 }
 
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java b/packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java
new file mode 100644
index 0000000..da9a139
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE;
+import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES;
+import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES;
+import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
+
+import android.Manifest;
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.watchdog.ExplicitHealthCheckService;
+import android.service.watchdog.IExplicitHealthCheckService;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+
+// TODO(b/120598832): Add tests
+/**
+ * Controls the connections with {@link ExplicitHealthCheckService}.
+ */
+class ExplicitHealthCheckController {
+    private static final String TAG = "ExplicitHealthCheckController";
+    private final Object mLock = new Object();
+    private final Context mContext;
+
+    // Called everytime a package passes the health check, so the watchdog is notified of the
+    // passing check. In practice, should never be null after it has been #setEnabled.
+    // To prevent deadlocks between the controller and watchdog threads, we have
+    // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
+    // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer.
+    @GuardedBy("mLock") @Nullable private Consumer<String> mPassedConsumer;
+    // Called everytime after a successful #syncRequest call, so the watchdog can receive packages
+    // supporting health checks and update its internal state. In practice, should never be null
+    // after it has been #setEnabled.
+    // To prevent deadlocks between the controller and watchdog threads, we have
+    // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
+    // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer.
+    @GuardedBy("mLock") @Nullable private Consumer<List<PackageConfig>> mSupportedConsumer;
+    // Called everytime we need to notify the watchdog to sync requests between itself and the
+    // health check service. In practice, should never be null after it has been #setEnabled.
+    // To prevent deadlocks between the controller and watchdog threads, we have
+    // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
+    // It's easier to just NOT hold #mLock when calling into watchdog code on this runnable.
+    @GuardedBy("mLock") @Nullable private Runnable mNotifySyncRunnable;
+    // Actual binder object to the explicit health check service.
+    @GuardedBy("mLock") @Nullable private IExplicitHealthCheckService mRemoteService;
+    // Connection to the explicit health check service, necessary to unbind.
+    // We should only try to bind if mConnection is null, non-null indicates we
+    // are connected or at least connecting.
+    @GuardedBy("mLock") @Nullable private ServiceConnection mConnection;
+    // Bind state of the explicit health check service.
+    @GuardedBy("mLock") private boolean mEnabled;
+
+    ExplicitHealthCheckController(Context context) {
+        mContext = context;
+    }
+
+    /** Enables or disables explicit health checks. */
+    public void setEnabled(boolean enabled) {
+        synchronized (mLock) {
+            Slog.i(TAG, "Explicit health checks " + (enabled ? "enabled." : "disabled."));
+            mEnabled = enabled;
+        }
+    }
+
+    /**
+     * Sets callbacks to listen to important events from the controller.
+     *
+     * <p> Should be called once at initialization before any other calls to the controller to
+     * ensure a happens-before relationship of the set parameters and visibility on other threads.
+     */
+    public void setCallbacks(Consumer<String> passedConsumer,
+            Consumer<List<PackageConfig>> supportedConsumer, Runnable notifySyncRunnable) {
+        synchronized (mLock) {
+            if (mPassedConsumer != null || mSupportedConsumer != null
+                    || mNotifySyncRunnable != null) {
+                Slog.wtf(TAG, "Resetting health check controller callbacks");
+            }
+
+            mPassedConsumer = Objects.requireNonNull(passedConsumer);
+            mSupportedConsumer = Objects.requireNonNull(supportedConsumer);
+            mNotifySyncRunnable = Objects.requireNonNull(notifySyncRunnable);
+        }
+    }
+
+    /**
+     * Calls the health check service to request or cancel packages based on
+     * {@code newRequestedPackages}.
+     *
+     * <p> Supported packages in {@code newRequestedPackages} that have not been previously
+     * requested will be requested while supported packages not in {@code newRequestedPackages}
+     * but were previously requested will be cancelled.
+     *
+     * <p> This handles binding and unbinding to the health check service as required.
+     *
+     * <p> Note, calling this may modify {@code newRequestedPackages}.
+     *
+     * <p> Note, this method is not thread safe, all calls should be serialized.
+     */
+    public void syncRequests(Set<String> newRequestedPackages) {
+        boolean enabled;
+        synchronized (mLock) {
+            enabled = mEnabled;
+        }
+
+        if (!enabled) {
+            Slog.i(TAG, "Health checks disabled, no supported packages");
+            // Call outside lock
+            mSupportedConsumer.accept(Collections.emptyList());
+            return;
+        }
+
+        getSupportedPackages(supportedPackageConfigs -> {
+            // Notify the watchdog without lock held
+            mSupportedConsumer.accept(supportedPackageConfigs);
+            getRequestedPackages(previousRequestedPackages -> {
+                synchronized (mLock) {
+                    // Hold lock so requests and cancellations are sent atomically.
+                    // It is important we don't mix requests from multiple threads.
+
+                    Set<String> supportedPackages = new ArraySet<>();
+                    for (PackageConfig config : supportedPackageConfigs) {
+                        supportedPackages.add(config.getPackageName());
+                    }
+                    // Note, this may modify newRequestedPackages
+                    newRequestedPackages.retainAll(supportedPackages);
+
+                    // Cancel packages no longer requested
+                    actOnDifference(previousRequestedPackages,
+                            newRequestedPackages, p -> cancel(p));
+                    // Request packages not yet requested
+                    actOnDifference(newRequestedPackages,
+                            previousRequestedPackages, p -> request(p));
+
+                    if (newRequestedPackages.isEmpty()) {
+                        Slog.i(TAG, "No more health check requests, unbinding...");
+                        unbindService();
+                        return;
+                    }
+                }
+            });
+        });
+    }
+
+    private void actOnDifference(Collection<String> collection1, Collection<String> collection2,
+            Consumer<String> action) {
+        Iterator<String> iterator = collection1.iterator();
+        while (iterator.hasNext()) {
+            String packageName = iterator.next();
+            if (!collection2.contains(packageName)) {
+                action.accept(packageName);
+            }
+        }
+    }
+
+    /**
+     * Requests an explicit health check for {@code packageName}.
+     * After this request, the callback registered on {@link #setCallbacks} can receive explicit
+     * health check passed results.
+     */
+    private void request(String packageName) {
+        synchronized (mLock) {
+            if (!prepareServiceLocked("request health check for " + packageName)) {
+                return;
+            }
+
+            Slog.i(TAG, "Requesting health check for package " + packageName);
+            try {
+                mRemoteService.request(packageName);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to request health check for package " + packageName, e);
+            }
+        }
+    }
+
+    /**
+     * Cancels all explicit health checks for {@code packageName}.
+     * After this request, the callback registered on {@link #setCallbacks} can no longer receive
+     * explicit health check passed results.
+     */
+    private void cancel(String packageName) {
+        synchronized (mLock) {
+            if (!prepareServiceLocked("cancel health check for " + packageName)) {
+                return;
+            }
+
+            Slog.i(TAG, "Cancelling health check for package " + packageName);
+            try {
+                mRemoteService.cancel(packageName);
+            } catch (RemoteException e) {
+                // Do nothing, if the service is down, when it comes up, we will sync requests,
+                // if there's some other error, retrying wouldn't fix anyways.
+                Slog.w(TAG, "Failed to cancel health check for package " + packageName, e);
+            }
+        }
+    }
+
+    /**
+     * Returns the packages that we can request explicit health checks for.
+     * The packages will be returned to the {@code consumer}.
+     */
+    private void getSupportedPackages(Consumer<List<PackageConfig>> consumer) {
+        synchronized (mLock) {
+            if (!prepareServiceLocked("get health check supported packages")) {
+                return;
+            }
+
+            Slog.d(TAG, "Getting health check supported packages");
+            try {
+                mRemoteService.getSupportedPackages(new RemoteCallback(result -> {
+                    List<PackageConfig> packages =
+                            result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES, android.service.watchdog.ExplicitHealthCheckService.PackageConfig.class);
+                    Slog.i(TAG, "Explicit health check supported packages " + packages);
+                    consumer.accept(packages);
+                }));
+            } catch (RemoteException e) {
+                // Request failed, treat as if all observed packages are supported, if any packages
+                // expire during this period, we may incorrectly treat it as failing health checks
+                // even if we don't support health checks for the package.
+                Slog.w(TAG, "Failed to get health check supported packages", e);
+            }
+        }
+    }
+
+    /**
+     * Returns the packages for which health checks are currently in progress.
+     * The packages will be returned to the {@code consumer}.
+     */
+    private void getRequestedPackages(Consumer<List<String>> consumer) {
+        synchronized (mLock) {
+            if (!prepareServiceLocked("get health check requested packages")) {
+                return;
+            }
+
+            Slog.d(TAG, "Getting health check requested packages");
+            try {
+                mRemoteService.getRequestedPackages(new RemoteCallback(result -> {
+                    List<String> packages = result.getStringArrayList(EXTRA_REQUESTED_PACKAGES);
+                    Slog.i(TAG, "Explicit health check requested packages " + packages);
+                    consumer.accept(packages);
+                }));
+            } catch (RemoteException e) {
+                // Request failed, treat as if we haven't requested any packages, if any packages
+                // were actually requested, they will not be cancelled now. May be cancelled later
+                Slog.w(TAG, "Failed to get health check requested packages", e);
+            }
+        }
+    }
+
+    /**
+     * Binds to the explicit health check service if the controller is enabled and
+     * not already bound.
+     */
+    private void bindService() {
+        synchronized (mLock) {
+            if (!mEnabled || mConnection != null || mRemoteService != null) {
+                if (!mEnabled) {
+                    Slog.i(TAG, "Not binding to service, service disabled");
+                } else if (mRemoteService != null) {
+                    Slog.i(TAG, "Not binding to service, service already connected");
+                } else {
+                    Slog.i(TAG, "Not binding to service, service already connecting");
+                }
+                return;
+            }
+            ComponentName component = getServiceComponentNameLocked();
+            if (component == null) {
+                Slog.wtf(TAG, "Explicit health check service not found");
+                return;
+            }
+
+            Intent intent = new Intent();
+            intent.setComponent(component);
+            mConnection = new ServiceConnection() {
+                @Override
+                public void onServiceConnected(ComponentName name, IBinder service) {
+                    Slog.i(TAG, "Explicit health check service is connected " + name);
+                    initState(service);
+                }
+
+                @Override
+                @MainThread
+                public void onServiceDisconnected(ComponentName name) {
+                    // Service crashed or process was killed, #onServiceConnected will be called.
+                    // Don't need to re-bind.
+                    Slog.i(TAG, "Explicit health check service is disconnected " + name);
+                    synchronized (mLock) {
+                        mRemoteService = null;
+                    }
+                }
+
+                @Override
+                public void onBindingDied(ComponentName name) {
+                    // Application hosting service probably got updated
+                    // Need to re-bind.
+                    Slog.i(TAG, "Explicit health check service binding is dead. Rebind: " + name);
+                    unbindService();
+                    bindService();
+                }
+
+                @Override
+                public void onNullBinding(ComponentName name) {
+                    // Should never happen. Service returned null from #onBind.
+                    Slog.wtf(TAG, "Explicit health check service binding is null?? " + name);
+                }
+            };
+
+            mContext.bindServiceAsUser(intent, mConnection,
+                    Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+            Slog.i(TAG, "Explicit health check service is bound");
+        }
+    }
+
+    /** Unbinds the explicit health check service. */
+    private void unbindService() {
+        synchronized (mLock) {
+            if (mRemoteService != null) {
+                mContext.unbindService(mConnection);
+                mRemoteService = null;
+                mConnection = null;
+            }
+            Slog.i(TAG, "Explicit health check service is unbound");
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private ServiceInfo getServiceInfoLocked() {
+        final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE);
+        final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA
+                        |  PackageManager.MATCH_SYSTEM_ONLY);
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            Slog.w(TAG, "No valid components found.");
+            return null;
+        }
+        return resolveInfo.serviceInfo;
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private ComponentName getServiceComponentNameLocked() {
+        final ServiceInfo serviceInfo = getServiceInfoLocked();
+        if (serviceInfo == null) {
+            return null;
+        }
+
+        final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        if (!Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE
+                .equals(serviceInfo.permission)) {
+            Slog.w(TAG, name.flattenToShortString() + " does not require permission "
+                    + Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE);
+            return null;
+        }
+        return name;
+    }
+
+    private void initState(IBinder service) {
+        synchronized (mLock) {
+            if (!mEnabled) {
+                Slog.w(TAG, "Attempting to connect disabled service?? Unbinding...");
+                // Very unlikely, but we disabled the service after binding but before we connected
+                unbindService();
+                return;
+            }
+            mRemoteService = IExplicitHealthCheckService.Stub.asInterface(service);
+            try {
+                mRemoteService.setCallback(new RemoteCallback(result -> {
+                    String packageName = result.getString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE);
+                    if (!TextUtils.isEmpty(packageName)) {
+                        if (mPassedConsumer == null) {
+                            Slog.wtf(TAG, "Health check passed for package " + packageName
+                                    + "but no consumer registered.");
+                        } else {
+                            // Call without lock held
+                            mPassedConsumer.accept(packageName);
+                        }
+                    } else {
+                        Slog.wtf(TAG, "Empty package passed explicit health check?");
+                    }
+                }));
+                Slog.i(TAG, "Service initialized, syncing requests");
+            } catch (RemoteException e) {
+                Slog.wtf(TAG, "Could not setCallback on explicit health check service");
+            }
+        }
+        // Calling outside lock
+        mNotifySyncRunnable.run();
+    }
+
+    /**
+     * Prepares the health check service to receive requests.
+     *
+     * @return {@code true} if it is ready and we can proceed with a request,
+     * {@code false} otherwise. If it is not ready, and the service is enabled,
+     * we will bind and the request should be automatically attempted later.
+     */
+    @GuardedBy("mLock")
+    private boolean prepareServiceLocked(String action) {
+        if (mRemoteService != null && mEnabled) {
+            return true;
+        }
+        Slog.i(TAG, "Service not ready to " + action
+                + (mEnabled ? ". Binding..." : ". Disabled"));
+        if (mEnabled) {
+            bindService();
+        }
+        return false;
+    }
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
new file mode 100644
index 0000000..9a8261c
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -0,0 +1,2094 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.content.Intent.ACTION_REBOOT;
+import static android.content.Intent.ACTION_SHUTDOWN;
+import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
+
+import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecoveryEvents;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.crashrecovery.flags.Flags;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.sysprop.CrashRecoveryProperties;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.EventLog;
+import android.util.IndentingPrintWriter;
+import android.util.LongArrayQueue;
+import android.util.Slog;
+import android.util.Xml;
+import android.util.XmlUtils;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.BackgroundThread;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Monitors the health of packages on the system and notifies interested observers when packages
+ * fail. On failure, the registered observer with the least user impacting mitigation will
+ * be notified.
+ * @hide
+ */
+public class PackageWatchdog {
+    private static final String TAG = "PackageWatchdog";
+
+    static final String PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS =
+            "watchdog_trigger_failure_duration_millis";
+    static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT =
+            "watchdog_trigger_failure_count";
+    static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED =
+            "watchdog_explicit_health_check_enabled";
+
+    // TODO: make the following values configurable via DeviceConfig
+    private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS =
+            TimeUnit.SECONDS.toMillis(30);
+    private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10;
+
+
+    /** Reason for package failure could not be determined. */
+    public static final int FAILURE_REASON_UNKNOWN = 0;
+
+    /** The package had a native crash. */
+    public static final int FAILURE_REASON_NATIVE_CRASH = 1;
+
+    /** The package failed an explicit health check. */
+    public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2;
+
+    /** The app crashed. */
+    public static final int FAILURE_REASON_APP_CRASH = 3;
+
+    /** The app was not responding. */
+    public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4;
+
+    /** The device was boot looping. */
+    public static final int FAILURE_REASON_BOOT_LOOP = 5;
+
+    /** @hide */
+    @IntDef(prefix = { "FAILURE_REASON_" }, value = {
+            FAILURE_REASON_UNKNOWN,
+            FAILURE_REASON_NATIVE_CRASH,
+            FAILURE_REASON_EXPLICIT_HEALTH_CHECK,
+            FAILURE_REASON_APP_CRASH,
+            FAILURE_REASON_APP_NOT_RESPONDING,
+            FAILURE_REASON_BOOT_LOOP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FailureReasons {}
+
+    // Duration to count package failures before it resets to 0
+    @VisibleForTesting
+    static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS =
+            (int) TimeUnit.MINUTES.toMillis(1);
+    // Number of package failures within the duration above before we notify observers
+    @VisibleForTesting
+    static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5;
+    @VisibleForTesting
+    static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2);
+    // Sliding window for tracking how many mitigation calls were made for a package.
+    @VisibleForTesting
+    static final long DEFAULT_DEESCALATION_WINDOW_MS = TimeUnit.HOURS.toMillis(1);
+    // Whether explicit health checks are enabled or not
+    private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true;
+
+    @VisibleForTesting
+    static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5;
+
+    static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10);
+
+    // Time needed to apply mitigation
+    private static final String MITIGATION_WINDOW_MS =
+            "persist.device_config.configuration.mitigation_window_ms";
+    @VisibleForTesting
+    static final long DEFAULT_MITIGATION_WINDOW_MS = TimeUnit.SECONDS.toMillis(5);
+
+    // Threshold level at which or above user might experience significant disruption.
+    private static final String MAJOR_USER_IMPACT_LEVEL_THRESHOLD =
+            "persist.device_config.configuration.major_user_impact_level_threshold";
+    private static final int DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD =
+            PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
+
+    // Comma separated list of all packages exempt from user impact level threshold. If a package
+    // in the list is crash looping, all the mitigations including factory reset will be performed.
+    private static final String PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+            "persist.device_config.configuration.packages_exempt_from_impact_level_threshold";
+
+    // Comma separated list of default packages exempt from user impact level threshold.
+    private static final String DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+            "com.android.systemui";
+
+    private long mNumberOfNativeCrashPollsRemaining;
+
+    private static final int DB_VERSION = 1;
+    private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog";
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_OBSERVER = "observer";
+    private static final String ATTR_VERSION = "version";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_DURATION = "duration";
+    private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration";
+    private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check";
+    private static final String ATTR_MITIGATION_CALLS = "mitigation-calls";
+    private static final String ATTR_MITIGATION_COUNT = "mitigation-count";
+
+    // A file containing information about the current mitigation count in the case of a boot loop.
+    // This allows boot loop information to persist in the case of an fs-checkpoint being
+    // aborted.
+    private static final String METADATA_FILE = "/metadata/watchdog/mitigation_count.txt";
+
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+     */
+    private static final int LOG_TAG_RESCUE_NOTE = 2900;
+
+    private static final Object sPackageWatchdogLock = new Object();
+    @GuardedBy("sPackageWatchdogLock")
+    private static PackageWatchdog sPackageWatchdog;
+
+    private final Object mLock = new Object();
+    // System server context
+    private final Context mContext;
+    // Handler to run short running tasks
+    private final Handler mShortTaskHandler;
+    // Handler for processing IO and long running tasks
+    private final Handler mLongTaskHandler;
+    // Contains (observer-name -> observer-handle) that have ever been registered from
+    // previous boots. Observers with all packages expired are periodically pruned.
+    // It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
+    @GuardedBy("mLock")
+    private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
+    // File containing the XML data of monitored packages /data/system/package-watchdog.xml
+    private final AtomicFile mPolicyFile;
+    private final ExplicitHealthCheckController mHealthCheckController;
+    private final Runnable mSyncRequests = this::syncRequests;
+    private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason;
+    private final Runnable mSaveToFile = this::saveToFile;
+    private final SystemClock mSystemClock;
+    private final BootThreshold mBootThreshold;
+    private final DeviceConfig.OnPropertiesChangedListener
+            mOnPropertyChangedListener = this::onPropertyChanged;
+
+    private final Set<String> mPackagesExemptFromImpactLevelThreshold = new ArraySet<>();
+
+    // The set of packages that have been synced with the ExplicitHealthCheckController
+    @GuardedBy("mLock")
+    private Set<String> mRequestedHealthCheckPackages = new ArraySet<>();
+    @GuardedBy("mLock")
+    private boolean mIsPackagesReady;
+    // Flag to control whether explicit health checks are supported or not
+    @GuardedBy("mLock")
+    private boolean mIsHealthCheckEnabled = DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED;
+    @GuardedBy("mLock")
+    private int mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS;
+    @GuardedBy("mLock")
+    private int mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT;
+    // SystemClock#uptimeMillis when we last executed #syncState
+    // 0 if no prune is scheduled.
+    @GuardedBy("mLock")
+    private long mUptimeAtLastStateSync;
+    // If true, sync explicit health check packages with the ExplicitHealthCheckController.
+    @GuardedBy("mLock")
+    private boolean mSyncRequired = false;
+
+    @GuardedBy("mLock")
+    private long mLastMitigation = -1000000;
+
+    @FunctionalInterface
+    @VisibleForTesting
+    interface SystemClock {
+        long uptimeMillis();
+    }
+
+    private PackageWatchdog(Context context) {
+        // Needs to be constructed inline
+        this(context, new AtomicFile(
+                        new File(new File(Environment.getDataDirectory(), "system"),
+                                "package-watchdog.xml")),
+                new Handler(Looper.myLooper()), BackgroundThread.getHandler(),
+                new ExplicitHealthCheckController(context),
+                android.os.SystemClock::uptimeMillis);
+    }
+
+    /**
+     * Creates a PackageWatchdog that allows injecting dependencies.
+     */
+    @VisibleForTesting
+    PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler,
+            Handler longTaskHandler, ExplicitHealthCheckController controller,
+            SystemClock clock) {
+        mContext = context;
+        mPolicyFile = policyFile;
+        mShortTaskHandler = shortTaskHandler;
+        mLongTaskHandler = longTaskHandler;
+        mHealthCheckController = controller;
+        mSystemClock = clock;
+        mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
+        mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+                DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS);
+
+        loadFromFile();
+        sPackageWatchdog = this;
+    }
+
+    /** Creates or gets singleton instance of PackageWatchdog. */
+    public static  @NonNull PackageWatchdog getInstance(@NonNull Context context) {
+        synchronized (sPackageWatchdogLock) {
+            if (sPackageWatchdog == null) {
+                new PackageWatchdog(context);
+            }
+            return sPackageWatchdog;
+        }
+    }
+
+    /**
+     * Called during boot to notify when packages are ready on the device so we can start
+     * binding.
+     * @hide
+     */
+    public void onPackagesReady() {
+        synchronized (mLock) {
+            mIsPackagesReady = true;
+            mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName),
+                    packages -> onSupportedPackages(packages),
+                    this::onSyncRequestNotified);
+            setPropertyChangedListenerLocked();
+            updateConfigs();
+        }
+    }
+
+    /**
+     * Registers {@code observer} to listen for package failures. Add a new ObserverInternal for
+     * this observer if it does not already exist.
+     *
+     * <p>Observers are expected to call this on boot. It does not specify any packages but
+     * it will resume observing any packages requested from a previous boot.
+     * @hide
+     */
+    public void registerHealthObserver(PackageHealthObserver observer) {
+        synchronized (mLock) {
+            ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
+            if (internalObserver != null) {
+                internalObserver.registeredObserver = observer;
+            } else {
+                internalObserver = new ObserverInternal(observer.getUniqueIdentifier(),
+                        new ArrayList<>());
+                internalObserver.registeredObserver = observer;
+                mAllObservers.put(observer.getUniqueIdentifier(), internalObserver);
+                syncState("added new observer");
+            }
+        }
+    }
+
+    /**
+     * Starts observing the health of the {@code packages} for {@code observer} and notifies
+     * {@code observer} of any package failures within the monitoring duration.
+     *
+     * <p>If monitoring a package supporting explicit health check, at the end of the monitoring
+     * duration if {@link #onHealthCheckPassed} was never called,
+     * {@link PackageHealthObserver#execute} will be called as if the package failed.
+     *
+     * <p>If {@code observer} is already monitoring a package in {@code packageNames},
+     * the monitoring window of that package will be reset to {@code durationMs} and the health
+     * check state will be reset to a default depending on if the package is contained in
+     * {@link mPackagesWithExplicitHealthCheckEnabled}.
+     *
+     * <p>If {@code packageNames} is empty, this will be a no-op.
+     *
+     * <p>If {@code durationMs} is less than 1, a default monitoring duration
+     * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used.
+     * @hide
+     */
+    public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
+            long durationMs) {
+        if (packageNames.isEmpty()) {
+            Slog.wtf(TAG, "No packages to observe, " + observer.getUniqueIdentifier());
+            return;
+        }
+        if (durationMs < 1) {
+            Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer "
+                    + observer.getUniqueIdentifier() + ". Not observing packages " + packageNames);
+            durationMs = DEFAULT_OBSERVING_DURATION_MS;
+        }
+
+        List<MonitoredPackage> packages = new ArrayList<>();
+        for (int i = 0; i < packageNames.size(); i++) {
+            // Health checks not available yet so health check state will start INACTIVE
+            MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), durationMs, false);
+            if (pkg != null) {
+                packages.add(pkg);
+            } else {
+                Slog.w(TAG, "Failed to create MonitoredPackage for pkg=" + packageNames.get(i));
+            }
+        }
+
+        if (packages.isEmpty()) {
+            return;
+        }
+
+        // Sync before we add the new packages to the observers. This will #pruneObservers,
+        // causing any elapsed time to be deducted from all existing packages before we add new
+        // packages. This maintains the invariant that the elapsed time for ALL (new and existing)
+        // packages is the same.
+        mLongTaskHandler.post(() -> {
+            syncState("observing new packages");
+
+            synchronized (mLock) {
+                ObserverInternal oldObserver = mAllObservers.get(observer.getUniqueIdentifier());
+                if (oldObserver == null) {
+                    Slog.d(TAG, observer.getUniqueIdentifier() + " started monitoring health "
+                            + "of packages " + packageNames);
+                    mAllObservers.put(observer.getUniqueIdentifier(),
+                            new ObserverInternal(observer.getUniqueIdentifier(), packages));
+                } else {
+                    Slog.d(TAG, observer.getUniqueIdentifier() + " added the following "
+                            + "packages to monitor " + packageNames);
+                    oldObserver.updatePackagesLocked(packages);
+                }
+            }
+
+            // Register observer in case not already registered
+            registerHealthObserver(observer);
+
+            // Sync after we add the new packages to the observers. We may have received packges
+            // requiring an earlier schedule than we are currently scheduled for.
+            syncState("updated observers");
+        });
+
+    }
+
+    /**
+     * Unregisters {@code observer} from listening to package failure.
+     * Additionally, this stops observing any packages that may have previously been observed
+     * even from a previous boot.
+     * @hide
+     */
+    public void unregisterHealthObserver(PackageHealthObserver observer) {
+        mLongTaskHandler.post(() -> {
+            synchronized (mLock) {
+                mAllObservers.remove(observer.getUniqueIdentifier());
+            }
+            syncState("unregistering observer: " + observer.getUniqueIdentifier());
+        });
+    }
+
+    /**
+     * Called when a process fails due to a crash, ANR or explicit health check.
+     *
+     * <p>For each package contained in the process, one registered observer with the least user
+     * impact will be notified for mitigation.
+     *
+     * <p>This method could be called frequently if there is a severe problem on the device.
+     */
+    public void onPackageFailure(@NonNull List<VersionedPackage> packages,
+            @FailureReasons int failureReason) {
+        if (packages == null) {
+            Slog.w(TAG, "Could not resolve a list of failing packages");
+            return;
+        }
+        synchronized (mLock) {
+            final long now = mSystemClock.uptimeMillis();
+            if (Flags.recoverabilityDetection()) {
+                if (now >= mLastMitigation
+                        && (now - mLastMitigation) < getMitigationWindowMs()) {
+                    Slog.i(TAG, "Skipping onPackageFailure mitigation");
+                    return;
+                }
+            }
+        }
+        mLongTaskHandler.post(() -> {
+            synchronized (mLock) {
+                if (mAllObservers.isEmpty()) {
+                    return;
+                }
+                boolean requiresImmediateAction = (failureReason == FAILURE_REASON_NATIVE_CRASH
+                        || failureReason == FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
+                if (requiresImmediateAction) {
+                    handleFailureImmediately(packages, failureReason);
+                } else {
+                    for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                        VersionedPackage versionedPackage = packages.get(pIndex);
+                        // Observer that will receive failure for versionedPackage
+                        PackageHealthObserver currentObserverToNotify = null;
+                        int currentObserverImpact = Integer.MAX_VALUE;
+                        MonitoredPackage currentMonitoredPackage = null;
+
+                        // Find observer with least user impact
+                        for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+                            ObserverInternal observer = mAllObservers.valueAt(oIndex);
+                            PackageHealthObserver registeredObserver = observer.registeredObserver;
+                            if (registeredObserver != null
+                                    && observer.onPackageFailureLocked(
+                                    versionedPackage.getPackageName())) {
+                                MonitoredPackage p = observer.getMonitoredPackage(
+                                        versionedPackage.getPackageName());
+                                int mitigationCount = 1;
+                                if (p != null) {
+                                    mitigationCount = p.getMitigationCountLocked() + 1;
+                                }
+                                int impact = registeredObserver.onHealthCheckFailed(
+                                        versionedPackage, failureReason, mitigationCount);
+                                if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
+                                        && impact < currentObserverImpact) {
+                                    currentObserverToNotify = registeredObserver;
+                                    currentObserverImpact = impact;
+                                    currentMonitoredPackage = p;
+                                }
+                            }
+                        }
+
+                        // Execute action with least user impact
+                        if (currentObserverToNotify != null) {
+                            int mitigationCount = 1;
+                            if (currentMonitoredPackage != null) {
+                                currentMonitoredPackage.noteMitigationCallLocked();
+                                mitigationCount =
+                                        currentMonitoredPackage.getMitigationCountLocked();
+                            }
+                            if (Flags.recoverabilityDetection()) {
+                                maybeExecute(currentObserverToNotify, versionedPackage,
+                                        failureReason, currentObserverImpact, mitigationCount);
+                            } else {
+                                currentObserverToNotify.execute(versionedPackage,
+                                        failureReason, mitigationCount);
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * For native crashes or explicit health check failures, call directly into each observer to
+     * mitigate the error without going through failure threshold logic.
+     */
+    private void handleFailureImmediately(List<VersionedPackage> packages,
+            @FailureReasons int failureReason) {
+        VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null;
+        PackageHealthObserver currentObserverToNotify = null;
+        int currentObserverImpact = Integer.MAX_VALUE;
+        for (ObserverInternal observer: mAllObservers.values()) {
+            PackageHealthObserver registeredObserver = observer.registeredObserver;
+            if (registeredObserver != null) {
+                int impact = registeredObserver.onHealthCheckFailed(
+                        failingPackage, failureReason, 1);
+                if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
+                        && impact < currentObserverImpact) {
+                    currentObserverToNotify = registeredObserver;
+                    currentObserverImpact = impact;
+                }
+            }
+        }
+        if (currentObserverToNotify != null) {
+            if (Flags.recoverabilityDetection()) {
+                maybeExecute(currentObserverToNotify, failingPackage, failureReason,
+                        currentObserverImpact, /*mitigationCount=*/ 1);
+            } else {
+                currentObserverToNotify.execute(failingPackage,  failureReason, 1);
+            }
+        }
+    }
+
+    private void maybeExecute(PackageHealthObserver currentObserverToNotify,
+                              VersionedPackage versionedPackage,
+                              @FailureReasons int failureReason,
+                              int currentObserverImpact,
+                              int mitigationCount) {
+        if (allowMitigations(currentObserverImpact, versionedPackage)) {
+            synchronized (mLock) {
+                mLastMitigation = mSystemClock.uptimeMillis();
+            }
+            currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount);
+        }
+    }
+
+    private boolean allowMitigations(int currentObserverImpact,
+            VersionedPackage versionedPackage) {
+        return currentObserverImpact < getUserImpactLevelLimit()
+                || getPackagesExemptFromImpactLevelThreshold().contains(
+                versionedPackage.getPackageName());
+    }
+
+    private long getMitigationWindowMs() {
+        return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS);
+    }
+
+
+    /**
+     * Called when the system server boots. If the system server is detected to be in a boot loop,
+     * query each observer and perform the mitigation action with the lowest user impact.
+     *
+     * Note: PackageWatchdog considers system_server restart loop as bootloop. Full reboots
+     * are not counted in bootloop.
+     * @hide
+     */
+    @SuppressWarnings("GuardedBy")
+    public void noteBoot() {
+        synchronized (mLock) {
+            // if boot count has reached threshold, start mitigation.
+            // We wait until threshold number of restarts only for the first time. Perform
+            // mitigations for every restart after that.
+            boolean mitigate = mBootThreshold.incrementAndTest();
+            if (mitigate) {
+                if (!Flags.recoverabilityDetection()) {
+                    mBootThreshold.reset();
+                }
+                int mitigationCount = mBootThreshold.getMitigationCount() + 1;
+                PackageHealthObserver currentObserverToNotify = null;
+                ObserverInternal currentObserverInternal = null;
+                int currentObserverImpact = Integer.MAX_VALUE;
+                for (int i = 0; i < mAllObservers.size(); i++) {
+                    final ObserverInternal observer = mAllObservers.valueAt(i);
+                    PackageHealthObserver registeredObserver = observer.registeredObserver;
+                    if (registeredObserver != null) {
+                        int impact = Flags.recoverabilityDetection()
+                                ? registeredObserver.onBootLoop(
+                                        observer.getBootMitigationCount() + 1)
+                                : registeredObserver.onBootLoop(mitigationCount);
+                        if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
+                                && impact < currentObserverImpact) {
+                            currentObserverToNotify = registeredObserver;
+                            currentObserverInternal = observer;
+                            currentObserverImpact = impact;
+                        }
+                    }
+                }
+                if (currentObserverToNotify != null) {
+                    if (Flags.recoverabilityDetection()) {
+                        int currentObserverMitigationCount =
+                                currentObserverInternal.getBootMitigationCount() + 1;
+                        currentObserverInternal.setBootMitigationCount(
+                                currentObserverMitigationCount);
+                        saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
+                        currentObserverToNotify.executeBootLoopMitigation(
+                                currentObserverMitigationCount);
+                    } else {
+                        mBootThreshold.setMitigationCount(mitigationCount);
+                        mBootThreshold.saveMitigationCountToMetadata();
+                        currentObserverToNotify.executeBootLoopMitigation(mitigationCount);
+                    }
+                }
+            }
+        }
+    }
+
+    // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also
+    // avoid holding lock?
+    // This currently adds about 7ms extra to shutdown thread
+    /** @hide Writes the package information to file during shutdown. */
+    public void writeNow() {
+        synchronized (mLock) {
+            // Must only run synchronous tasks as this runs on the ShutdownThread and no other
+            // thread is guaranteed to run during shutdown.
+            if (!mAllObservers.isEmpty()) {
+                mLongTaskHandler.removeCallbacks(mSaveToFile);
+                pruneObserversLocked();
+                saveToFile();
+                Slog.i(TAG, "Last write to update package durations");
+            }
+        }
+    }
+
+    /**
+     * Enables or disables explicit health checks.
+     * <p> If explicit health checks are enabled, the health check service is started.
+     * <p> If explicit health checks are disabled, pending explicit health check requests are
+     * passed and the health check service is stopped.
+     */
+    private void setExplicitHealthCheckEnabled(boolean enabled) {
+        synchronized (mLock) {
+            mIsHealthCheckEnabled = enabled;
+            mHealthCheckController.setEnabled(enabled);
+            mSyncRequired = true;
+            // Prune to update internal state whenever health check is enabled/disabled
+            syncState("health check state " + (enabled ? "enabled" : "disabled"));
+        }
+    }
+
+    /**
+     * This method should be only called on mShortTaskHandler, since it modifies
+     * {@link #mNumberOfNativeCrashPollsRemaining}.
+     */
+    private void checkAndMitigateNativeCrashes() {
+        mNumberOfNativeCrashPollsRemaining--;
+        // Check if native watchdog reported a crash
+        if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
+            // We rollback all available low impact rollbacks when crash is unattributable
+            onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
+            // we stop polling after an attempt to execute rollback, regardless of whether the
+            // attempt succeeds or not
+        } else {
+            if (mNumberOfNativeCrashPollsRemaining > 0) {
+                mShortTaskHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
+                        NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
+            }
+        }
+    }
+
+    /**
+     * Since this method can eventually trigger a rollback, it should be called
+     * only once boot has completed {@code onBootCompleted} and not earlier, because the install
+     * session must be entirely completed before we try to rollback.
+     * @hide
+     */
+    public void scheduleCheckAndMitigateNativeCrashes() {
+        Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check "
+                + "and mitigate native crashes");
+        mShortTaskHandler.post(()->checkAndMitigateNativeCrashes());
+    }
+
+    private int getUserImpactLevelLimit() {
+        return SystemProperties.getInt(MAJOR_USER_IMPACT_LEVEL_THRESHOLD,
+                DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD);
+    }
+
+    private Set<String> getPackagesExemptFromImpactLevelThreshold() {
+        if (mPackagesExemptFromImpactLevelThreshold.isEmpty()) {
+            String packageNames = SystemProperties.get(PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD,
+                    DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD);
+            return Set.of(packageNames.split("\\s*,\\s*"));
+        }
+        return mPackagesExemptFromImpactLevelThreshold;
+    }
+
+    /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}.
+     * @hide
+     */
+    @Retention(SOURCE)
+    @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_10,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_20,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_40,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_71,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_75,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_80,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_90,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_100})
+    public @interface PackageHealthObserverImpact {
+        /** No action to take. */
+        int USER_IMPACT_LEVEL_0 = 0;
+        /* Action has low user impact, user of a device will barely notice. */
+        int USER_IMPACT_LEVEL_10 = 10;
+        /* Actions having medium user impact, user of a device will likely notice. */
+        int USER_IMPACT_LEVEL_20 = 20;
+        int USER_IMPACT_LEVEL_30 = 30;
+        int USER_IMPACT_LEVEL_40 = 40;
+        int USER_IMPACT_LEVEL_50 = 50;
+        int USER_IMPACT_LEVEL_70 = 70;
+        /* Action has high user impact, a last resort, user of a device will be very frustrated. */
+        int USER_IMPACT_LEVEL_71 = 71;
+        int USER_IMPACT_LEVEL_75 = 75;
+        int USER_IMPACT_LEVEL_80 = 80;
+        int USER_IMPACT_LEVEL_90 = 90;
+        int USER_IMPACT_LEVEL_100 = 100;
+    }
+
+    /** Register instances of this interface to receive notifications on package failure. */
+    public interface PackageHealthObserver {
+        /**
+         * Called when health check fails for the {@code versionedPackage}.
+         *
+         * @param versionedPackage the package that is failing. This may be null if a native
+         *                          service is crashing.
+         * @param failureReason   the type of failure that is occurring.
+         * @param mitigationCount the number of times mitigation has been called for this package
+         *                        (including this time).
+         *
+         *
+         * @return any one of {@link PackageHealthObserverImpact} to express the impact
+         * to the user on {@link #execute}
+         */
+        @PackageHealthObserverImpact int onHealthCheckFailed(
+                @Nullable VersionedPackage versionedPackage,
+                @FailureReasons int failureReason,
+                int mitigationCount);
+
+        /**
+         * Executes mitigation for {@link #onHealthCheckFailed}.
+         *
+         * @param versionedPackage the package that is failing. This may be null if a native
+         *                          service is crashing.
+         * @param failureReason   the type of failure that is occurring.
+         * @param mitigationCount the number of times mitigation has been called for this package
+         *                        (including this time).
+         * @return {@code true} if action was executed successfully, {@code false} otherwise
+         */
+        boolean execute(@Nullable VersionedPackage versionedPackage,
+                @FailureReasons int failureReason, int mitigationCount);
+
+
+        /**
+         * Called when the system server has booted several times within a window of time, defined
+         * by {@link #mBootThreshold}
+         *
+         * @param mitigationCount the number of times mitigation has been attempted for this
+         *                        boot loop (including this time).
+         */
+        default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) {
+            return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+        }
+
+        /**
+         * Executes mitigation for {@link #onBootLoop}
+         * @param mitigationCount the number of times mitigation has been attempted for this
+         *                        boot loop (including this time).
+         */
+        default boolean executeBootLoopMitigation(int mitigationCount) {
+            return false;
+        }
+
+        // TODO(b/120598832): Ensure uniqueness?
+        /**
+         * Identifier for the observer, should not change across device updates otherwise the
+         * watchdog may drop observing packages with the old name.
+         */
+        @NonNull String getUniqueIdentifier();
+
+        /**
+         * An observer will not be pruned if this is set, even if the observer is not explicitly
+         * monitoring any packages.
+         */
+        default boolean isPersistent() {
+            return false;
+        }
+
+        /**
+         * Returns {@code true} if this observer wishes to observe the given package, {@code false}
+         * otherwise
+         *
+         * <p> A persistent observer may choose to start observing certain failing packages, even if
+         * it has not explicitly asked to watch the package with {@link #startObservingHealth}.
+         */
+        default boolean mayObservePackage(@NonNull String packageName) {
+            return false;
+        }
+    }
+
+    @VisibleForTesting
+    long getTriggerFailureCount() {
+        synchronized (mLock) {
+            return mTriggerFailureCount;
+        }
+    }
+
+    @VisibleForTesting
+    long getTriggerFailureDurationMs() {
+        synchronized (mLock) {
+            return mTriggerFailureDurationMs;
+        }
+    }
+
+    /**
+     * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}.
+     */
+    private void syncRequestsAsync() {
+        mShortTaskHandler.removeCallbacks(mSyncRequests);
+        mShortTaskHandler.post(mSyncRequests);
+    }
+
+    /**
+     * Syncs health check requests with the {@link ExplicitHealthCheckController}.
+     * Calls to this must be serialized.
+     *
+     * @see #syncRequestsAsync
+     */
+    private void syncRequests() {
+        boolean syncRequired = false;
+        synchronized (mLock) {
+            if (mIsPackagesReady) {
+                Set<String> packages = getPackagesPendingHealthChecksLocked();
+                if (mSyncRequired || !packages.equals(mRequestedHealthCheckPackages)
+                        || packages.isEmpty()) {
+                    syncRequired = true;
+                    mRequestedHealthCheckPackages = packages;
+                }
+            } // else, we will sync requests when packages become ready
+        }
+
+        // Call outside lock to avoid holding lock when calling into the controller.
+        if (syncRequired) {
+            Slog.i(TAG, "Syncing health check requests for packages: "
+                    + mRequestedHealthCheckPackages);
+            mHealthCheckController.syncRequests(mRequestedHealthCheckPackages);
+            mSyncRequired = false;
+        }
+    }
+
+    /**
+     * Updates the observers monitoring {@code packageName} that explicit health check has passed.
+     *
+     * <p> This update is strictly for registered observers at the time of the call
+     * Observers that register after this signal will have no knowledge of prior signals and will
+     * effectively behave as if the explicit health check hasn't passed for {@code packageName}.
+     *
+     * <p> {@code packageName} can still be considered failed if reported by
+     * {@link #onPackageFailureLocked} before the package expires.
+     *
+     * <p> Triggered by components outside the system server when they are fully functional after an
+     * update.
+     */
+    private void onHealthCheckPassed(String packageName) {
+        Slog.i(TAG, "Health check passed for package: " + packageName);
+        boolean isStateChanged = false;
+
+        synchronized (mLock) {
+            for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) {
+                ObserverInternal observer = mAllObservers.valueAt(observerIdx);
+                MonitoredPackage monitoredPackage = observer.getMonitoredPackage(packageName);
+
+                if (monitoredPackage != null) {
+                    int oldState = monitoredPackage.getHealthCheckStateLocked();
+                    int newState = monitoredPackage.tryPassHealthCheckLocked();
+                    isStateChanged |= oldState != newState;
+                }
+            }
+        }
+
+        if (isStateChanged) {
+            syncState("health check passed for " + packageName);
+        }
+    }
+
+    private void onSupportedPackages(List<PackageConfig> supportedPackages) {
+        boolean isStateChanged = false;
+
+        Map<String, Long> supportedPackageTimeouts = new ArrayMap<>();
+        Iterator<PackageConfig> it = supportedPackages.iterator();
+        while (it.hasNext()) {
+            PackageConfig info = it.next();
+            supportedPackageTimeouts.put(info.getPackageName(), info.getHealthCheckTimeoutMillis());
+        }
+
+        synchronized (mLock) {
+            Slog.d(TAG, "Received supported packages " + supportedPackages);
+            Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
+            while (oit.hasNext()) {
+                Iterator<MonitoredPackage> pit = oit.next().getMonitoredPackages()
+                        .values().iterator();
+                while (pit.hasNext()) {
+                    MonitoredPackage monitoredPackage = pit.next();
+                    String packageName = monitoredPackage.getName();
+                    int oldState = monitoredPackage.getHealthCheckStateLocked();
+                    int newState;
+
+                    if (supportedPackageTimeouts.containsKey(packageName)) {
+                        // Supported packages become ACTIVE if currently INACTIVE
+                        newState = monitoredPackage.setHealthCheckActiveLocked(
+                                supportedPackageTimeouts.get(packageName));
+                    } else {
+                        // Unsupported packages are marked as PASSED unless already FAILED
+                        newState = monitoredPackage.tryPassHealthCheckLocked();
+                    }
+                    isStateChanged |= oldState != newState;
+                }
+            }
+        }
+
+        if (isStateChanged) {
+            syncState("updated health check supported packages " + supportedPackages);
+        }
+    }
+
+    private void onSyncRequestNotified() {
+        synchronized (mLock) {
+            mSyncRequired = true;
+            syncRequestsAsync();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private Set<String> getPackagesPendingHealthChecksLocked() {
+        Set<String> packages = new ArraySet<>();
+        Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
+        while (oit.hasNext()) {
+            ObserverInternal observer = oit.next();
+            Iterator<MonitoredPackage> pit =
+                    observer.getMonitoredPackages().values().iterator();
+            while (pit.hasNext()) {
+                MonitoredPackage monitoredPackage = pit.next();
+                String packageName = monitoredPackage.getName();
+                if (monitoredPackage.isPendingHealthChecksLocked()) {
+                    packages.add(packageName);
+                }
+            }
+        }
+        return packages;
+    }
+
+    /**
+     * Syncs the state of the observers.
+     *
+     * <p> Prunes all observers, saves new state to disk, syncs health check requests with the
+     * health check service and schedules the next state sync.
+     */
+    private void syncState(String reason) {
+        synchronized (mLock) {
+            Slog.i(TAG, "Syncing state, reason: " + reason);
+            pruneObserversLocked();
+
+            saveToFileAsync();
+            syncRequestsAsync();
+
+            // Done syncing state, schedule the next state sync
+            scheduleNextSyncStateLocked();
+        }
+    }
+
+    private void syncStateWithScheduledReason() {
+        syncState("scheduled");
+    }
+
+    @GuardedBy("mLock")
+    private void scheduleNextSyncStateLocked() {
+        long durationMs = getNextStateSyncMillisLocked();
+        mShortTaskHandler.removeCallbacks(mSyncStateWithScheduledReason);
+        if (durationMs == Long.MAX_VALUE) {
+            Slog.i(TAG, "Cancelling state sync, nothing to sync");
+            mUptimeAtLastStateSync = 0;
+        } else {
+            mUptimeAtLastStateSync = mSystemClock.uptimeMillis();
+            mShortTaskHandler.postDelayed(mSyncStateWithScheduledReason, durationMs);
+        }
+    }
+
+    /**
+     * Returns the next duration in millis to sync the watchdog state.
+     *
+     * @returns Long#MAX_VALUE if there are no observed packages.
+     */
+    @GuardedBy("mLock")
+    private long getNextStateSyncMillisLocked() {
+        long shortestDurationMs = Long.MAX_VALUE;
+        for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+            ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex)
+                    .getMonitoredPackages();
+            for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                MonitoredPackage mp = packages.valueAt(pIndex);
+                long duration = mp.getShortestScheduleDurationMsLocked();
+                if (duration < shortestDurationMs) {
+                    shortestDurationMs = duration;
+                }
+            }
+        }
+        return shortestDurationMs;
+    }
+
+    /**
+     * Removes {@code elapsedMs} milliseconds from all durations on monitored packages
+     * and updates other internal state.
+     */
+    @GuardedBy("mLock")
+    private void pruneObserversLocked() {
+        long elapsedMs = mUptimeAtLastStateSync == 0
+                ? 0 : mSystemClock.uptimeMillis() - mUptimeAtLastStateSync;
+        if (elapsedMs <= 0) {
+            Slog.i(TAG, "Not pruning observers, elapsed time: " + elapsedMs + "ms");
+            return;
+        }
+
+        Iterator<ObserverInternal> it = mAllObservers.values().iterator();
+        while (it.hasNext()) {
+            ObserverInternal observer = it.next();
+            Set<MonitoredPackage> failedPackages =
+                    observer.prunePackagesLocked(elapsedMs);
+            if (!failedPackages.isEmpty()) {
+                onHealthCheckFailed(observer, failedPackages);
+            }
+            if (observer.getMonitoredPackages().isEmpty() && (observer.registeredObserver == null
+                    || !observer.registeredObserver.isPersistent())) {
+                Slog.i(TAG, "Discarding observer " + observer.name + ". All packages expired");
+                it.remove();
+            }
+        }
+    }
+
+    private void onHealthCheckFailed(ObserverInternal observer,
+            Set<MonitoredPackage> failedPackages) {
+        mLongTaskHandler.post(() -> {
+            synchronized (mLock) {
+                PackageHealthObserver registeredObserver = observer.registeredObserver;
+                if (registeredObserver != null) {
+                    Iterator<MonitoredPackage> it = failedPackages.iterator();
+                    while (it.hasNext()) {
+                        VersionedPackage versionedPkg = getVersionedPackage(it.next().getName());
+                        if (versionedPkg != null) {
+                            Slog.i(TAG,
+                                    "Explicit health check failed for package " + versionedPkg);
+                            registeredObserver.execute(versionedPkg,
+                                    PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Gets PackageInfo for the given package. Matches any user and apex.
+     *
+     * @throws PackageManager.NameNotFoundException if no such package is installed.
+     */
+    private PackageInfo getPackageInfo(String packageName)
+            throws PackageManager.NameNotFoundException {
+        PackageManager pm = mContext.getPackageManager();
+        try {
+            // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX
+            // flag, so make two separate attempts to get the package info.
+            // We don't need both flags at the same time because we assume
+            // apex files are always installed for all users.
+            return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER);
+        } catch (PackageManager.NameNotFoundException e) {
+            return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
+        }
+    }
+
+    @Nullable
+    private VersionedPackage getVersionedPackage(String packageName) {
+        final PackageManager pm = mContext.getPackageManager();
+        if (pm == null || TextUtils.isEmpty(packageName)) {
+            return null;
+        }
+        try {
+            final long versionCode = getPackageInfo(packageName).getLongVersionCode();
+            return new VersionedPackage(packageName, versionCode);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Loads mAllObservers from file.
+     *
+     * <p>Note that this is <b>not</b> thread safe and should only called be called
+     * from the constructor.
+     */
+    private void loadFromFile() {
+        InputStream infile = null;
+        mAllObservers.clear();
+        try {
+            infile = mPolicyFile.openRead();
+            final TypedXmlPullParser parser = Xml.resolvePullParser(infile);
+            XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG);
+            int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                ObserverInternal observer = ObserverInternal.read(parser, this);
+                if (observer != null) {
+                    mAllObservers.put(observer.name, observer);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Nothing to monitor
+        } catch (IOException | NumberFormatException | XmlPullParserException e) {
+            Slog.wtf(TAG, "Unable to read monitored packages, deleting file", e);
+            mPolicyFile.delete();
+        } finally {
+            IoUtils.closeQuietly(infile);
+        }
+    }
+
+    private void onPropertyChanged(DeviceConfig.Properties properties) {
+        try {
+            updateConfigs();
+        } catch (Exception ignore) {
+            Slog.w(TAG, "Failed to reload device config changes");
+        }
+    }
+
+    /** Adds a {@link DeviceConfig#OnPropertiesChangedListener}. */
+    private void setPropertyChangedListenerLocked() {
+        DeviceConfig.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_ROLLBACK,
+                mContext.getMainExecutor(),
+                mOnPropertyChangedListener);
+    }
+
+    @VisibleForTesting
+    void removePropertyChangedListener() {
+        DeviceConfig.removeOnPropertiesChangedListener(mOnPropertyChangedListener);
+    }
+
+    /**
+     * Health check is enabled or disabled after reading the flags
+     * from DeviceConfig.
+     */
+    @VisibleForTesting
+    void updateConfigs() {
+        synchronized (mLock) {
+            mTriggerFailureCount = DeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_ROLLBACK,
+                    PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+                    DEFAULT_TRIGGER_FAILURE_COUNT);
+            if (mTriggerFailureCount <= 0) {
+                mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT;
+            }
+
+            mTriggerFailureDurationMs = DeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_ROLLBACK,
+                    PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS,
+                    DEFAULT_TRIGGER_FAILURE_DURATION_MS);
+            if (mTriggerFailureDurationMs <= 0) {
+                mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS;
+            }
+
+            setExplicitHealthCheckEnabled(DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_ROLLBACK,
+                    PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED,
+                    DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED));
+        }
+    }
+
+    /**
+     * Persists mAllObservers to file. Threshold information is ignored.
+     */
+    private boolean saveToFile() {
+        Slog.i(TAG, "Saving observer state to file");
+        synchronized (mLock) {
+            FileOutputStream stream;
+            try {
+                stream = mPolicyFile.startWrite();
+            } catch (IOException e) {
+                Slog.w(TAG, "Cannot update monitored packages", e);
+                return false;
+            }
+
+            try {
+                TypedXmlSerializer out = Xml.resolveSerializer(stream);
+                out.startDocument(null, true);
+                out.startTag(null, TAG_PACKAGE_WATCHDOG);
+                out.attributeInt(null, ATTR_VERSION, DB_VERSION);
+                for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+                    mAllObservers.valueAt(oIndex).writeLocked(out);
+                }
+                out.endTag(null, TAG_PACKAGE_WATCHDOG);
+                out.endDocument();
+                mPolicyFile.finishWrite(stream);
+                return true;
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to save monitored packages, restoring backup", e);
+                mPolicyFile.failWrite(stream);
+                return false;
+            } finally {
+                IoUtils.closeQuietly(stream);
+            }
+        }
+    }
+
+    private void saveToFileAsync() {
+        if (!mLongTaskHandler.hasCallbacks(mSaveToFile)) {
+            mLongTaskHandler.post(mSaveToFile);
+        }
+    }
+
+    /** @hide Convert a {@code LongArrayQueue} to a String of comma-separated values. */
+    public static String longArrayQueueToString(LongArrayQueue queue) {
+        if (queue.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(queue.get(0));
+            for (int i = 1; i < queue.size(); i++) {
+                sb.append(",");
+                sb.append(queue.get(i));
+            }
+            return sb.toString();
+        }
+        return "";
+    }
+
+    /** @hide Parse a comma-separated String of longs into a LongArrayQueue. */
+    public static LongArrayQueue parseLongArrayQueue(String commaSeparatedValues) {
+        LongArrayQueue result = new LongArrayQueue();
+        if (!TextUtils.isEmpty(commaSeparatedValues)) {
+            String[] values = commaSeparatedValues.split(",");
+            for (String value : values) {
+                result.addLast(Long.parseLong(value));
+            }
+        }
+        return result;
+    }
+
+
+    /** Dump status of every observer in mAllObservers. */
+    public void dump(@NonNull PrintWriter pw) {
+        if (Flags.synchronousRebootInRescueParty() && RescueParty.isRecoveryTriggeredReboot()) {
+            dumpInternal(pw);
+        } else {
+            synchronized (mLock) {
+                dumpInternal(pw);
+            }
+        }
+    }
+
+    private void dumpInternal(@NonNull PrintWriter pw) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println("Package Watchdog status");
+        ipw.increaseIndent();
+        synchronized (mLock) {
+            for (String observerName : mAllObservers.keySet()) {
+                ipw.println("Observer name: " + observerName);
+                ipw.increaseIndent();
+                ObserverInternal observerInternal = mAllObservers.get(observerName);
+                observerInternal.dump(ipw);
+                ipw.decreaseIndent();
+            }
+        }
+        ipw.decreaseIndent();
+        dumpCrashRecoveryEvents(ipw);
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    void registerObserverInternal(ObserverInternal observerInternal) {
+        mAllObservers.put(observerInternal.name, observerInternal);
+    }
+
+    /**
+     * Represents an observer monitoring a set of packages along with the failure thresholds for
+     * each package.
+     *
+     * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing
+     * instances of this class.
+     */
+    static class ObserverInternal {
+        public final String name;
+        @GuardedBy("mLock")
+        private final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>();
+        @Nullable
+        @GuardedBy("mLock")
+        public PackageHealthObserver registeredObserver;
+        private int mMitigationCount;
+
+        ObserverInternal(String name, List<MonitoredPackage> packages) {
+            this(name, packages, /*mitigationCount=*/ 0);
+        }
+
+        ObserverInternal(String name, List<MonitoredPackage> packages, int mitigationCount) {
+            this.name = name;
+            updatePackagesLocked(packages);
+            this.mMitigationCount = mitigationCount;
+        }
+
+        /**
+         * Writes important {@link MonitoredPackage} details for this observer to file.
+         * Does not persist any package failure thresholds.
+         */
+        @GuardedBy("mLock")
+        public boolean writeLocked(TypedXmlSerializer out) {
+            try {
+                out.startTag(null, TAG_OBSERVER);
+                out.attribute(null, ATTR_NAME, name);
+                if (Flags.recoverabilityDetection()) {
+                    out.attributeInt(null, ATTR_MITIGATION_COUNT, mMitigationCount);
+                }
+                for (int i = 0; i < mPackages.size(); i++) {
+                    MonitoredPackage p = mPackages.valueAt(i);
+                    p.writeLocked(out);
+                }
+                out.endTag(null, TAG_OBSERVER);
+                return true;
+            } catch (IOException e) {
+                Slog.w(TAG, "Cannot save observer", e);
+                return false;
+            }
+        }
+
+        public int getBootMitigationCount() {
+            return mMitigationCount;
+        }
+
+        public void setBootMitigationCount(int mitigationCount) {
+            mMitigationCount = mitigationCount;
+        }
+
+        @GuardedBy("mLock")
+        public void updatePackagesLocked(List<MonitoredPackage> packages) {
+            for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                MonitoredPackage p = packages.get(pIndex);
+                MonitoredPackage existingPackage = getMonitoredPackage(p.getName());
+                if (existingPackage != null) {
+                    existingPackage.updateHealthCheckDuration(p.mDurationMs);
+                } else {
+                    putMonitoredPackage(p);
+                }
+            }
+        }
+
+        /**
+         * Reduces the monitoring durations of all packages observed by this observer by
+         * {@code elapsedMs}. If any duration is less than 0, the package is removed from
+         * observation. If any health check duration is less than 0, the health check result
+         * is evaluated.
+         *
+         * @return a {@link Set} of packages that were removed from the observer without explicit
+         * health check passing, or an empty list if no package expired for which an explicit health
+         * check was still pending
+         */
+        @GuardedBy("mLock")
+        private Set<MonitoredPackage> prunePackagesLocked(long elapsedMs) {
+            Set<MonitoredPackage> failedPackages = new ArraySet<>();
+            Iterator<MonitoredPackage> it = mPackages.values().iterator();
+            while (it.hasNext()) {
+                MonitoredPackage p = it.next();
+                int oldState = p.getHealthCheckStateLocked();
+                int newState = p.handleElapsedTimeLocked(elapsedMs);
+                if (oldState != HealthCheckState.FAILED
+                        && newState == HealthCheckState.FAILED) {
+                    Slog.i(TAG, "Package " + p.getName() + " failed health check");
+                    failedPackages.add(p);
+                }
+                if (p.isExpiredLocked()) {
+                    it.remove();
+                }
+            }
+            return failedPackages;
+        }
+
+        /**
+         * Increments failure counts of {@code packageName}.
+         * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise
+         * @hide
+         */
+        @GuardedBy("mLock")
+        public boolean onPackageFailureLocked(String packageName) {
+            if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent()
+                    && registeredObserver.mayObservePackage(packageName)) {
+                putMonitoredPackage(sPackageWatchdog.newMonitoredPackage(
+                        packageName, DEFAULT_OBSERVING_DURATION_MS, false));
+            }
+            MonitoredPackage p = getMonitoredPackage(packageName);
+            if (p != null) {
+                return p.onFailureLocked();
+            }
+            return false;
+        }
+
+        /**
+         * Returns the map of packages monitored by this observer.
+         *
+         * @return a mapping of package names to {@link MonitoredPackage} objects.
+         */
+        @GuardedBy("mLock")
+        public ArrayMap<String, MonitoredPackage> getMonitoredPackages() {
+            return mPackages;
+        }
+
+        /**
+         * Returns the {@link MonitoredPackage} associated with a given package name if the
+         * package is being monitored by this observer.
+         *
+         * @param packageName: the name of the package.
+         * @return the {@link MonitoredPackage} object associated with the package name if one
+         *         exists, {@code null} otherwise.
+         */
+        @GuardedBy("mLock")
+        @Nullable
+        public MonitoredPackage getMonitoredPackage(String packageName) {
+            return mPackages.get(packageName);
+        }
+
+        /**
+         * Associates a {@link MonitoredPackage} with the observer.
+         *
+         * @param p: the {@link MonitoredPackage} to store.
+         */
+        @GuardedBy("mLock")
+        public void putMonitoredPackage(MonitoredPackage p) {
+            mPackages.put(p.getName(), p);
+        }
+
+        /**
+         * Returns one ObserverInternal from the {@code parser} and advances its state.
+         *
+         * <p>Note that this method is <b>not</b> thread safe. It should only be called from
+         * #loadFromFile which in turn is only called on construction of the
+         * singleton PackageWatchdog.
+         **/
+        public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) {
+            String observerName = null;
+            int observerMitigationCount = 0;
+            if (TAG_OBSERVER.equals(parser.getName())) {
+                observerName = parser.getAttributeValue(null, ATTR_NAME);
+                if (TextUtils.isEmpty(observerName)) {
+                    Slog.wtf(TAG, "Unable to read observer name");
+                    return null;
+                }
+            }
+            List<MonitoredPackage> packages = new ArrayList<>();
+            int innerDepth = parser.getDepth();
+            try {
+                if (Flags.recoverabilityDetection()) {
+                    try {
+                        observerMitigationCount =
+                                parser.getAttributeInt(null, ATTR_MITIGATION_COUNT);
+                    } catch (XmlPullParserException e) {
+                        Slog.i(
+                            TAG,
+                            "ObserverInternal mitigation count was not present.");
+                    }
+                }
+                while (XmlUtils.nextElementWithin(parser, innerDepth)) {
+                    if (TAG_PACKAGE.equals(parser.getName())) {
+                        try {
+                            MonitoredPackage pkg = watchdog.parseMonitoredPackage(parser);
+                            if (pkg != null) {
+                                packages.add(pkg);
+                            }
+                        } catch (NumberFormatException e) {
+                            Slog.wtf(TAG, "Skipping package for observer " + observerName, e);
+                            continue;
+                        }
+                    }
+                }
+            } catch (XmlPullParserException | IOException e) {
+                Slog.wtf(TAG, "Unable to read observer " + observerName, e);
+                return null;
+            }
+            if (packages.isEmpty()) {
+                return null;
+            }
+            return new ObserverInternal(observerName, packages, observerMitigationCount);
+        }
+
+        /** Dumps information about this observer and the packages it watches. */
+        public void dump(IndentingPrintWriter pw) {
+            boolean isPersistent = registeredObserver != null && registeredObserver.isPersistent();
+            pw.println("Persistent: " + isPersistent);
+            for (String packageName : mPackages.keySet()) {
+                MonitoredPackage p = getMonitoredPackage(packageName);
+                pw.println(packageName +  ": ");
+                pw.increaseIndent();
+                pw.println("# Failures: " + p.mFailureHistory.size());
+                pw.println("Monitoring duration remaining: " + p.mDurationMs + "ms");
+                pw.println("Explicit health check duration: " + p.mHealthCheckDurationMs + "ms");
+                pw.println("Health check state: " + p.toString(p.mHealthCheckState));
+                pw.decreaseIndent();
+            }
+        }
+    }
+
+    /** @hide */
+    @Retention(SOURCE)
+    @IntDef(value = {
+            HealthCheckState.ACTIVE,
+            HealthCheckState.INACTIVE,
+            HealthCheckState.PASSED,
+            HealthCheckState.FAILED})
+    public @interface HealthCheckState {
+        // The package has not passed health check but has requested a health check
+        int ACTIVE = 0;
+        // The package has not passed health check and has not requested a health check
+        int INACTIVE = 1;
+        // The package has passed health check
+        int PASSED = 2;
+        // The package has failed health check
+        int FAILED = 3;
+    }
+
+    MonitoredPackage newMonitoredPackage(
+            String name, long durationMs, boolean hasPassedHealthCheck) {
+        return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck,
+                new LongArrayQueue());
+    }
+
+    MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
+            boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) {
+        return new MonitoredPackage(name, durationMs, healthCheckDurationMs,
+                hasPassedHealthCheck, mitigationCalls);
+    }
+
+    MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser)
+            throws XmlPullParserException {
+        String packageName = parser.getAttributeValue(null, ATTR_NAME);
+        long duration = parser.getAttributeLong(null, ATTR_DURATION);
+        long healthCheckDuration = parser.getAttributeLong(null,
+                        ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
+        boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK);
+        LongArrayQueue mitigationCalls = parseLongArrayQueue(
+                parser.getAttributeValue(null, ATTR_MITIGATION_CALLS));
+        return newMonitoredPackage(packageName,
+                duration, healthCheckDuration, hasPassedHealthCheck, mitigationCalls);
+    }
+
+    /**
+     * Represents a package and its health check state along with the time
+     * it should be monitored for.
+     *
+     * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing
+     * instances of this class.
+     */
+    class MonitoredPackage {
+        private final String mPackageName;
+        // Times when package failures happen sorted in ascending order
+        @GuardedBy("mLock")
+        private final LongArrayQueue mFailureHistory = new LongArrayQueue();
+        // Times when an observer was called to mitigate this package's failure. Sorted in
+        // ascending order.
+        @GuardedBy("mLock")
+        private final LongArrayQueue mMitigationCalls;
+        // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after
+        // methods that could change the health check state: handleElapsedTimeLocked and
+        // tryPassHealthCheckLocked
+        private int mHealthCheckState = HealthCheckState.INACTIVE;
+        // Whether an explicit health check has passed.
+        // This value in addition with mHealthCheckDurationMs determines the health check state
+        // of the package, see #getHealthCheckStateLocked
+        @GuardedBy("mLock")
+        private boolean mHasPassedHealthCheck;
+        // System uptime duration to monitor package.
+        @GuardedBy("mLock")
+        private long mDurationMs;
+        // System uptime duration to check the result of an explicit health check
+        // Initially, MAX_VALUE until we get a value from the health check service
+        // and request health checks.
+        // This value in addition with mHasPassedHealthCheck determines the health check state
+        // of the package, see #getHealthCheckStateLocked
+        @GuardedBy("mLock")
+        private long mHealthCheckDurationMs = Long.MAX_VALUE;
+
+        MonitoredPackage(String packageName, long durationMs,
+                long healthCheckDurationMs, boolean hasPassedHealthCheck,
+                LongArrayQueue mitigationCalls) {
+            mPackageName = packageName;
+            mDurationMs = durationMs;
+            mHealthCheckDurationMs = healthCheckDurationMs;
+            mHasPassedHealthCheck = hasPassedHealthCheck;
+            mMitigationCalls = mitigationCalls;
+            updateHealthCheckStateLocked();
+        }
+
+        /** Writes the salient fields to disk using {@code out}.
+         * @hide
+         */
+        @GuardedBy("mLock")
+        public void writeLocked(TypedXmlSerializer out) throws IOException {
+            out.startTag(null, TAG_PACKAGE);
+            out.attribute(null, ATTR_NAME, getName());
+            out.attributeLong(null, ATTR_DURATION, mDurationMs);
+            out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs);
+            out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck);
+            LongArrayQueue normalizedCalls = normalizeMitigationCalls();
+            out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls));
+            out.endTag(null, TAG_PACKAGE);
+        }
+
+        /**
+         * Increment package failures or resets failure count depending on the last package failure.
+         *
+         * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise
+         */
+        @GuardedBy("mLock")
+        public boolean onFailureLocked() {
+            // Sliding window algorithm: find out if there exists a window containing failures >=
+            // mTriggerFailureCount.
+            final long now = mSystemClock.uptimeMillis();
+            mFailureHistory.addLast(now);
+            while (now - mFailureHistory.peekFirst() > mTriggerFailureDurationMs) {
+                // Prune values falling out of the window
+                mFailureHistory.removeFirst();
+            }
+            boolean failed = mFailureHistory.size() >= mTriggerFailureCount;
+            if (failed) {
+                mFailureHistory.clear();
+            }
+            return failed;
+        }
+
+        /**
+         * Notes the timestamp of a mitigation call into the observer.
+         */
+        @GuardedBy("mLock")
+        public void noteMitigationCallLocked() {
+            mMitigationCalls.addLast(mSystemClock.uptimeMillis());
+        }
+
+        /**
+         * Prunes any mitigation calls outside of the de-escalation window, and returns the
+         * number of calls that are in the window afterwards.
+         *
+         * @return the number of mitigation calls made in the de-escalation window.
+         */
+        @GuardedBy("mLock")
+        public int getMitigationCountLocked() {
+            try {
+                final long now = mSystemClock.uptimeMillis();
+                while (now - mMitigationCalls.peekFirst() > DEFAULT_DEESCALATION_WINDOW_MS) {
+                    mMitigationCalls.removeFirst();
+                }
+            } catch (NoSuchElementException ignore) {
+            }
+
+            return mMitigationCalls.size();
+        }
+
+        /**
+         * Before writing to disk, make the mitigation call timestamps relative to the current
+         * system uptime. This is because they need to be relative to the uptime which will reset
+         * at the next boot.
+         *
+         * @return a LongArrayQueue of the mitigation calls relative to the current system uptime.
+         */
+        @GuardedBy("mLock")
+        public LongArrayQueue normalizeMitigationCalls() {
+            LongArrayQueue normalized = new LongArrayQueue();
+            final long now = mSystemClock.uptimeMillis();
+            for (int i = 0; i < mMitigationCalls.size(); i++) {
+                normalized.addLast(mMitigationCalls.get(i) - now);
+            }
+            return normalized;
+        }
+
+        /**
+         * Sets the initial health check duration.
+         *
+         * @return the new health check state
+         */
+        @GuardedBy("mLock")
+        public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) {
+            if (initialHealthCheckDurationMs <= 0) {
+                Slog.wtf(TAG, "Cannot set non-positive health check duration "
+                        + initialHealthCheckDurationMs + "ms for package " + getName()
+                        + ". Using total duration " + mDurationMs + "ms instead");
+                initialHealthCheckDurationMs = mDurationMs;
+            }
+            if (mHealthCheckState == HealthCheckState.INACTIVE) {
+                // Transitions to ACTIVE
+                mHealthCheckDurationMs = initialHealthCheckDurationMs;
+            }
+            return updateHealthCheckStateLocked();
+        }
+
+        /**
+         * Updates the monitoring durations of the package.
+         *
+         * @return the new health check state
+         */
+        @GuardedBy("mLock")
+        public int handleElapsedTimeLocked(long elapsedMs) {
+            if (elapsedMs <= 0) {
+                Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + getName());
+                return mHealthCheckState;
+            }
+            // Transitions to FAILED if now <= 0 and health check not passed
+            mDurationMs -= elapsedMs;
+            if (mHealthCheckState == HealthCheckState.ACTIVE) {
+                // We only update health check durations if we have #setHealthCheckActiveLocked
+                // This ensures we don't leave the INACTIVE state for an unexpected elapsed time
+                // Transitions to FAILED if now <= 0 and health check not passed
+                mHealthCheckDurationMs -= elapsedMs;
+            }
+            return updateHealthCheckStateLocked();
+        }
+
+        /** Explicitly update the monitoring duration of the package. */
+        @GuardedBy("mLock")
+        public void updateHealthCheckDuration(long newDurationMs) {
+            mDurationMs = newDurationMs;
+        }
+
+        /**
+         * Marks the health check as passed and transitions to {@link HealthCheckState.PASSED}
+         * if not yet {@link HealthCheckState.FAILED}.
+         *
+         * @return the new {@link HealthCheckState health check state}
+         */
+        @GuardedBy("mLock")
+        @HealthCheckState
+        public int tryPassHealthCheckLocked() {
+            if (mHealthCheckState != HealthCheckState.FAILED) {
+                // FAILED is a final state so only pass if we haven't failed
+                // Transition to PASSED
+                mHasPassedHealthCheck = true;
+            }
+            return updateHealthCheckStateLocked();
+        }
+
+        /** Returns the monitored package name. */
+        private String getName() {
+            return mPackageName;
+        }
+
+        /**
+         * Returns the current {@link HealthCheckState health check state}.
+         */
+        @GuardedBy("mLock")
+        @HealthCheckState
+        public int getHealthCheckStateLocked() {
+            return mHealthCheckState;
+        }
+
+        /**
+         * Returns the shortest duration before the package should be scheduled for a prune.
+         *
+         * @return the duration or {@link Long#MAX_VALUE} if the package should not be scheduled
+         */
+        @GuardedBy("mLock")
+        public long getShortestScheduleDurationMsLocked() {
+            // Consider health check duration only if #isPendingHealthChecksLocked is true
+            return Math.min(toPositive(mDurationMs),
+                    isPendingHealthChecksLocked()
+                    ? toPositive(mHealthCheckDurationMs) : Long.MAX_VALUE);
+        }
+
+        /**
+         * Returns {@code true} if the total duration left to monitor the package is less than or
+         * equal to 0 {@code false} otherwise.
+         */
+        @GuardedBy("mLock")
+        public boolean isExpiredLocked() {
+            return mDurationMs <= 0;
+        }
+
+        /**
+         * Returns {@code true} if the package, {@link #getName} is expecting health check results
+         * {@code false} otherwise.
+         */
+        @GuardedBy("mLock")
+        public boolean isPendingHealthChecksLocked() {
+            return mHealthCheckState == HealthCheckState.ACTIVE
+                    || mHealthCheckState == HealthCheckState.INACTIVE;
+        }
+
+        /**
+         * Updates the health check state based on {@link #mHasPassedHealthCheck}
+         * and {@link #mHealthCheckDurationMs}.
+         *
+         * @return the new {@link HealthCheckState health check state}
+         */
+        @GuardedBy("mLock")
+        @HealthCheckState
+        private int updateHealthCheckStateLocked() {
+            int oldState = mHealthCheckState;
+            if (mHasPassedHealthCheck) {
+                // Set final state first to avoid ambiguity
+                mHealthCheckState = HealthCheckState.PASSED;
+            } else if (mHealthCheckDurationMs <= 0 || mDurationMs <= 0) {
+                // Set final state first to avoid ambiguity
+                mHealthCheckState = HealthCheckState.FAILED;
+            } else if (mHealthCheckDurationMs == Long.MAX_VALUE) {
+                mHealthCheckState = HealthCheckState.INACTIVE;
+            } else {
+                mHealthCheckState = HealthCheckState.ACTIVE;
+            }
+
+            if (oldState != mHealthCheckState) {
+                Slog.i(TAG, "Updated health check state for package " + getName() + ": "
+                        + toString(oldState) + " -> " + toString(mHealthCheckState));
+            }
+            return mHealthCheckState;
+        }
+
+        /** Returns a {@link String} representation of the current health check state. */
+        private String toString(@HealthCheckState int state) {
+            switch (state) {
+                case HealthCheckState.ACTIVE:
+                    return "ACTIVE";
+                case HealthCheckState.INACTIVE:
+                    return "INACTIVE";
+                case HealthCheckState.PASSED:
+                    return "PASSED";
+                case HealthCheckState.FAILED:
+                    return "FAILED";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        /** Returns {@code value} if it is greater than 0 or {@link Long#MAX_VALUE} otherwise. */
+        private long toPositive(long value) {
+            return value > 0 ? value : Long.MAX_VALUE;
+        }
+
+        /** Compares the equality of this object with another {@link MonitoredPackage}. */
+        @VisibleForTesting
+        boolean isEqualTo(MonitoredPackage pkg) {
+            return (getName().equals(pkg.getName()))
+                    && mDurationMs == pkg.mDurationMs
+                    && mHasPassedHealthCheck == pkg.mHasPassedHealthCheck
+                    && mHealthCheckDurationMs == pkg.mHealthCheckDurationMs
+                    && (mMitigationCalls.toString()).equals(pkg.mMitigationCalls.toString());
+        }
+    }
+
+    @GuardedBy("mLock")
+    @SuppressWarnings("GuardedBy")
+    void saveAllObserversBootMitigationCountToMetadata(String filePath) {
+        HashMap<String, Integer> bootMitigationCounts = new HashMap<>();
+        for (int i = 0; i < mAllObservers.size(); i++) {
+            final ObserverInternal observer = mAllObservers.valueAt(i);
+            bootMitigationCounts.put(observer.name, observer.getBootMitigationCount());
+        }
+
+        try {
+            FileOutputStream fileStream = new FileOutputStream(new File(filePath));
+            ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
+            objectStream.writeObject(bootMitigationCounts);
+            objectStream.flush();
+            objectStream.close();
+            fileStream.close();
+        } catch (Exception e) {
+            Slog.i(TAG, "Could not save observers metadata to file: " + e);
+        }
+    }
+
+    /**
+     * Handles the thresholding logic for system server boots.
+     */
+    class BootThreshold {
+
+        private final int mBootTriggerCount;
+        private final long mTriggerWindow;
+
+        BootThreshold(int bootTriggerCount, long triggerWindow) {
+            this.mBootTriggerCount = bootTriggerCount;
+            this.mTriggerWindow = triggerWindow;
+        }
+
+        public void reset() {
+            setStart(0);
+            setCount(0);
+        }
+
+        protected int getCount() {
+            return CrashRecoveryProperties.rescueBootCount().orElse(0);
+        }
+
+        protected void setCount(int count) {
+            CrashRecoveryProperties.rescueBootCount(count);
+        }
+
+        public long getStart() {
+            return CrashRecoveryProperties.rescueBootStart().orElse(0L);
+        }
+
+        public int getMitigationCount() {
+            return CrashRecoveryProperties.bootMitigationCount().orElse(0);
+        }
+
+        public void setStart(long start) {
+            CrashRecoveryProperties.rescueBootStart(getStartTime(start));
+        }
+
+        public void setMitigationStart(long start) {
+            CrashRecoveryProperties.bootMitigationStart(getStartTime(start));
+        }
+
+        public long getMitigationStart() {
+            return CrashRecoveryProperties.bootMitigationStart().orElse(0L);
+        }
+
+        public void setMitigationCount(int count) {
+            CrashRecoveryProperties.bootMitigationCount(count);
+        }
+
+        private static long constrain(long amount, long low, long high) {
+            return amount < low ? low : (amount > high ? high : amount);
+        }
+
+        public long getStartTime(long start) {
+            final long now = mSystemClock.uptimeMillis();
+            return constrain(start, 0, now);
+        }
+
+        public void saveMitigationCountToMetadata() {
+            try (BufferedWriter writer = new BufferedWriter(new FileWriter(METADATA_FILE))) {
+                writer.write(String.valueOf(getMitigationCount()));
+            } catch (Exception e) {
+                Slog.e(TAG, "Could not save metadata to file: " + e);
+            }
+        }
+
+        public void readMitigationCountFromMetadataIfNecessary() {
+            File bootPropsFile = new File(METADATA_FILE);
+            if (bootPropsFile.exists()) {
+                try (BufferedReader reader = new BufferedReader(new FileReader(METADATA_FILE))) {
+                    String mitigationCount = reader.readLine();
+                    setMitigationCount(Integer.parseInt(mitigationCount));
+                    bootPropsFile.delete();
+                } catch (Exception e) {
+                    Slog.i(TAG, "Could not read metadata file: " + e);
+                }
+            }
+        }
+
+
+        /** Increments the boot counter, and returns whether the device is bootlooping. */
+        @GuardedBy("mLock")
+        public boolean incrementAndTest() {
+            if (Flags.recoverabilityDetection()) {
+                readAllObserversBootMitigationCountIfNecessary(METADATA_FILE);
+            } else {
+                readMitigationCountFromMetadataIfNecessary();
+            }
+
+            final long now = mSystemClock.uptimeMillis();
+            if (now - getStart() < 0) {
+                Slog.e(TAG, "Window was less than zero. Resetting start to current time.");
+                setStart(now);
+                setMitigationStart(now);
+            }
+            if (now - getMitigationStart() > DEFAULT_DEESCALATION_WINDOW_MS) {
+                setMitigationStart(now);
+                if (Flags.recoverabilityDetection()) {
+                    resetAllObserversBootMitigationCount();
+                } else {
+                    setMitigationCount(0);
+                }
+            }
+            final long window = now - getStart();
+            if (window >= mTriggerWindow) {
+                setCount(1);
+                setStart(now);
+                return false;
+            } else {
+                int count = getCount() + 1;
+                setCount(count);
+                EventLog.writeEvent(LOG_TAG_RESCUE_NOTE, Process.ROOT_UID, count, window);
+                if (Flags.recoverabilityDetection()) {
+                    // After a reboot (e.g. by WARM_REBOOT or mainline rollback) we apply
+                    // mitigations without waiting for DEFAULT_BOOT_LOOP_TRIGGER_COUNT.
+                    return (count >= mBootTriggerCount)
+                            || (performedMitigationsDuringWindow() && count > 1);
+                }
+                return count >= mBootTriggerCount;
+            }
+        }
+
+        @GuardedBy("mLock")
+        private boolean performedMitigationsDuringWindow() {
+            for (ObserverInternal observerInternal: mAllObservers.values()) {
+                if (observerInternal.getBootMitigationCount() > 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @GuardedBy("mLock")
+        private void resetAllObserversBootMitigationCount() {
+            for (int i = 0; i < mAllObservers.size(); i++) {
+                final ObserverInternal observer = mAllObservers.valueAt(i);
+                observer.setBootMitigationCount(0);
+            }
+            saveAllObserversBootMitigationCountToMetadata(METADATA_FILE);
+        }
+
+        @GuardedBy("mLock")
+        @SuppressWarnings("GuardedBy")
+        void readAllObserversBootMitigationCountIfNecessary(String filePath) {
+            File metadataFile = new File(filePath);
+            if (metadataFile.exists()) {
+                try {
+                    FileInputStream fileStream = new FileInputStream(metadataFile);
+                    ObjectInputStream objectStream = new ObjectInputStream(fileStream);
+                    HashMap<String, Integer> bootMitigationCounts =
+                            (HashMap<String, Integer>) objectStream.readObject();
+                    objectStream.close();
+                    fileStream.close();
+
+                    for (int i = 0; i < mAllObservers.size(); i++) {
+                        final ObserverInternal observer = mAllObservers.valueAt(i);
+                        if (bootMitigationCounts.containsKey(observer.name)) {
+                            observer.setBootMitigationCount(
+                                    bootMitigationCounts.get(observer.name));
+                        }
+                    }
+                } catch (Exception e) {
+                    Slog.i(TAG, "Could not read observer metadata file: " + e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Register broadcast receiver for shutdown.
+     * We would save the observer state to persist across boots.
+     *
+     * @hide
+     */
+    public void registerShutdownBroadcastReceiver() {
+        BroadcastReceiver shutdownEventReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                // Only write if intent is relevant to device reboot or shutdown.
+                String intentAction = intent.getAction();
+                if (ACTION_REBOOT.equals(intentAction)
+                        || ACTION_SHUTDOWN.equals(intentAction)) {
+                    writeNow();
+                }
+            }
+        };
+
+        // Setup receiver for device reboots or shutdowns.
+        IntentFilter filter = new IntentFilter(ACTION_REBOOT);
+        filter.addAction(ACTION_SHUTDOWN);
+        mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null,
+                /* run on main thread */ null);
+    }
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
new file mode 100644
index 0000000..f1b2f6b
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -0,0 +1,990 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.crashrecovery.flags.Flags;
+import android.os.Build;
+import android.os.Environment;
+import android.os.PowerManager;
+import android.os.RecoverySystem;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.sysprop.CrashRecoveryProperties;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.ArrayUtils;
+import android.util.EventLog;
+import android.util.FileUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.PackageWatchdog.FailureReasons;
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utilities to help rescue the system from crash loops. Callers are expected to
+ * report boot events and persistent app crashes, and if they happen frequently
+ * enough this class will slowly escalate through several rescue operations
+ * before finally rebooting and prompting the user if they want to wipe data as
+ * a last resort.
+ *
+ * @hide
+ */
+public class RescueParty {
+    @VisibleForTesting
+    static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue";
+    @VisibleForTesting
+    static final int LEVEL_NONE = 0;
+    @VisibleForTesting
+    static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1;
+    @VisibleForTesting
+    static final int LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2;
+    @VisibleForTesting
+    static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;
+    @VisibleForTesting
+    static final int LEVEL_WARM_REBOOT = 4;
+    @VisibleForTesting
+    static final int LEVEL_FACTORY_RESET = 5;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_NONE = 0;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET = 1;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET = 2;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_WARM_REBOOT = 3;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 4;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 5;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 6;
+    @VisibleForTesting
+    static final int RESCUE_LEVEL_FACTORY_RESET = 7;
+
+    @IntDef(prefix = { "RESCUE_LEVEL_" }, value = {
+        RESCUE_LEVEL_NONE,
+        RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET,
+        RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET,
+        RESCUE_LEVEL_WARM_REBOOT,
+        RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
+        RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
+        RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
+        RESCUE_LEVEL_FACTORY_RESET
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface RescueLevels {}
+
+    @VisibleForTesting
+    static final String RESCUE_NON_REBOOT_LEVEL_LIMIT = "persist.sys.rescue_non_reboot_level_limit";
+    @VisibleForTesting
+    static final int DEFAULT_RESCUE_NON_REBOOT_LEVEL_LIMIT = RESCUE_LEVEL_WARM_REBOOT - 1;
+    @VisibleForTesting
+    static final String TAG = "RescueParty";
+    @VisibleForTesting
+    static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2);
+    @VisibleForTesting
+    static final int DEVICE_CONFIG_RESET_MODE = Settings.RESET_MODE_TRUSTED_DEFAULTS;
+    // The DeviceConfig namespace containing all RescueParty switches.
+    @VisibleForTesting
+    static final String NAMESPACE_CONFIGURATION = "configuration";
+    @VisibleForTesting
+    static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG =
+            "namespace_to_package_mapping";
+    @VisibleForTesting
+    static final long DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN = 1440;
+
+    private static final String NAME = "rescue-party-observer";
+
+    private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
+    private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
+    private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
+            "persist.device_config.configuration.disable_rescue_party";
+    private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
+            "persist.device_config.configuration.disable_rescue_party_factory_reset";
+    private static final String PROP_THROTTLE_DURATION_MIN_FLAG =
+            "persist.device_config.configuration.rescue_party_throttle_duration_min";
+
+    private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
+            | ApplicationInfo.FLAG_SYSTEM;
+
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+     */
+    private static final int LOG_TAG_RESCUE_SUCCESS = 2902;
+    private static final int LOG_TAG_RESCUE_FAILURE = 2903;
+
+    /** Register the Rescue Party observer as a Package Watchdog health observer */
+    public static void registerHealthObserver(Context context) {
+        PackageWatchdog.getInstance(context).registerHealthObserver(
+                RescuePartyObserver.getInstance(context));
+    }
+
+    private static boolean isDisabled() {
+        // Check if we're explicitly enabled for testing
+        if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
+            return false;
+        }
+
+        // We're disabled if the DeviceConfig disable flag is set to true.
+        // This is in case that an emergency rollback of the feature is needed.
+        if (SystemProperties.getBoolean(PROP_DEVICE_CONFIG_DISABLE_FLAG, false)) {
+            Slog.v(TAG, "Disabled because of DeviceConfig flag");
+            return true;
+        }
+
+        // We're disabled on all engineering devices
+        if (Build.TYPE.equals("eng")) {
+            Slog.v(TAG, "Disabled because of eng build");
+            return true;
+        }
+
+        // We're disabled on userdebug devices connected over USB, since that's
+        // a decent signal that someone is actively trying to debug the device,
+        // or that it's in a lab environment.
+        if (Build.TYPE.equals("userdebug") && isUsbActive()) {
+            Slog.v(TAG, "Disabled because of active USB connection");
+            return true;
+        }
+
+        // One last-ditch check
+        if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) {
+            Slog.v(TAG, "Disabled because of manual property");
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if we're currently attempting to reboot for a factory reset. This method must
+     * return true if RescueParty tries to reboot early during a boot loop, since the device
+     * will not be fully booted at this time.
+     */
+    public static boolean isRecoveryTriggeredReboot() {
+        return isFactoryResetPropertySet() || isRebootPropertySet();
+    }
+
+    static boolean isFactoryResetPropertySet() {
+        return CrashRecoveryProperties.attemptingFactoryReset().orElse(false);
+    }
+
+    static boolean isRebootPropertySet() {
+        return CrashRecoveryProperties.attemptingReboot().orElse(false);
+    }
+
+    protected static long getLastFactoryResetTimeMs() {
+        return CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L);
+    }
+
+    protected static int getMaxRescueLevelAttempted() {
+        return CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE);
+    }
+
+    protected static void setFactoryResetProperty(boolean value) {
+        CrashRecoveryProperties.attemptingFactoryReset(value);
+    }
+    protected static void setRebootProperty(boolean value) {
+        CrashRecoveryProperties.attemptingReboot(value);
+    }
+
+    protected static void setLastFactoryResetTimeMs(long value) {
+        CrashRecoveryProperties.lastFactoryResetTimeMs(value);
+    }
+
+    protected static void setMaxRescueLevelAttempted(int level) {
+        CrashRecoveryProperties.maxRescueLevelAttempted(level);
+    }
+
+    private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) {
+        Set<String> resultSet = new ArraySet<String>();
+        if (!Flags.deprecateFlagsAndSettingsResets()) {
+            try {
+                String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION,
+                        NAMESPACE_TO_PACKAGE_MAPPING_FLAG, "");
+                String[] mappingEntries = flagVal.split(",");
+                for (int i = 0; i < mappingEntries.length; i++) {
+                    if (TextUtils.isEmpty(mappingEntries[i])) {
+                        continue;
+                    }
+                    String[] splitEntry = mappingEntries[i].split(":");
+                    if (splitEntry.length != 2) {
+                        throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]);
+                    }
+                    String namespace = splitEntry[0];
+                    String packageName = splitEntry[1];
+
+                    if (packageNames.contains(packageName)) {
+                        resultSet.add(namespace);
+                    }
+                }
+            } catch (Exception e) {
+                resultSet.clear();
+                Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e);
+            } finally {
+                return resultSet;
+            }
+        } else {
+            return resultSet;
+        }
+    }
+
+    @VisibleForTesting
+    static long getElapsedRealtime() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    private static class RescuePartyMonitorCallback implements DeviceConfig.MonitorCallback {
+        Context mContext;
+
+        RescuePartyMonitorCallback(Context context) {
+            this.mContext = context;
+        }
+
+        public void onNamespaceUpdate(@NonNull String updatedNamespace) {
+            if (!Flags.deprecateFlagsAndSettingsResets()) {
+                startObservingPackages(mContext, updatedNamespace);
+            }
+        }
+
+        public void onDeviceConfigAccess(@NonNull String callingPackage,
+                @NonNull String namespace) {
+
+            if (!Flags.deprecateFlagsAndSettingsResets()) {
+                RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess(
+                        callingPackage,
+                        namespace);
+            }
+        }
+    }
+
+    private static void startObservingPackages(Context context, @NonNull String updatedNamespace) {
+        if (!Flags.deprecateFlagsAndSettingsResets()) {
+            RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
+            Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(
+                    updatedNamespace);
+            if (callingPackages == null) {
+                return;
+            }
+            List<String> callingPackageList = new ArrayList<>();
+            callingPackageList.addAll(callingPackages);
+            Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+                    + updatedNamespace);
+            PackageWatchdog.getInstance(context).startObservingHealth(
+                    rescuePartyObserver,
+                    callingPackageList,
+                    DEFAULT_OBSERVING_DURATION_MS);
+        }
+    }
+
+    private static int getMaxRescueLevel(boolean mayPerformReboot) {
+        if (Flags.recoverabilityDetection()) {
+            if (!mayPerformReboot
+                    || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
+                return SystemProperties.getInt(RESCUE_NON_REBOOT_LEVEL_LIMIT,
+                        DEFAULT_RESCUE_NON_REBOOT_LEVEL_LIMIT);
+            }
+            return RESCUE_LEVEL_FACTORY_RESET;
+        } else {
+            if (!mayPerformReboot
+                    || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
+                return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
+            }
+            return LEVEL_FACTORY_RESET;
+        }
+    }
+
+    private static int getMaxRescueLevel() {
+        if (!SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
+            return Level.factoryReset();
+        }
+        return Level.reboot();
+    }
+
+    /**
+     * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
+     *
+     * @param mitigationCount: the mitigation attempt number (1 = first attempt etc.)
+     * @param mayPerformReboot: whether or not a reboot and factory reset may be performed
+     *                          for the given failure.
+     * @return the rescue level for the n-th mitigation attempt.
+     */
+    private static int getRescueLevel(int mitigationCount, boolean mayPerformReboot) {
+        if (!Flags.deprecateFlagsAndSettingsResets()) {
+            if (mitigationCount == 1) {
+                return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
+            } else if (mitigationCount == 2) {
+                return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
+            } else if (mitigationCount == 3) {
+                return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
+            } else if (mitigationCount == 4) {
+                return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT);
+            } else if (mitigationCount >= 5) {
+                return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET);
+            } else {
+                Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
+                return LEVEL_NONE;
+            }
+        } else {
+            if (mitigationCount == 1) {
+                return Level.reboot();
+            } else if (mitigationCount >= 2) {
+                return Math.min(getMaxRescueLevel(), Level.factoryReset());
+            } else {
+                Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
+                return LEVEL_NONE;
+            }
+        }
+    }
+
+    /**
+     * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
+     * When failedPackage is null then 1st and 2nd mitigation counts are redundant (scoped and
+     * all device config reset). Behaves as if one mitigation attempt was already done.
+     *
+     * @param mitigationCount the mitigation attempt number (1 = first attempt etc.).
+     * @param mayPerformReboot whether or not a reboot and factory reset may be performed
+     * for the given failure.
+     * @param failedPackage in case of bootloop this is null.
+     * @return the rescue level for the n-th mitigation attempt.
+     */
+    private static @RescueLevels int getRescueLevel(int mitigationCount, boolean mayPerformReboot,
+            @Nullable VersionedPackage failedPackage) {
+        // Skipping RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET since it's not defined without a failed
+        // package.
+        if (failedPackage == null && mitigationCount > 0) {
+            mitigationCount += 1;
+        }
+        if (mitigationCount == 1) {
+            return RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET;
+        } else if (mitigationCount == 2) {
+            return RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET;
+        } else if (mitigationCount == 3) {
+            return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_WARM_REBOOT);
+        } else if (mitigationCount == 4) {
+            return Math.min(getMaxRescueLevel(mayPerformReboot),
+                    RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS);
+        } else if (mitigationCount == 5) {
+            return Math.min(getMaxRescueLevel(mayPerformReboot),
+                    RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES);
+        } else if (mitigationCount == 6) {
+            return Math.min(getMaxRescueLevel(mayPerformReboot),
+                    RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS);
+        } else if (mitigationCount >= 7) {
+            return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_FACTORY_RESET);
+        } else {
+            return RESCUE_LEVEL_NONE;
+        }
+    }
+
+    /**
+     * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
+     *
+     * @param mitigationCount the mitigation attempt number (1 = first attempt etc.).
+     * @return the rescue level for the n-th mitigation attempt.
+     */
+    private static @RescueLevels int getRescueLevel(int mitigationCount) {
+        if (mitigationCount == 1) {
+            return Level.reboot();
+        } else if (mitigationCount >= 2) {
+            return Math.min(getMaxRescueLevel(), Level.factoryReset());
+        } else {
+            return Level.none();
+        }
+    }
+
+    private static void executeRescueLevel(Context context, @Nullable String failedPackage,
+            int level) {
+        Slog.w(TAG, "Attempting rescue level " + levelToString(level));
+        try {
+            executeRescueLevelInternal(context, level, failedPackage);
+            EventLog.writeEvent(LOG_TAG_RESCUE_SUCCESS, level);
+            String successMsg = "Finished rescue level " + levelToString(level);
+            if (!TextUtils.isEmpty(failedPackage)) {
+                successMsg += " for package " + failedPackage;
+            }
+            logCrashRecoveryEvent(Log.DEBUG, successMsg);
+        } catch (Throwable t) {
+            logRescueException(level, failedPackage, t);
+        }
+    }
+
+    private static void executeRescueLevelInternal(Context context, int level, @Nullable
+            String failedPackage) throws Exception {
+        if (Flags.recoverabilityDetection()) {
+            executeRescueLevelInternalNew(context, level, failedPackage);
+        } else {
+            executeRescueLevelInternalOld(context, level, failedPackage);
+        }
+    }
+
+    private static void executeRescueLevelInternalOld(Context context, int level, @Nullable
+            String failedPackage) throws Exception {
+        CrashRecoveryStatsLog.write(CrashRecoveryStatsLog.RESCUE_PARTY_RESET_REPORTED,
+                level, levelToString(level));
+        // Try our best to reset all settings possible, and once finished
+        // rethrow any exception that we encountered
+        Exception res = null;
+        switch (level) {
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+                break;
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                break;
+            case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+                break;
+            case LEVEL_WARM_REBOOT:
+                executeWarmReboot(context, level, failedPackage);
+                break;
+            case LEVEL_FACTORY_RESET:
+                // Before the completion of Reboot, if any crash happens then PackageWatchdog
+                // escalates to next level i.e. factory reset, as they happen in separate threads.
+                // Adding a check to prevent factory reset to execute before above reboot completes.
+                // Note: this reboot property is not persistent resets after reboot is completed.
+                if (isRebootPropertySet()) {
+                    return;
+                }
+                executeFactoryReset(context, level, failedPackage);
+                break;
+        }
+
+        if (res != null) {
+            throw res;
+        }
+    }
+
+    private static void executeRescueLevelInternalNew(Context context, @RescueLevels int level,
+            @Nullable String failedPackage) throws Exception {
+        CrashRecoveryStatsLog.write(CrashRecoveryStatsLog.RESCUE_PARTY_RESET_REPORTED,
+                level, levelToString(level));
+        switch (level) {
+            case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET:
+                break;
+            case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET:
+                break;
+            case RESCUE_LEVEL_WARM_REBOOT:
+                executeWarmReboot(context, level, failedPackage);
+                break;
+            case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+                // do nothing
+                break;
+            case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                // do nothing
+                break;
+            case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+                // do nothing
+                break;
+            case RESCUE_LEVEL_FACTORY_RESET:
+                // Before the completion of Reboot, if any crash happens then PackageWatchdog
+                // escalates to next level i.e. factory reset, as they happen in separate threads.
+                // Adding a check to prevent factory reset to execute before above reboot completes.
+                // Note: this reboot property is not persistent resets after reboot is completed.
+                if (isRebootPropertySet()) {
+                    return;
+                }
+                executeFactoryReset(context, level, failedPackage);
+                break;
+        }
+    }
+
+    private static void executeWarmReboot(Context context, int level,
+            @Nullable String failedPackage) {
+        if (Flags.deprecateFlagsAndSettingsResets()) {
+            if (shouldThrottleReboot()) {
+                return;
+            }
+        }
+
+        // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
+        // when device shutting down.
+        setRebootProperty(true);
+
+        if (Flags.synchronousRebootInRescueParty()) {
+            try {
+                PowerManager pm = context.getSystemService(PowerManager.class);
+                if (pm != null) {
+                    pm.reboot(TAG);
+                }
+            } catch (Throwable t) {
+                logRescueException(level, failedPackage, t);
+            }
+        } else {
+            Runnable runnable = () -> {
+                try {
+                    PowerManager pm = context.getSystemService(PowerManager.class);
+                    if (pm != null) {
+                        pm.reboot(TAG);
+                    }
+                } catch (Throwable t) {
+                    logRescueException(level, failedPackage, t);
+                }
+            };
+            Thread thread = new Thread(runnable);
+            thread.start();
+        }
+    }
+
+    private static void executeFactoryReset(Context context, int level,
+            @Nullable String failedPackage) {
+        if (Flags.deprecateFlagsAndSettingsResets()) {
+            if (shouldThrottleReboot()) {
+                return;
+            }
+        }
+        setFactoryResetProperty(true);
+        long now = System.currentTimeMillis();
+        setLastFactoryResetTimeMs(now);
+
+        if (Flags.synchronousRebootInRescueParty()) {
+            try {
+                RecoverySystem.rebootPromptAndWipeUserData(context, TAG + "," + failedPackage);
+            } catch (Throwable t) {
+                logRescueException(level, failedPackage, t);
+            }
+        } else {
+            Runnable runnable = new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        RecoverySystem.rebootPromptAndWipeUserData(context,
+                            TAG + "," + failedPackage);
+                    } catch (Throwable t) {
+                        logRescueException(level, failedPackage, t);
+                    }
+                }
+            };
+            Thread thread = new Thread(runnable);
+            thread.start();
+        }
+    }
+
+
+    private static String getCompleteMessage(Throwable t) {
+        final StringBuilder builder = new StringBuilder();
+        builder.append(t.getMessage());
+        while ((t = t.getCause()) != null) {
+            builder.append(": ").append(t.getMessage());
+        }
+        return builder.toString();
+    }
+
+    private static void logRescueException(int level, @Nullable String failedPackageName,
+            Throwable t) {
+        final String msg = getCompleteMessage(t);
+        EventLog.writeEvent(LOG_TAG_RESCUE_FAILURE, level, msg);
+        String failureMsg = "Failed rescue level " + levelToString(level);
+        if (!TextUtils.isEmpty(failedPackageName)) {
+            failureMsg += " for package " + failedPackageName;
+        }
+        logCrashRecoveryEvent(Log.ERROR, failureMsg + ": " + msg);
+    }
+
+    private static int mapRescueLevelToUserImpact(int rescueLevel) {
+        if (Flags.recoverabilityDetection()) {
+            switch (rescueLevel) {
+                case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10;
+                case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_40;
+                case RESCUE_LEVEL_WARM_REBOOT:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50;
+                case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
+                case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_75;
+                case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_80;
+                case RESCUE_LEVEL_FACTORY_RESET:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_100;
+                default:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+            }
+        } else {
+            switch (rescueLevel) {
+                case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+                case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10;
+                case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+                case LEVEL_WARM_REBOOT:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50;
+                case LEVEL_FACTORY_RESET:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_100;
+                default:
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+            }
+        }
+    }
+
+    /**
+     * Handle mitigation action for package failures. This observer will be register to Package
+     * Watchdog and will receive calls about package failures. This observer is persistent so it
+     * may choose to mitigate failures for packages it has not explicitly asked to observe.
+     */
+    public static class RescuePartyObserver implements PackageHealthObserver {
+
+        private final Context mContext;
+        private final Map<String, Set<String>> mCallingPackageNamespaceSetMap = new HashMap<>();
+        private final Map<String, Set<String>> mNamespaceCallingPackageSetMap = new HashMap<>();
+
+        @GuardedBy("RescuePartyObserver.class")
+        static RescuePartyObserver sRescuePartyObserver;
+
+        private RescuePartyObserver(Context context) {
+            mContext = context;
+        }
+
+        /** Creates or gets singleton instance of RescueParty. */
+        public static RescuePartyObserver getInstance(Context context) {
+            synchronized (RescuePartyObserver.class) {
+                if (sRescuePartyObserver == null) {
+                    sRescuePartyObserver = new RescuePartyObserver(context);
+                }
+                return sRescuePartyObserver;
+            }
+        }
+
+        /** Gets singleton instance. It returns null if the instance is not created yet.*/
+        @Nullable
+        public static RescuePartyObserver getInstanceIfCreated() {
+            synchronized (RescuePartyObserver.class) {
+                return sRescuePartyObserver;
+            }
+        }
+
+        @VisibleForTesting
+        static void reset() {
+            synchronized (RescuePartyObserver.class) {
+                sRescuePartyObserver = null;
+            }
+        }
+
+        @Override
+        public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
+                @FailureReasons int failureReason, int mitigationCount) {
+            int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+            if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
+                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
+                if (Flags.recoverabilityDetection()) {
+                    if (!Flags.deprecateFlagsAndSettingsResets()) {
+                        impact =  mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
+                                mayPerformReboot(failedPackage), failedPackage));
+                    } else {
+                        impact =  mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+                    }
+                } else {
+                    impact =  mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
+                            mayPerformReboot(failedPackage)));
+                }
+            }
+
+            Slog.i(TAG, "Checking available remediations for health check failure."
+                    + " failedPackage: "
+                    + (failedPackage == null ? null : failedPackage.getPackageName())
+                    + " failureReason: " + failureReason
+                    + " available impact: " + impact);
+            return impact;
+        }
+
+        @Override
+        public boolean execute(@Nullable VersionedPackage failedPackage,
+                @FailureReasons int failureReason, int mitigationCount) {
+            if (isDisabled()) {
+                return false;
+            }
+            Slog.i(TAG, "Executing remediation."
+                    + " failedPackage: "
+                    + (failedPackage == null ? null : failedPackage.getPackageName())
+                    + " failureReason: " + failureReason
+                    + " mitigationCount: " + mitigationCount);
+            if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
+                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
+                final int level;
+                if (Flags.recoverabilityDetection()) {
+                    if (!Flags.deprecateFlagsAndSettingsResets()) {
+                        level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage),
+                                failedPackage);
+                    } else {
+                        level = getRescueLevel(mitigationCount);
+                    }
+                } else {
+                    level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage));
+                }
+                executeRescueLevel(mContext,
+                        failedPackage == null ? null : failedPackage.getPackageName(), level);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public boolean isPersistent() {
+            return true;
+        }
+
+        @Override
+        public boolean mayObservePackage(String packageName) {
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                // A package is a module if this is non-null
+                if (pm.getModuleInfo(packageName, 0) != null) {
+                    return true;
+                }
+            } catch (PackageManager.NameNotFoundException | IllegalStateException ignore) {
+            }
+
+            return isPersistentSystemApp(packageName);
+        }
+
+        @Override
+        public int onBootLoop(int mitigationCount) {
+            if (isDisabled()) {
+                return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+            }
+            if (Flags.recoverabilityDetection()) {
+                if (!Flags.deprecateFlagsAndSettingsResets()) {
+                    return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
+                            true, /*failedPackage=*/ null));
+                } else {
+                    return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+                }
+            } else {
+                return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
+            }
+        }
+
+        @Override
+        public boolean executeBootLoopMitigation(int mitigationCount) {
+            if (isDisabled()) {
+                return false;
+            }
+            boolean mayPerformReboot = !shouldThrottleReboot();
+            final int level;
+            if (Flags.recoverabilityDetection()) {
+                if (!Flags.deprecateFlagsAndSettingsResets()) {
+                    level = getRescueLevel(mitigationCount, mayPerformReboot,
+                            /*failedPackage=*/ null);
+                } else {
+                    level = getRescueLevel(mitigationCount);
+                }
+            } else {
+                level = getRescueLevel(mitigationCount, mayPerformReboot);
+            }
+            executeRescueLevel(mContext, /*failedPackage=*/ null, level);
+            return true;
+        }
+
+        @Override
+        public String getUniqueIdentifier() {
+            return NAME;
+        }
+
+        /**
+         * Returns {@code true} if the failing package is non-null and performing a reboot or
+         * prompting a factory reset is an acceptable mitigation strategy for the package's
+         * failure, {@code false} otherwise.
+         */
+        private boolean mayPerformReboot(@Nullable VersionedPackage failingPackage) {
+            if (failingPackage == null) {
+                return false;
+            }
+            if (shouldThrottleReboot())  {
+                return false;
+            }
+
+            return isPersistentSystemApp(failingPackage.getPackageName());
+        }
+
+        private boolean isPersistentSystemApp(@NonNull String packageName) {
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+                return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+        }
+
+        private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
+                @NonNull String namespace) {
+            if (!Flags.deprecateFlagsAndSettingsResets()) {
+                // Record it in calling packages to namespace map
+                Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
+                if (namespaceSet == null) {
+                    namespaceSet = new ArraySet<>();
+                    mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
+                }
+                namespaceSet.add(namespace);
+                // Record it in namespace to calling packages map
+                Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
+                if (callingPackageSet == null) {
+                    callingPackageSet = new ArraySet<>();
+                }
+                callingPackageSet.add(callingPackage);
+                mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
+            }
+        }
+
+        private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
+            return mCallingPackageNamespaceSetMap.get(failedPackage);
+        }
+
+        private synchronized Set<String> getAllAffectedNamespaceSet() {
+            return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet());
+        }
+
+        private synchronized Set<String> getCallingPackagesSet(String namespace) {
+            return mNamespaceCallingPackageSetMap.get(namespace);
+        }
+    }
+
+    /**
+     * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset.
+     * Will return {@code false} if a factory reset was already offered recently.
+     */
+    private static boolean shouldThrottleReboot() {
+        Long lastResetTime = getLastFactoryResetTimeMs();
+        long now = System.currentTimeMillis();
+        long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG,
+                DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN);
+        return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin);
+    }
+
+    private static int[] getAllUserIds() {
+        int systemUserId = UserHandle.SYSTEM.getIdentifier();
+        int[] userIds = { systemUserId };
+        try {
+            for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) {
+                try {
+                    final int userId = Integer.parseInt(file.getName());
+                    if (userId != systemUserId) {
+                        userIds = ArrayUtils.appendInt(userIds, userId);
+                    }
+                } catch (NumberFormatException ignored) {
+                }
+            }
+        } catch (Throwable t) {
+            Slog.w(TAG, "Trouble discovering users", t);
+        }
+        return userIds;
+    }
+
+    /**
+     * Hacky test to check if the device has an active USB connection, which is
+     * a good proxy for someone doing local development work.
+     */
+    private static boolean isUsbActive() {
+        if (SystemProperties.getBoolean(PROP_VIRTUAL_DEVICE, false)) {
+            Slog.v(TAG, "Assuming virtual device is connected over USB");
+            return true;
+        }
+        try {
+            final String state = FileUtils
+                    .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, "");
+            return "CONFIGURED".equals(state.trim());
+        } catch (Throwable t) {
+            Slog.w(TAG, "Failed to determine if device was on USB", t);
+            return false;
+        }
+    }
+
+    private static class Level {
+        static int none() {
+            return Flags.recoverabilityDetection() ? RESCUE_LEVEL_NONE : LEVEL_NONE;
+        }
+
+        static int reboot() {
+            return Flags.recoverabilityDetection() ? RESCUE_LEVEL_WARM_REBOOT : LEVEL_WARM_REBOOT;
+        }
+
+        static int factoryReset() {
+            return Flags.recoverabilityDetection()
+                    ? RESCUE_LEVEL_FACTORY_RESET
+                    : LEVEL_FACTORY_RESET;
+        }
+    }
+
+    private static String levelToString(int level) {
+        if (Flags.recoverabilityDetection()) {
+            switch (level) {
+                case RESCUE_LEVEL_NONE:
+                    return "NONE";
+                case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET:
+                    return "SCOPED_DEVICE_CONFIG_RESET";
+                case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET:
+                    return "ALL_DEVICE_CONFIG_RESET";
+                case RESCUE_LEVEL_WARM_REBOOT:
+                    return "WARM_REBOOT";
+                case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+                    return "RESET_SETTINGS_UNTRUSTED_DEFAULTS";
+                case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                    return "RESET_SETTINGS_UNTRUSTED_CHANGES";
+                case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+                    return "RESET_SETTINGS_TRUSTED_DEFAULTS";
+                case RESCUE_LEVEL_FACTORY_RESET:
+                    return "FACTORY_RESET";
+                default:
+                    return Integer.toString(level);
+            }
+        } else {
+            switch (level) {
+                case LEVEL_NONE:
+                    return "NONE";
+                case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+                    return "RESET_SETTINGS_UNTRUSTED_DEFAULTS";
+                case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                    return "RESET_SETTINGS_UNTRUSTED_CHANGES";
+                case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+                    return "RESET_SETTINGS_TRUSTED_DEFAULTS";
+                case LEVEL_WARM_REBOOT:
+                    return "WARM_REBOOT";
+                case LEVEL_FACTORY_RESET:
+                    return "FACTORY_RESET";
+                default:
+                    return Integer.toString(level);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryModule.java b/packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryModule.java
similarity index 100%
rename from services/core/java/com/android/server/crashrecovery/CrashRecoveryModule.java
rename to packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryModule.java
diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java b/packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
similarity index 92%
rename from services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
rename to packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
index 3eb3380..2e2a937 100644
--- a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
@@ -18,7 +18,7 @@
 
 import android.os.Environment;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
+import android.util.Log;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -41,7 +41,7 @@
 
     /** Persist recovery related events in crashrecovery events file.**/
     public static void logCrashRecoveryEvent(int priority, String msg) {
-        Slog.println(priority, TAG, msg);
+        Log.println(priority, TAG, msg);
         try {
             File fname = getCrashRecoveryEventsFile();
             synchronized (sFileLock) {
@@ -52,7 +52,7 @@
                 pw.close();
             }
         } catch (IOException e) {
-            Slog.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage());
+            Log.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage());
         }
     }
 
@@ -72,7 +72,7 @@
                     pw.println(line);
                 }
             } catch (IOException e) {
-                Slog.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage());
+                Log.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage());
             }
         }
         pw.decreaseIndent();
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
new file mode 100644
index 0000000..2931652
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -0,0 +1,786 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.crashrecovery.flags.Flags;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.sysprop.CrashRecoveryProperties;
+import android.util.ArraySet;
+import android.util.FileUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.PackageWatchdog;
+import com.android.server.PackageWatchdog.FailureReasons;
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * {@link PackageHealthObserver} for {@link RollbackManagerService}.
+ * This class monitors crashes and triggers RollbackManager rollback accordingly.
+ * It also monitors native crashes for some short while after boot.
+ *
+ * @hide
+ */
+public final class RollbackPackageHealthObserver implements PackageHealthObserver {
+    private static final String TAG = "RollbackPackageHealthObserver";
+    private static final String NAME = "rollback-observer";
+    private static final String CLASS_NAME = RollbackPackageHealthObserver.class.getName();
+
+    private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
+            | ApplicationInfo.FLAG_SYSTEM;
+
+    private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG =
+            "persist.device_config.configuration.disable_high_impact_rollback";
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final File mLastStagedRollbackIdsFile;
+    private final File mTwoPhaseRollbackEnabledFile;
+    // Staged rollback ids that have been committed but their session is not yet ready
+    private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>();
+    // True if needing to roll back only rebootless apexes when native crash happens
+    private boolean mTwoPhaseRollbackEnabled;
+
+    @VisibleForTesting
+    public RollbackPackageHealthObserver(@NonNull Context context) {
+        mContext = context;
+        HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
+        handlerThread.start();
+        mHandler = new Handler(handlerThread.getLooper());
+        File dataDir = new File(Environment.getDataDirectory(), "rollback-observer");
+        dataDir.mkdirs();
+        mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
+        mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
+        PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+
+        if (SystemProperties.getBoolean("sys.boot_completed", false)) {
+            // Load the value from the file if system server has crashed and restarted
+            mTwoPhaseRollbackEnabled = readBoolean(mTwoPhaseRollbackEnabledFile);
+        } else {
+            // Disable two-phase rollback for a normal reboot. We assume the rebootless apex
+            // installed before reboot is stable if native crash didn't happen.
+            mTwoPhaseRollbackEnabled = false;
+            writeBoolean(mTwoPhaseRollbackEnabledFile, false);
+        }
+    }
+
+    @Override
+    public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
+            @FailureReasons int failureReason, int mitigationCount) {
+        int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+        if (Flags.recoverabilityDetection()) {
+            List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+            List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
+                    availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW);
+            if (!lowImpactRollbacks.isEmpty()) {
+                if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+                    // For native crashes, we will directly roll back any available rollbacks at low
+                    // impact level
+                    impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+                } else if (getRollbackForPackage(failedPackage, lowImpactRollbacks) != null) {
+                    // Rollback is available for crashing low impact package
+                    impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+                } else {
+                    impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
+                }
+            }
+        } else {
+            boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class)
+                    .getAvailableRollbacks().isEmpty();
+
+            if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
+                    && anyRollbackAvailable) {
+                // For native crashes, we will directly roll back any available rollbacks
+                // Note: For non-native crashes the rollback-all step has higher impact
+                impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+            } else if (getAvailableRollback(failedPackage) != null) {
+                // Rollback is available, we may get a callback into #execute
+                impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+            } else if (anyRollbackAvailable) {
+                // If any rollbacks are available, we will commit them
+                impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
+            }
+        }
+
+        Slog.i(TAG, "Checking available remediations for health check failure."
+                + " failedPackage: "
+                + (failedPackage == null ? null : failedPackage.getPackageName())
+                + " failureReason: " + failureReason
+                + " available impact: " + impact);
+        return impact;
+    }
+
+    @Override
+    public boolean execute(@Nullable VersionedPackage failedPackage,
+            @FailureReasons int rollbackReason, int mitigationCount) {
+        Slog.i(TAG, "Executing remediation."
+                + " failedPackage: "
+                + (failedPackage == null ? null : failedPackage.getPackageName())
+                + " rollbackReason: " + rollbackReason
+                + " mitigationCount: " + mitigationCount);
+        if (Flags.recoverabilityDetection()) {
+            List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+            if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+                mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
+                return true;
+            }
+
+            List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
+                    availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW);
+            RollbackInfo rollback = getRollbackForPackage(failedPackage, lowImpactRollbacks);
+            if (rollback != null) {
+                mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
+            } else if (!lowImpactRollbacks.isEmpty()) {
+                // Apply all available low impact rollbacks.
+                mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
+            }
+        } else {
+            if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+                mHandler.post(() -> rollbackAll(rollbackReason));
+                return true;
+            }
+
+            RollbackInfo rollback = getAvailableRollback(failedPackage);
+            if (rollback != null) {
+                mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
+            } else {
+                mHandler.post(() -> rollbackAll(rollbackReason));
+            }
+        }
+
+        // Assume rollbacks executed successfully
+        return true;
+    }
+
+    @Override
+    public int onBootLoop(int mitigationCount) {
+        int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+        if (Flags.recoverabilityDetection()) {
+            List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+            if (!availableRollbacks.isEmpty()) {
+                impact = getUserImpactBasedOnRollbackImpactLevel(availableRollbacks);
+            }
+        }
+        return impact;
+    }
+
+    @Override
+    public boolean executeBootLoopMitigation(int mitigationCount) {
+        if (Flags.recoverabilityDetection()) {
+            List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+
+            triggerLeastImpactLevelRollback(availableRollbacks,
+                    PackageWatchdog.FAILURE_REASON_BOOT_LOOP);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    @NonNull
+    public String getUniqueIdentifier() {
+        return NAME;
+    }
+
+    @Override
+    public boolean isPersistent() {
+        return true;
+    }
+
+    @Override
+    public boolean mayObservePackage(@NonNull String packageName) {
+        if (getAvailableRollbacks().isEmpty()) {
+            return false;
+        }
+        return isPersistentSystemApp(packageName);
+    }
+
+    private List<RollbackInfo> getAvailableRollbacks() {
+        return mContext.getSystemService(RollbackManager.class).getAvailableRollbacks();
+    }
+
+    private boolean isPersistentSystemApp(@NonNull String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    private void assertInWorkerThread() {
+        Preconditions.checkState(mHandler.getLooper().isCurrentThread());
+    }
+
+    /**
+     * Start observing health of {@code packages} for {@code durationMs}.
+     * This may cause {@code packages} to be rolled back if they crash too freqeuntly.
+     */
+    @AnyThread
+    @NonNull
+    public void startObservingHealth(@NonNull List<String> packages, @NonNull long durationMs) {
+        PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
+    }
+
+    @AnyThread
+    @NonNull
+    public void notifyRollbackAvailable(@NonNull RollbackInfo rollback) {
+        mHandler.post(() -> {
+            // Enable two-phase rollback when a rebootless apex rollback is made available.
+            // We assume the rebootless apex is stable and is less likely to be the cause
+            // if native crash doesn't happen before reboot. So we will clear the flag and disable
+            // two-phase rollback after reboot.
+            if (isRebootlessApex(rollback)) {
+                mTwoPhaseRollbackEnabled = true;
+                writeBoolean(mTwoPhaseRollbackEnabledFile, true);
+            }
+        });
+    }
+
+    private static boolean isRebootlessApex(RollbackInfo rollback) {
+        if (!rollback.isStaged()) {
+            for (PackageRollbackInfo info : rollback.getPackages()) {
+                if (info.isApex()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot
+     * to check for native crashes and mitigate them if needed.
+     */
+    @AnyThread
+    public void onBootCompletedAsync() {
+        mHandler.post(()->onBootCompleted());
+    }
+
+    @WorkerThread
+    private void onBootCompleted() {
+        assertInWorkerThread();
+
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
+            // TODO(gavincorkery): Call into Package Watchdog from outside the observer
+            PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes();
+        }
+
+        SparseArray<String> rollbackIds = popLastStagedRollbackIds();
+        for (int i = 0; i < rollbackIds.size(); i++) {
+            WatchdogRollbackLogger.logRollbackStatusOnBoot(mContext,
+                    rollbackIds.keyAt(i), rollbackIds.valueAt(i),
+                    rollbackManager.getRecentlyCommittedRollbacks());
+        }
+    }
+
+    @AnyThread
+    private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
+            for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
+                if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) {
+                    return rollback;
+                }
+                // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have
+                //  to rely on complicated reasoning as below
+
+                // Due to b/147666157, for apk in apex, we do not know the version we are rolling
+                // back from. But if a package X is embedded in apex A exclusively (not embedded in
+                // any other apex), which is not guaranteed, then it is sufficient to check only
+                // package names here, as the version of failedPackage and the PackageRollbackInfo
+                // can't be different. If failedPackage has a higher version, then it must have
+                // been updated somehow. There are two ways: it was updated by an update of apex A
+                // or updated directly as apk. In both cases, this rollback would have gotten
+                // expired when onPackageReplaced() was called. Since the rollback exists, it has
+                // same version as failedPackage.
+                if (packageRollback.isApkInApex()
+                        && packageRollback.getVersionRolledBackFrom().getPackageName()
+                        .equals(failedPackage.getPackageName())) {
+                    return rollback;
+                }
+            }
+        }
+        return null;
+    }
+
+    @AnyThread
+    private RollbackInfo getRollbackForPackage(@Nullable VersionedPackage failedPackage,
+            List<RollbackInfo> availableRollbacks) {
+        if (failedPackage == null) {
+            return null;
+        }
+
+        for (RollbackInfo rollback : availableRollbacks) {
+            for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
+                if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) {
+                    return rollback;
+                }
+                // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have
+                //  to rely on complicated reasoning as below
+
+                // Due to b/147666157, for apk in apex, we do not know the version we are rolling
+                // back from. But if a package X is embedded in apex A exclusively (not embedded in
+                // any other apex), which is not guaranteed, then it is sufficient to check only
+                // package names here, as the version of failedPackage and the PackageRollbackInfo
+                // can't be different. If failedPackage has a higher version, then it must have
+                // been updated somehow. There are two ways: it was updated by an update of apex A
+                // or updated directly as apk. In both cases, this rollback would have gotten
+                // expired when onPackageReplaced() was called. Since the rollback exists, it has
+                // same version as failedPackage.
+                if (packageRollback.isApkInApex()
+                        && packageRollback.getVersionRolledBackFrom().getPackageName()
+                        .equals(failedPackage.getPackageName())) {
+                    return rollback;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if staged session associated with {@code rollbackId} was marked
+     * as handled, {@code false} if already handled.
+     */
+    @WorkerThread
+    private boolean markStagedSessionHandled(int rollbackId) {
+        assertInWorkerThread();
+        return mPendingStagedRollbackIds.remove(rollbackId);
+    }
+
+    /**
+     * Returns {@code true} if all pending staged rollback sessions were marked as handled,
+     * {@code false} if there is any left.
+     */
+    @WorkerThread
+    private boolean isPendingStagedSessionsEmpty() {
+        assertInWorkerThread();
+        return mPendingStagedRollbackIds.isEmpty();
+    }
+
+    private static boolean readBoolean(File file) {
+        try (FileInputStream fis = new FileInputStream(file)) {
+            return fis.read() == 1;
+        } catch (IOException ignore) {
+            return false;
+        }
+    }
+
+    private static void writeBoolean(File file, boolean value) {
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            fos.write(value ? 1 : 0);
+            fos.flush();
+            FileUtils.sync(fos);
+        } catch (IOException ignore) {
+        }
+    }
+
+    @WorkerThread
+    private void saveStagedRollbackId(int stagedRollbackId, @Nullable VersionedPackage logPackage) {
+        assertInWorkerThread();
+        writeStagedRollbackId(mLastStagedRollbackIdsFile, stagedRollbackId, logPackage);
+    }
+
+    static void writeStagedRollbackId(File file, int stagedRollbackId,
+            @Nullable VersionedPackage logPackage) {
+        try {
+            FileOutputStream fos = new FileOutputStream(file, true);
+            PrintWriter pw = new PrintWriter(fos);
+            String logPackageName = logPackage != null ? logPackage.getPackageName() : "";
+            pw.append(String.valueOf(stagedRollbackId)).append(",").append(logPackageName);
+            pw.println();
+            pw.flush();
+            FileUtils.sync(fos);
+            pw.close();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to save last staged rollback id", e);
+            file.delete();
+        }
+    }
+
+    @WorkerThread
+    private SparseArray<String> popLastStagedRollbackIds() {
+        assertInWorkerThread();
+        try {
+            return readStagedRollbackIds(mLastStagedRollbackIdsFile);
+        } finally {
+            mLastStagedRollbackIdsFile.delete();
+        }
+    }
+
+    static SparseArray<String> readStagedRollbackIds(File file) {
+        SparseArray<String> result = new SparseArray<>();
+        try {
+            String line;
+            BufferedReader reader = new BufferedReader(new FileReader(file));
+            while ((line = reader.readLine()) != null) {
+                // Each line is of the format: "id,logging_package"
+                String[] values = line.trim().split(",");
+                String rollbackId = values[0];
+                String logPackageName = "";
+                if (values.length > 1) {
+                    logPackageName = values[1];
+                }
+                result.put(Integer.parseInt(rollbackId), logPackageName);
+            }
+        } catch (Exception ignore) {
+            return new SparseArray<>();
+        }
+        return result;
+    }
+
+
+    /**
+     * Returns true if the package name is the name of a module.
+     */
+    @AnyThread
+    private boolean isModule(String packageName) {
+        // Check if the package is listed among the system modules or is an
+        // APK inside an updatable APEX.
+        try {
+            final PackageInfo pkg = mContext.getPackageManager()
+                    .getPackageInfo(packageName, 0 /* flags */);
+            String apexPackageName = pkg.getApexPackageName();
+            if (apexPackageName != null) {
+                packageName = apexPackageName;
+            }
+
+            return pm.getModuleInfo(packageName, 0 /* flags */) != null;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Rolls back the session that owns {@code failedPackage}
+     *
+     * @param rollback {@code rollbackInfo} of the {@code failedPackage}
+     * @param failedPackage the package that needs to be rolled back
+     */
+    @WorkerThread
+    private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
+            @FailureReasons int rollbackReason) {
+        assertInWorkerThread();
+        String failedPackageName = (failedPackage == null ? null : failedPackage.getPackageName());
+
+        Slog.i(TAG, "Rolling back package. RollbackId: " + rollback.getRollbackId()
+                + " failedPackage: " + failedPackageName
+                + " rollbackReason: " + rollbackReason);
+        logCrashRecoveryEvent(Log.DEBUG, String.format("Rolling back %s. Reason: %s",
+                failedPackageName, rollbackReason));
+        final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);
+        final String failedPackageToLog;
+        if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+            failedPackageToLog = SystemProperties.get(
+                    "sys.init.updatable_crashing_process_name", "");
+        } else {
+            failedPackageToLog = failedPackage.getPackageName();
+        }
+        VersionedPackage logPackageTemp = null;
+        if (isModule(failedPackage.getPackageName())) {
+            logPackageTemp = WatchdogRollbackLogger.getLogPackage(mContext, failedPackage);
+        }
+
+        final VersionedPackage logPackage = logPackageTemp;
+        WatchdogRollbackLogger.logEvent(logPackage,
+                CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+                reasonToLog, failedPackageToLog);
+
+        Consumer<Intent> onResult = result -> {
+            assertInWorkerThread();
+            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                    RollbackManager.STATUS_FAILURE);
+            if (status == RollbackManager.STATUS_SUCCESS) {
+                if (rollback.isStaged()) {
+                    int rollbackId = rollback.getRollbackId();
+                    saveStagedRollbackId(rollbackId, logPackage);
+                    WatchdogRollbackLogger.logEvent(logPackage,
+                            CrashRecoveryStatsLog
+                            .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
+                            reasonToLog, failedPackageToLog);
+
+                } else {
+                    WatchdogRollbackLogger.logEvent(logPackage,
+                            CrashRecoveryStatsLog
+                                    .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                            reasonToLog, failedPackageToLog);
+                }
+            } else {
+                WatchdogRollbackLogger.logEvent(logPackage,
+                        CrashRecoveryStatsLog
+                                .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+                        reasonToLog, failedPackageToLog);
+            }
+            if (rollback.isStaged()) {
+                markStagedSessionHandled(rollback.getRollbackId());
+                // Wait for all pending staged sessions to get handled before rebooting.
+                if (isPendingStagedSessionsEmpty()) {
+                    CrashRecoveryProperties.attemptingReboot(true);
+                    mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
+                }
+            }
+        };
+
+        // Define a BroadcastReceiver to handle the result
+        BroadcastReceiver rollbackReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent result) {
+                mHandler.post(() -> onResult.accept(result));
+            }
+        };
+
+        String intentActionName = CLASS_NAME + rollback.getRollbackId();
+        // Register the BroadcastReceiver
+        mContext.registerReceiver(rollbackReceiver,
+                new IntentFilter(intentActionName),
+                Context.RECEIVER_NOT_EXPORTED);
+
+        Intent intentReceiver = new Intent(intentActionName);
+        intentReceiver.putExtra("rollbackId", rollback.getRollbackId());
+        intentReceiver.setPackage(mContext.getPackageName());
+        intentReceiver.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
+        PendingIntent rollbackPendingIntent = PendingIntent.getBroadcast(mContext,
+                rollback.getRollbackId(),
+                intentReceiver,
+                PendingIntent.FLAG_MUTABLE);
+
+        rollbackManager.commitRollback(rollback.getRollbackId(),
+                Collections.singletonList(failedPackage),
+                rollbackPendingIntent.getIntentSender());
+    }
+
+    /**
+     * Two-phase rollback:
+     * 1. roll back rebootless apexes first
+     * 2. roll back all remaining rollbacks if native crash doesn't stop after (1) is done
+     *
+     * This approach gives us a better chance to correctly attribute native crash to rebootless
+     * apex update without rolling back Mainline updates which might contains critical security
+     * fixes.
+     */
+    @WorkerThread
+    private boolean useTwoPhaseRollback(List<RollbackInfo> rollbacks) {
+        assertInWorkerThread();
+        if (!mTwoPhaseRollbackEnabled) {
+            return false;
+        }
+
+        Slog.i(TAG, "Rolling back all rebootless APEX rollbacks");
+        boolean found = false;
+        for (RollbackInfo rollback : rollbacks) {
+            if (isRebootlessApex(rollback)) {
+                VersionedPackage firstRollback =
+                        rollback.getPackages().get(0).getVersionRolledBackFrom();
+                rollbackPackage(rollback, firstRollback,
+                        PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+                found = true;
+            }
+        }
+        return found;
+    }
+
+    /**
+     * Rollback the package that has minimum rollback impact level.
+     * @param availableRollbacks all available rollbacks
+     * @param rollbackReason reason to rollback
+     */
+    private void triggerLeastImpactLevelRollback(List<RollbackInfo> availableRollbacks,
+            @FailureReasons int rollbackReason) {
+        int minRollbackImpactLevel = getMinRollbackImpactLevel(availableRollbacks);
+
+        if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_LOW) {
+            // Apply all available low impact rollbacks.
+            mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
+        } else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) {
+            // Check disable_high_impact_rollback device config before performing rollback
+            if (SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) {
+                return;
+            }
+            // Rollback one package at a time. If that doesn't resolve the issue, rollback
+            // next with same impact level.
+            mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason));
+        }
+    }
+
+    /**
+     * sort the available high impact rollbacks by first package name to have a deterministic order.
+     * Apply the first available rollback.
+     * @param availableRollbacks all available rollbacks
+     * @param rollbackReason reason to rollback
+     */
+    @WorkerThread
+    private void rollbackHighImpact(List<RollbackInfo> availableRollbacks,
+            @FailureReasons int rollbackReason) {
+        assertInWorkerThread();
+        List<RollbackInfo> highImpactRollbacks =
+                getRollbacksAvailableForImpactLevel(
+                        availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+
+        // sort rollbacks based on package name of the first package. This is to have a
+        // deterministic order of rollbacks.
+        List<RollbackInfo> sortedHighImpactRollbacks = highImpactRollbacks.stream().sorted(
+                Comparator.comparing(a -> a.getPackages().get(0).getPackageName())).toList();
+        VersionedPackage firstRollback =
+                sortedHighImpactRollbacks
+                        .get(0)
+                        .getPackages()
+                        .get(0)
+                        .getVersionRolledBackFrom();
+        Slog.i(TAG, "Rolling back high impact rollback for package: "
+                + firstRollback.getPackageName());
+        rollbackPackage(sortedHighImpactRollbacks.get(0), firstRollback, rollbackReason);
+    }
+
+    @WorkerThread
+    private void rollbackAll(@FailureReasons int rollbackReason) {
+        assertInWorkerThread();
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
+        if (useTwoPhaseRollback(rollbacks)) {
+            return;
+        }
+
+        Slog.i(TAG, "Rolling back all available rollbacks");
+        // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
+        // pending staged rollbacks are handled.
+        for (RollbackInfo rollback : rollbacks) {
+            if (rollback.isStaged()) {
+                mPendingStagedRollbackIds.add(rollback.getRollbackId());
+            }
+        }
+
+        for (RollbackInfo rollback : rollbacks) {
+            VersionedPackage firstRollback =
+                    rollback.getPackages().get(0).getVersionRolledBackFrom();
+            rollbackPackage(rollback, firstRollback, rollbackReason);
+        }
+    }
+
+    /**
+     * Rollback all available low impact rollbacks
+     * @param availableRollbacks all available rollbacks
+     * @param rollbackReason reason to rollbacks
+     */
+    @WorkerThread
+    private void rollbackAllLowImpact(
+            List<RollbackInfo> availableRollbacks, @FailureReasons int rollbackReason) {
+        assertInWorkerThread();
+
+        List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
+                availableRollbacks,
+                PackageManager.ROLLBACK_USER_IMPACT_LOW);
+        if (useTwoPhaseRollback(lowImpactRollbacks)) {
+            return;
+        }
+
+        Slog.i(TAG, "Rolling back all available low impact rollbacks");
+        logCrashRecoveryEvent(Log.DEBUG, "Rolling back all available. Reason: " + rollbackReason);
+        // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
+        // pending staged rollbacks are handled.
+        for (RollbackInfo rollback : lowImpactRollbacks) {
+            if (rollback.isStaged()) {
+                mPendingStagedRollbackIds.add(rollback.getRollbackId());
+            }
+        }
+
+        for (RollbackInfo rollback : lowImpactRollbacks) {
+            VersionedPackage firstRollback =
+                    rollback.getPackages().get(0).getVersionRolledBackFrom();
+            rollbackPackage(rollback, firstRollback, rollbackReason);
+        }
+    }
+
+    private List<RollbackInfo> getRollbacksAvailableForImpactLevel(
+            List<RollbackInfo> availableRollbacks, int impactLevel) {
+        return availableRollbacks.stream()
+                .filter(rollbackInfo -> rollbackInfo.getRollbackImpactLevel() == impactLevel)
+                .toList();
+    }
+
+    private int getMinRollbackImpactLevel(List<RollbackInfo> availableRollbacks) {
+        return availableRollbacks.stream()
+                .mapToInt(RollbackInfo::getRollbackImpactLevel)
+                .min()
+                .orElse(-1);
+    }
+
+    private int getUserImpactBasedOnRollbackImpactLevel(List<RollbackInfo> availableRollbacks) {
+        int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+        int minImpact = getMinRollbackImpactLevel(availableRollbacks);
+        switch (minImpact) {
+            case PackageManager.ROLLBACK_USER_IMPACT_LOW:
+                impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
+                break;
+            case PackageManager.ROLLBACK_USER_IMPACT_HIGH:
+                if (!SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) {
+                    impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90;
+                }
+                break;
+            default:
+                impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+        }
+        return impact;
+    }
+
+    @VisibleForTesting
+    Handler getHandler() {
+        return mHandler;
+    }
+}
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/WatchdogRollbackLogger.java
similarity index 100%
rename from services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
rename to packages/CrashRecovery/services/module/java/com/android/server/rollback/WatchdogRollbackLogger.java
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
new file mode 100644
index 0000000..0b7b986
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.File;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java
+ *
+ * @hide
+ */
+public class ArrayUtils {
+    private ArrayUtils() { /* cannot be instantiated */ }
+    public static final File[] EMPTY_FILE = new File[0];
+
+
+    /**
+     * Return first index of {@code value} in {@code array}, or {@code -1} if
+     * not found.
+     */
+    public static <T> int indexOf(@Nullable T[] array, T value) {
+        if (array == null) return -1;
+        for (int i = 0; i < array.length; i++) {
+            if (Objects.equals(array[i], value)) return i;
+        }
+        return -1;
+    }
+
+    /** @hide */
+    public static @NonNull File[] defeatNullable(@Nullable File[] val) {
+        return (val != null) ? val : EMPTY_FILE;
+    }
+
+    /**
+     * Checks if given array is null or has zero elements.
+     */
+    public static boolean isEmpty(@Nullable int[] array) {
+        return array == null || array.length == 0;
+    }
+
+    /**
+     * True if the byte array is null or has length 0.
+     */
+    public static boolean isEmpty(@Nullable byte[] array) {
+        return array == null || array.length == 0;
+    }
+
+    /**
+     * Converts from List of bytes to byte array
+     * @param list
+     * @return byte[]
+     */
+    public static byte[] toPrimitive(List<byte[]> list) {
+        if (list.size() == 0) {
+            return new byte[0];
+        }
+        int byteLen = list.get(0).length;
+        byte[] array = new byte[list.size() * byteLen];
+        for (int i = 0; i < list.size(); i++) {
+            for (int j = 0; j < list.get(i).length; j++) {
+                array[i * byteLen + j] = list.get(i)[j];
+            }
+        }
+        return array;
+    }
+
+    /**
+     * Adds value to given array if not already present, providing set-like
+     * behavior.
+     */
+    public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
+        return appendInt(cur, val, false);
+    }
+
+    /**
+     * Adds value to given array.
+     */
+    public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
+            boolean allowDuplicates) {
+        if (cur == null) {
+            return new int[] { val };
+        }
+        final int n = cur.length;
+        if (!allowDuplicates) {
+            for (int i = 0; i < n; i++) {
+                if (cur[i] == val) {
+                    return cur;
+                }
+            }
+        }
+        int[] ret = new int[n + 1];
+        System.arraycopy(cur, 0, ret, 0, n);
+        ret[n] = val;
+        return ret;
+    }
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
new file mode 100644
index 0000000..9c73fee
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Bits and pieces copied from hidden API of android.os.FileUtils.
+ *
+ * @hide
+ */
+public class FileUtils {
+    /**
+     * Read a text file into a String, optionally limiting the length.
+     *
+     * @param file     to read (will not seek, so things like /proc files are OK)
+     * @param max      length (positive for head, negative of tail, 0 for no limit)
+     * @param ellipsis to add of the file was truncated (can be null)
+     * @return the contents of the file, possibly truncated
+     * @throws IOException if something goes wrong reading the file
+     * @hide
+     */
+    public static @Nullable String readTextFile(@Nullable File file, @Nullable int max,
+            @Nullable String ellipsis) throws IOException {
+        InputStream input = new FileInputStream(file);
+        // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
+        // input stream, bytes read not equal to buffer size is not necessarily the correct
+        // indication for EOF; but it is true for BufferedInputStream due to its implementation.
+        BufferedInputStream bis = new BufferedInputStream(input);
+        try {
+            long size = file.length();
+            if (max > 0 || (size > 0 && max == 0)) {  // "head" mode: read the first N bytes
+                if (size > 0 && (max == 0 || size < max)) max = (int) size;
+                byte[] data = new byte[max + 1];
+                int length = bis.read(data);
+                if (length <= 0) return "";
+                if (length <= max) return new String(data, 0, length);
+                if (ellipsis == null) return new String(data, 0, max);
+                return new String(data, 0, max) + ellipsis;
+            } else if (max < 0) {  // "tail" mode: keep the last N
+                int len;
+                boolean rolled = false;
+                byte[] last = null;
+                byte[] data = null;
+                do {
+                    if (last != null) rolled = true;
+                    byte[] tmp = last;
+                    last = data;
+                    data = tmp;
+                    if (data == null) data = new byte[-max];
+                    len = bis.read(data);
+                } while (len == data.length);
+
+                if (last == null && len <= 0) return "";
+                if (last == null) return new String(data, 0, len);
+                if (len > 0) {
+                    rolled = true;
+                    System.arraycopy(last, len, last, 0, last.length - len);
+                    System.arraycopy(data, 0, last, last.length - len, len);
+                }
+                if (ellipsis == null || !rolled) return new String(last);
+                return ellipsis + new String(last);
+            } else {  // "cat" mode: size unknown, read it all in streaming fashion
+                ByteArrayOutputStream contents = new ByteArrayOutputStream();
+                int len;
+                byte[] data = new byte[1024];
+                do {
+                    len = bis.read(data);
+                    if (len > 0) contents.write(data, 0, len);
+                } while (len == data.length);
+                return contents.toString();
+            }
+        } finally {
+            bis.close();
+            input.close();
+        }
+    }
+
+    /**
+     * Perform an fsync on the given FileOutputStream. The stream at this
+     * point must be flushed but not yet closed.
+     *
+     * @hide
+     */
+    public static boolean sync(FileOutputStream stream) {
+        try {
+            if (stream != null) {
+                stream.getFD().sync();
+            }
+            return true;
+        } catch (IOException e) {
+        }
+        return false;
+    }
+
+    /**
+     * List the files in the directory or return empty file.
+     *
+     * @hide
+     */
+    public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
+        return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
+            : ArrayUtils.EMPTY_FILE;
+    }
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/LongArrayQueue.java b/packages/CrashRecovery/services/module/java/com/android/util/LongArrayQueue.java
new file mode 100644
index 0000000..9a24ada
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/util/LongArrayQueue.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import libcore.util.EmptyArray;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Copied from frameworks/base/core/java/android/util/LongArrayQueue.java
+ *
+ * @hide
+ */
+public class LongArrayQueue {
+
+    private long[] mValues;
+    private int mSize;
+    private int mHead;
+    private int mTail;
+
+    private long[] newUnpaddedLongArray(int num) {
+        return new long[num];
+    }
+    /**
+     * Initializes a queue with the given starting capacity.
+     *
+     * @param initialCapacity the capacity.
+     */
+    public LongArrayQueue(int initialCapacity) {
+        if (initialCapacity == 0) {
+            mValues = EmptyArray.LONG;
+        } else {
+            mValues = newUnpaddedLongArray(initialCapacity);
+        }
+        mSize = 0;
+        mHead = mTail = 0;
+    }
+
+    /**
+     * Initializes a queue with default starting capacity.
+     */
+    public LongArrayQueue() {
+        this(16);
+    }
+
+    /** @hide */
+    public static int growSize(int currentSize) {
+        return currentSize <= 4 ? 8 : currentSize * 2;
+    }
+
+    private void grow() {
+        if (mSize < mValues.length) {
+            throw new IllegalStateException("Queue not full yet!");
+        }
+        final int newSize = growSize(mSize);
+        final long[] newArray = newUnpaddedLongArray(newSize);
+        final int r = mValues.length - mHead; // Number of elements on and to the right of head.
+        System.arraycopy(mValues, mHead, newArray, 0, r);
+        System.arraycopy(mValues, 0, newArray, r, mHead);
+        mValues = newArray;
+        mHead = 0;
+        mTail = mSize;
+    }
+
+    /**
+     * Returns the number of elements in the queue.
+     */
+    public int size() {
+        return mSize;
+    }
+
+    /**
+     * Removes all elements from this queue.
+     */
+    public void clear() {
+        mSize = 0;
+        mHead = mTail = 0;
+    }
+
+    /**
+     * Adds a value to the tail of the queue.
+     *
+     * @param value the value to be added.
+     */
+    public void addLast(long value) {
+        if (mSize == mValues.length) {
+            grow();
+        }
+        mValues[mTail] = value;
+        mTail = (mTail + 1) % mValues.length;
+        mSize++;
+    }
+
+    /**
+     * Removes an element from the head of the queue.
+     *
+     * @return the element at the head of the queue.
+     * @throws NoSuchElementException if the queue is empty.
+     */
+    public long removeFirst() {
+        if (mSize == 0) {
+            throw new NoSuchElementException("Queue is empty!");
+        }
+        final long ret = mValues[mHead];
+        mHead = (mHead + 1) % mValues.length;
+        mSize--;
+        return ret;
+    }
+
+    /**
+     * Returns the element at the given position from the head of the queue, where 0 represents the
+     * head of the queue.
+     *
+     * @param position the position from the head of the queue.
+     * @return the element found at the given position.
+     * @throws IndexOutOfBoundsException if {@code position} < {@code 0} or
+     *                                   {@code position} >= {@link #size()}
+     */
+    public long get(int position) {
+        if (position < 0 || position >= mSize) {
+            throw new IndexOutOfBoundsException("Index " + position
+                + " not valid for a queue of size " + mSize);
+        }
+        final int index = (mHead + position) % mValues.length;
+        return mValues[index];
+    }
+
+    /**
+     * Returns the element at the head of the queue, without removing it.
+     *
+     * @return the element at the head of the queue.
+     * @throws NoSuchElementException if the queue is empty
+     */
+    public long peekFirst() {
+        if (mSize == 0) {
+            throw new NoSuchElementException("Queue is empty!");
+        }
+        return mValues[mHead];
+    }
+
+    /**
+     * Returns the element at the tail of the queue.
+     *
+     * @return the element at the tail of the queue.
+     * @throws NoSuchElementException if the queue is empty.
+     */
+    public long peekLast() {
+        if (mSize == 0) {
+            throw new NoSuchElementException("Queue is empty!");
+        }
+        final int index = (mTail == 0) ? mValues.length - 1 : mTail - 1;
+        return mValues[index];
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        if (mSize <= 0) {
+            return "{}";
+        }
+
+        final StringBuilder buffer = new StringBuilder(mSize * 64);
+        buffer.append('{');
+        buffer.append(get(0));
+        for (int i = 1; i < mSize; i++) {
+            buffer.append(", ");
+            buffer.append(get(i));
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+}
diff --git a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
new file mode 100644
index 0000000..50823f5
--- /dev/null
+++ b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import com.android.modules.utils.TypedXmlPullParser;
+
+import libcore.util.XmlObjectFactory;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ *  Bits and pieces copied from hidden API of
+ *  frameworks/base/core/java/com/android/internal/util/XmlUtils.java
+ *
+ * @hide
+ */
+public class XmlUtils {
+
+    private static final String STRING_ARRAY_SEPARATOR = ":";
+
+    /** @hide */
+    public static final void beginDocument(XmlPullParser parser, String firstElementName)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != parser.START_TAG
+            && type != parser.END_DOCUMENT) {
+            // Do nothing
+        }
+
+        if (type != parser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        if (!parser.getName().equals(firstElementName)) {
+            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName()
+                + ", expected " + firstElementName);
+        }
+    }
+
+    /** @hide */
+    public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
+            throws IOException, XmlPullParserException {
+        for (;;) {
+            int type = parser.next();
+            if (type == XmlPullParser.END_DOCUMENT
+                    || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
+                return false;
+            }
+            if (type == XmlPullParser.START_TAG
+                    && parser.getDepth() == outerDepth + 1) {
+                return true;
+            }
+        }
+    }
+
+    private static XmlPullParser newPullParser() {
+        try {
+            XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+            return parser;
+        } catch (XmlPullParserException e) {
+            throw new AssertionError();
+        }
+    }
+
+    /** @hide */
+    public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
+            throws IOException {
+        final byte[] magic = new byte[4];
+        if (in instanceof FileInputStream) {
+            try {
+                Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0);
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        } else {
+            if (!in.markSupported()) {
+                in = new BufferedInputStream(in);
+            }
+            in.mark(8);
+            in.read(magic);
+            in.reset();
+        }
+
+        final TypedXmlPullParser xml;
+        xml = (TypedXmlPullParser) newPullParser();
+        try {
+            xml.setInput(in, "UTF_8");
+        } catch (XmlPullParserException e) {
+            throw new IOException(e);
+        }
+        return xml;
+    }
+}
diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/packages/CrashRecovery/services/platform/java/com/android/server/ExplicitHealthCheckController.java
similarity index 100%
rename from services/core/java/com/android/server/ExplicitHealthCheckController.java
rename to packages/CrashRecovery/services/platform/java/com/android/server/ExplicitHealthCheckController.java
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
similarity index 100%
rename from services/core/java/com/android/server/PackageWatchdog.java
rename to packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
diff --git a/services/core/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
similarity index 100%
rename from services/core/java/com/android/server/RescueParty.java
rename to packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryModule.java b/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryModule.java
similarity index 100%
copy from services/core/java/com/android/server/crashrecovery/CrashRecoveryModule.java
copy to packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryModule.java
diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java b/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
similarity index 92%
copy from services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
copy to packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
index 3eb3380..2e2a937 100644
--- a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
@@ -18,7 +18,7 @@
 
 import android.os.Environment;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
+import android.util.Log;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -41,7 +41,7 @@
 
     /** Persist recovery related events in crashrecovery events file.**/
     public static void logCrashRecoveryEvent(int priority, String msg) {
-        Slog.println(priority, TAG, msg);
+        Log.println(priority, TAG, msg);
         try {
             File fname = getCrashRecoveryEventsFile();
             synchronized (sFileLock) {
@@ -52,7 +52,7 @@
                 pw.close();
             }
         } catch (IOException e) {
-            Slog.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage());
+            Log.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage());
         }
     }
 
@@ -72,7 +72,7 @@
                     pw.println(line);
                 }
             } catch (IOException e) {
-                Slog.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage());
+                Log.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage());
             }
         }
         pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
similarity index 100%
rename from services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
rename to packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/WatchdogRollbackLogger.java
similarity index 100%
copy from services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
copy to packages/CrashRecovery/services/platform/java/com/android/server/rollback/WatchdogRollbackLogger.java
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
index c48e7e4..8df8a07 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
@@ -33,7 +33,6 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import com.android.compose.rememberSystemUiController
-import com.android.compose.theme.LocalAndroidColorScheme
 import androidx.compose.ui.unit.dp
 import com.android.credentialmanager.common.material.ModalBottomSheetLayout
 import com.android.credentialmanager.common.material.ModalBottomSheetValue
@@ -57,7 +56,7 @@
         )
         androidx.compose.material3.ModalBottomSheet(
                 onDismissRequest = onDismiss,
-                containerColor = LocalAndroidColorScheme.current.surfaceBright,
+                containerColor = MaterialTheme.colorScheme.surfaceBright,
                 sheetState = state,
                 content = {
                     Box(
@@ -91,7 +90,7 @@
             setBottomSheetSystemBarsColor(sysUiController)
         }
         ModalBottomSheetLayout(
-                sheetBackgroundColor = LocalAndroidColorScheme.current.surfaceBright,
+                sheetBackgroundColor = MaterialTheme.colorScheme.surfaceBright,
                 modifier = Modifier.background(Color.Transparent),
                 sheetState = state,
                 sheetContent = { sheetContent() },
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
index 006a2d9..426fec2 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
@@ -29,12 +29,12 @@
 import androidx.compose.foundation.lazy.LazyListScope
 import androidx.compose.material3.Card
 import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.credentialmanager.ui.theme.Shapes
 
 /**
@@ -54,7 +54,7 @@
         modifier = modifier.fillMaxWidth().wrapContentHeight(),
         border = null,
         colors = CardDefaults.cardColors(
-            containerColor = LocalAndroidColorScheme.current.surfaceBright,
+            containerColor = MaterialTheme.colorScheme.surfaceBright,
         ),
     ) {
         if (topAppBar != null) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index 2c3c63b..84078c4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -31,6 +31,7 @@
 import androidx.compose.material.icons.outlined.Lock
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.SuggestionChip
 import androidx.compose.material3.SuggestionChipDefaults
 import androidx.compose.runtime.Composable
@@ -52,7 +53,6 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.credentialmanager.ui.theme.EntryShape
 import com.android.credentialmanager.ui.theme.Shapes
 
@@ -172,7 +172,7 @@
                             // Decorative purpose only.
                             contentDescription = null,
                             modifier = Modifier.size(24.dp),
-                            tint = LocalAndroidColorScheme.current.onSurfaceVariant,
+                            tint = MaterialTheme.colorScheme.onSurfaceVariant,
                         )
                     }
                 }
@@ -186,7 +186,7 @@
                         Icon(
                             modifier = iconSize,
                             bitmap = iconImageBitmap,
-                            tint = LocalAndroidColorScheme.current.onSurfaceVariant,
+                            tint = MaterialTheme.colorScheme.onSurfaceVariant,
                             // Decorative purpose only.
                             contentDescription = null,
                         )
@@ -210,7 +210,7 @@
                     Icon(
                         modifier = iconSize,
                         imageVector = iconImageVector,
-                        tint = LocalAndroidColorScheme.current.onSurfaceVariant,
+                        tint = MaterialTheme.colorScheme.onSurfaceVariant,
                         // Decorative purpose only.
                         contentDescription = null,
                     )
@@ -222,7 +222,7 @@
                     Icon(
                         modifier = iconSize,
                         painter = iconPainter,
-                        tint = LocalAndroidColorScheme.current.onSurfaceVariant,
+                        tint = MaterialTheme.colorScheme.onSurfaceVariant,
                         // Decorative purpose only.
                         contentDescription = null,
                     )
@@ -233,9 +233,9 @@
         },
         border = null,
         colors = SuggestionChipDefaults.suggestionChipColors(
-            containerColor = LocalAndroidColorScheme.current.surfaceContainerHigh,
-            labelColor = LocalAndroidColorScheme.current.onSurfaceVariant,
-            iconContentColor = LocalAndroidColorScheme.current.onSurfaceVariant,
+            containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+            labelColor = MaterialTheme.colorScheme.onSurfaceVariant,
+            iconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
         ),
     )
 }
@@ -338,7 +338,7 @@
                         imageVector = navigationIcon,
                         contentDescription = navigationIconContentDescription,
                         modifier = Modifier.size(24.dp).autoMirrored(),
-                        tint = LocalAndroidColorScheme.current.onSurfaceVariant,
+                        tint = MaterialTheme.colorScheme.onSurfaceVariant,
                 )
             }
         }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
index 342af3b..37268ad 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
@@ -21,23 +21,23 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.runtime.Composable
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
-import com.android.compose.theme.LocalAndroidColorScheme
 
 @Composable
 fun CredentialListSectionHeader(text: String, isFirstSection: Boolean) {
     InternalSectionHeader(
         text = text,
-        color = LocalAndroidColorScheme.current.onSurfaceVariant,
+        color = MaterialTheme.colorScheme.onSurfaceVariant,
         applyTopPadding = !isFirstSection
     )
 }
 
 @Composable
 fun MoreAboutPasskeySectionHeader(text: String) {
-    InternalSectionHeader(text, LocalAndroidColorScheme.current.onSurface)
+    InternalSectionHeader(text, MaterialTheme.colorScheme.onSurface)
 }
 
 @Composable
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
index 244b604..41b4e9b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt
@@ -19,7 +19,10 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.navigationBars
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.icons.Icons
@@ -61,8 +64,15 @@
             )
         }
         Box(
-            modifier = Modifier
-                .align(Alignment.BottomCenter).wrapContentSize().padding(bottom = 18.dp)
+            modifier =
+            Modifier.align(Alignment.BottomCenter)
+                .wrapContentSize()
+                .padding(
+                    bottom =
+                    WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 24.dp,
+                    start = 24.dp,
+                    end = 24.dp,
+                )
         ) {
             Card(
                 shape = Shapes.medium,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
index b4075f1..d325ebb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
@@ -17,9 +17,9 @@
 package com.android.credentialmanager.common.ui
 
 import androidx.compose.runtime.Composable
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.ui.graphics.Color
 import com.android.compose.SystemUiController
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
 
 @Composable
@@ -34,7 +34,7 @@
         darkIcons = false
     )
     sysUiController.setNavigationBarColor(
-        color = LocalAndroidColorScheme.current.surfaceBright,
+        color = MaterialTheme.colorScheme.surfaceBright,
         darkIcons = false
     )
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
index 68c2244..3e999cb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
@@ -26,7 +26,6 @@
 import androidx.compose.ui.text.style.Hyphens
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
-import com.android.compose.theme.LocalAndroidColorScheme
 
 /**
  * The headline for a screen. E.g. "Create a passkey for X", "Choose a saved sign-in for X".
@@ -38,7 +37,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = LocalAndroidColorScheme.current.onSurface,
+        color = MaterialTheme.colorScheme.onSurface,
         textAlign = TextAlign.Center,
         style = MaterialTheme.typography.headlineSmall.copy(hyphens = Hyphens.Auto),
     )
@@ -52,7 +51,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = LocalAndroidColorScheme.current.onSurfaceVariant,
+        color = MaterialTheme.colorScheme.onSurfaceVariant,
         style = MaterialTheme.typography.bodyMedium.copy(hyphens = Hyphens.Auto),
     )
 }
@@ -70,7 +69,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = LocalAndroidColorScheme.current.onSurfaceVariant,
+        color = MaterialTheme.colorScheme.onSurfaceVariant,
         style = MaterialTheme.typography.bodySmall.copy(hyphens = Hyphens.Auto),
         overflow = TextOverflow.Ellipsis,
         maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE,
@@ -86,7 +85,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = LocalAndroidColorScheme.current.onSurface,
+        color = MaterialTheme.colorScheme.onSurface,
         style = MaterialTheme.typography.titleLarge.copy(hyphens = Hyphens.Auto),
     )
 }
@@ -104,7 +103,7 @@
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
-        color = LocalAndroidColorScheme.current.onSurface,
+        color = MaterialTheme.colorScheme.onSurface,
         style = MaterialTheme.typography.titleSmall.copy(hyphens = Hyphens.Auto),
         overflow = TextOverflow.Ellipsis,
         maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE,
@@ -160,7 +159,7 @@
         modifier = modifier.wrapContentSize(),
         text = text,
         textAlign = TextAlign.Center,
-        color = LocalAndroidColorScheme.current.onSurfaceVariant,
+        color = MaterialTheme.colorScheme.onSurfaceVariant,
         style = MaterialTheme.typography.labelLarge.copy(hyphens = Hyphens.Auto),
     )
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 4993a1f..d788891 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -30,6 +30,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.material3.Divider
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.NewReleases
 import androidx.compose.material.icons.filled.Add
@@ -46,7 +47,6 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.core.graphics.drawable.toBitmap
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.credentialmanager.CredentialSelectorViewModel
 import com.android.credentialmanager.R
 import com.android.credentialmanager.common.BiometricError
@@ -448,7 +448,7 @@
                 item {
                     Divider(
                         thickness = 1.dp,
-                        color = LocalAndroidColorScheme.current.outlineVariant,
+                        color = MaterialTheme.colorScheme.outlineVariant,
                         modifier = Modifier.padding(vertical = 16.dp)
                     )
                 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index d688a1a..824dd4a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -44,7 +44,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Process;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -101,8 +100,7 @@
     private int mActivityResultCode = Activity.RESULT_CANCELED;
     private int mPendingUserActionReason = -1;
 
-    private final boolean mLocalLOGV =
-            TextUtils.equals("userdebug", SystemProperties.get("ro.build.type", ""));
+    private final boolean mLocalLOGV = false;
     PackageManager mPm;
     AppOpsManager mAppOpsManager;
     UserManager mUserManager;
@@ -145,11 +143,6 @@
     private AlertDialog mDialog;
 
     private void startInstallConfirm() {
-        if (mLocalLOGV) {
-            Log.d(TAG, "startInstallConfirm mAppInfo = " + mAppInfo
-                    + ", existingUpdateOwner = " + getExistingUpdateOwner()
-                    + ", mOriginatingPackage = " + mOriginatingPackage);
-        }
         TextView viewToEnable;
 
         if (mAppInfo != null) {
@@ -190,10 +183,6 @@
         try {
             final String packageName = mPkgInfo.packageName;
             final InstallSourceInfo sourceInfo = mPm.getInstallSourceInfo(packageName);
-            if (mLocalLOGV) {
-                Log.d(TAG, "getExistingUpdateOwner mAppInfo = " + mAppInfo
-                        + ", packageName = " + packageName + ", sourceInfo = " + sourceInfo);
-            }
             return sourceInfo.getUpdateOwnerPackageName();
         } catch (NameNotFoundException e) {
             return null;
@@ -314,12 +303,6 @@
 
     private void initiateInstall() {
         final String existingUpdateOwner = getExistingUpdateOwner();
-        if (mLocalLOGV) {
-            Log.d(TAG, "initiateInstall mAppInfo = " + mAppInfo
-                    + ", existingUpdateOwner = " + existingUpdateOwner
-                    + ", mOriginatingPackage = " + mOriginatingPackage
-                    + ", mSessionId = " + mSessionId);
-        }
         if (mSessionId == SessionInfo.INVALID_ID &&
             !TextUtils.isEmpty(existingUpdateOwner) &&
             !TextUtils.equals(existingUpdateOwner, mOriginatingPackage)) {
@@ -831,28 +814,15 @@
 
         @Override
         public void onOpChanged(String op, String packageName) {
-            if (mLocalLOGV) {
-                Log.d(TAG, "UnknownSourcesListener onOpChanged op = " + op
-                        + ", packageName = " + packageName
-                        + ", mOriginatingPackage = " + mOriginatingPackage);
-            }
             if (!mOriginatingPackage.equals(packageName)) {
                 return;
             }
             unregister(this);
             mActiveUnknownSourcesListeners.remove(this);
-            if (mLocalLOGV) {
-                Log.d(TAG, "UnknownSourcesListener onOpChanged isDestroyed() = "
-                        + isDestroyed());
-            }
             if (isDestroyed()) {
                 return;
             }
             new Handler(Looper.getMainLooper()).postDelayed(() -> {
-                if (mLocalLOGV) {
-                    Log.d(TAG, "UnknownSourcesListener onOpChanged post isDestroyed()"
-                            + "= " + isDestroyed() + ", getIntent() = " + getIntent());
-                }
                 if (!isDestroyed()) {
                     startActivity(getIntent());
                     // The start flag (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP) doesn't
@@ -870,9 +840,6 @@
     }
 
     private void register(UnknownSourcesListener listener) {
-        if (mLocalLOGV) {
-            Log.d(TAG, "UnknownSourcesListener register");
-        }
         mAppOpsManager.startWatchingMode(
                 AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, mOriginatingPackage,
                 listener);
@@ -880,9 +847,6 @@
     }
 
     private void unregister(UnknownSourcesListener listener) {
-        if (mLocalLOGV) {
-            Log.d(TAG, "UnknownSourcesListener unregister");
-        }
         mAppOpsManager.stopWatchingMode(listener);
         mActiveUnknownSourcesListeners.remove(listener);
     }
diff --git a/packages/PrintSpooler/res/values-kn/strings.xml b/packages/PrintSpooler/res/values-kn/strings.xml
index 27279a7..93de16f 100644
--- a/packages/PrintSpooler/res/values-kn/strings.xml
+++ b/packages/PrintSpooler/res/values-kn/strings.xml
@@ -33,7 +33,7 @@
     <string name="pages_range_example" msgid="8558694453556945172">"ಉದಾ. 1—5,8,11—13"</string>
     <string name="print_preview" msgid="8010217796057763343">"ಮುದ್ರಣ ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"ಪೂರ್ವವೀಕ್ಷಣೆಗಾಗಿ PDF ವೀಕ್ಷಕವನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
-    <string name="printing_app_crashed" msgid="854477616686566398">"ಮುದ್ರಣದ ಅಪ್ಲಿಕೇಶನ್ ಕ್ರ್ಯಾಶ್ ಆಗಿದೆ"</string>
+    <string name="printing_app_crashed" msgid="854477616686566398">"ಮುದ್ರಣದ ಆ್ಯಪ್‌ ಕ್ರ್ಯಾಶ್ ಆಗಿದೆ"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"ಮುದ್ರಣ ಕಾರ್ಯ ರಚಿಸಲಾಗುತ್ತಿದೆ"</string>
     <string name="save_as_pdf" msgid="5718454119847596853">"PDF ರೂಪದಲ್ಲಿ ಸೇವ್ ಮಾಡಿ"</string>
     <string name="all_printers" msgid="5018829726861876202">"ಎಲ್ಲಾ ಪ್ರಿಂಟರ್‌ಗಳು…"</string>
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index b286182..601e001 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -57,7 +57,7 @@
  * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false)
  * 2. User doesn't set any title or icon for button.
  */
-public class ActionButtonsPreference extends Preference {
+public class ActionButtonsPreference extends Preference implements GroupSectionDividerMixin {
 
     private static final String TAG = "ActionButtonPreference";
     private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 6cd777e..10769ec 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -43,7 +43,7 @@
  * Banner message is a banner displaying important information (permission request, page error etc),
  * and provide actions for user to address. It requires a user action to be dismissed.
  */
-public class BannerMessagePreference extends Preference {
+public class BannerMessagePreference extends Preference implements GroupSectionDividerMixin {
 
     public enum AttentionLevel {
         HIGH(0, R.color.banner_background_attention_high, R.color.banner_accent_attention_high),
diff --git a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
index eb14746..84ff1bb 100644
--- a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
+++ b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
@@ -30,7 +30,7 @@
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0,
     defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
 
     init {
         layoutResource = R.layout.settingslib_expressive_preference_card
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml
index 33519cb..dd7eac7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml
@@ -26,12 +26,12 @@
         android:outlineAmbientShadowColor="@android:color/transparent"
         android:outlineSpotShadowColor="@android:color/transparent"
         android:background="@android:color/transparent"
-        android:theme="@style/Theme.CollapsingToolbar.Settings">
+        android:theme="@style/ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar">
 
         <Toolbar
             android:id="@+id/action_bar"
             android:layout_width="match_parent"
-            android:layout_height="?attr/actionBarSize"
+            android:layout_height="?android:attr/actionBarSize"
             android:theme="?android:attr/actionBarTheme"
             android:transitionName="shared_element_view"
             app:layout_collapseMode="pin"/>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
index c20beaf..02f171c 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
@@ -21,4 +21,15 @@
         <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item>
         <item name="colorAccent">@color/settingslib_accent_device_default_dark</item>
     </style>
-</resources>
\ No newline at end of file
+
+    <!--
+      ~ TODO(b/349675008): Remove this theme overlay once the platform bridge theme properly sets
+      ~ the MaterialComponents colors based on the platform theme.
+      -->
+    <style name="ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar">
+        <item name="elevationOverlayEnabled">true</item>
+        <item name="elevationOverlayColor">?attr/colorPrimary</item>
+        <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item>
+        <item name="colorAccent">@color/settingslib_accent_device_default_dark</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
index 9ecc297..4039317 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
@@ -21,4 +21,15 @@
         <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item>
         <item name="colorAccent">@color/settingslib_accent_device_default_light</item>
     </style>
-</resources>
\ No newline at end of file
+
+    <!--
+      ~ TODO(b/349675008): Remove this theme overlay once the platform bridge theme properly sets
+      ~ the MaterialComponents colors based on the platform theme.
+      -->
+    <style name="ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar">
+        <item name="elevationOverlayEnabled">true</item>
+        <item name="elevationOverlayColor">?attr/colorPrimary</item>
+        <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item>
+        <item name="colorAccent">@color/settingslib_accent_device_default_light</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml
new file mode 100644
index 0000000..bcb9baf
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright (C) 2024 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!-- See appcompat/appcompat/THEMES for the theme structure. -->
+<resources>
+    <!--
+      ~ Bridge theme overlay to simulate AppCompat themes based on a platform theme.
+      ~ Only non-widget attributes are included here since we should still use the platform widgets.
+      ~ Only public theme attributes (as in platform public-final.xml) can be referenced here since
+      ~ this is used in modules.
+      -->
+    <style name="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" parent="">
+        <!-- START Base.V7.Theme.AppCompat -->
+
+        <item name="colorBackgroundFloating">?android:colorBackgroundFloating</item>
+
+        <item name="isLightTheme">?android:isLightTheme</item>
+
+        <item name="selectableItemBackground">?android:selectableItemBackground</item>
+        <item name="selectableItemBackgroundBorderless">?android:selectableItemBackgroundBorderless</item>
+        <item name="homeAsUpIndicator">?android:homeAsUpIndicator</item>
+
+        <item name="dividerVertical">?android:dividerVertical</item>
+        <item name="dividerHorizontal">?android:dividerHorizontal</item>
+
+        <!-- List attributes -->
+        <item name="textAppearanceListItem">?android:textAppearanceListItem</item>
+        <item name="textAppearanceListItemSmall">?android:textAppearanceListItemSmall</item>
+        <item name="textAppearanceListItemSecondary">?android:textAppearanceListItemSecondary</item>
+        <item name="listPreferredItemHeight">?android:listPreferredItemHeight</item>
+        <item name="listPreferredItemHeightSmall">?android:listPreferredItemHeightSmall</item>
+        <item name="listPreferredItemHeightLarge">?android:listPreferredItemHeightLarge</item>
+        <item name="listPreferredItemPaddingLeft">?android:listPreferredItemPaddingLeft</item>
+        <item name="listPreferredItemPaddingRight">?android:listPreferredItemPaddingRight</item>
+        <item name="listPreferredItemPaddingStart">?android:listPreferredItemPaddingStart</item>
+        <item name="listPreferredItemPaddingEnd">?android:listPreferredItemPaddingEnd</item>
+
+        <!-- Color palette -->
+        <item name="colorPrimaryDark">?android:colorPrimaryDark</item>
+        <item name="colorPrimary">?android:colorPrimary</item>
+        <item name="colorAccent">?android:colorAccent</item>
+
+        <item name="colorControlNormal">?android:colorControlNormal</item>
+        <item name="colorControlActivated">?android:colorControlActivated</item>
+        <item name="colorControlHighlight">?android:colorControlHighlight</item>
+        <item name="colorButtonNormal">?android:colorButtonNormal</item>
+
+        <item name="colorError">?android:colorError</item>
+
+        <!-- END Base.V7.Theme.AppCompat -->
+    </style>
+    <style name="Base.ThemeOverlay.AppCompat.PlatformBridge" parent="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" />
+    <style name="ThemeOverlay.AppCompat.PlatformBridge" parent="Base.ThemeOverlay.AppCompat.PlatformBridge" />
+
+    <!--
+      ~ Bridge theme overlay to simulate MaterialComponents themes based on a platform theme.
+      -->
+    <style name="Base.V31.ThemeOverlay.MaterialComponents.PlatformBridge" parent="ThemeOverlay.AppCompat.PlatformBridge">
+        <!-- START Base.V14.Theme.MaterialComponents.Bridge -->
+        <!--
+          ~ This is copied as-is from the original bridge theme since it is guaranteed to not affect
+          ~ existing widgets.
+          -->
+
+        <item name="isMaterialTheme">true</item>
+
+        <item name="colorPrimaryVariant">@color/design_dark_default_color_primary_variant</item>
+        <item name="colorSecondary">@color/design_dark_default_color_secondary</item>
+        <item name="colorSecondaryVariant">@color/design_dark_default_color_secondary_variant</item>
+        <item name="colorSurface">@color/design_dark_default_color_surface</item>
+        <item name="colorPrimarySurface">?attr/colorSurface</item>
+        <item name="colorOnPrimary">@color/design_dark_default_color_on_primary</item>
+        <item name="colorOnSecondary">@color/design_dark_default_color_on_secondary</item>
+        <item name="colorOnBackground">@color/design_dark_default_color_on_background</item>
+        <item name="colorOnError">@color/design_dark_default_color_on_error</item>
+        <item name="colorOnSurface">@color/design_dark_default_color_on_surface</item>
+        <item name="colorOnPrimarySurface">?attr/colorOnSurface</item>
+
+        <item name="scrimBackground">@color/mtrl_scrim_color</item>
+        <item name="popupMenuBackground">@drawable/mtrl_popupmenu_background_overlay</item>
+
+        <item name="minTouchTargetSize">@dimen/mtrl_min_touch_target_size</item>
+
+        <!-- MaterialComponents Widget styles -->
+        <item name="badgeStyle">@style/Widget.MaterialComponents.Badge</item>
+        <item name="bottomAppBarStyle">@style/Widget.MaterialComponents.BottomAppBar</item>
+        <item name="chipStyle">@style/Widget.MaterialComponents.Chip.Action</item>
+        <item name="chipGroupStyle">@style/Widget.MaterialComponents.ChipGroup</item>
+        <item name="chipStandaloneStyle">@style/Widget.MaterialComponents.Chip.Entry</item>
+        <item name="circularProgressIndicatorStyle">@style/Widget.MaterialComponents.CircularProgressIndicator</item>
+        <item name="extendedFloatingActionButtonStyle">@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon</item>
+        <item name="linearProgressIndicatorStyle">@style/Widget.MaterialComponents.LinearProgressIndicator</item>
+        <item name="materialButtonStyle">@style/Widget.MaterialComponents.Button</item>
+        <item name="materialButtonOutlinedStyle">@style/Widget.MaterialComponents.Button.OutlinedButton</item>
+        <item name="materialButtonToggleGroupStyle">@style/Widget.MaterialComponents.MaterialButtonToggleGroup</item>
+        <item name="materialCardViewStyle">@style/Widget.MaterialComponents.CardView</item>
+        <item name="navigationRailStyle">@style/Widget.MaterialComponents.NavigationRailView</item>
+        <item name="sliderStyle">@style/Widget.MaterialComponents.Slider</item>
+
+        <!-- Type styles -->
+        <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item>
+        <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item>
+        <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item>
+        <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item>
+        <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item>
+        <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item>
+        <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>
+        <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item>
+        <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item>
+        <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
+        <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>
+        <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item>
+        <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item>
+
+        <!-- Shape styles -->
+        <item name="shapeAppearanceSmallComponent">
+          @style/ShapeAppearance.MaterialComponents.SmallComponent
+        </item>
+        <item name="shapeAppearanceMediumComponent">
+          @style/ShapeAppearance.MaterialComponents.MediumComponent
+        </item>
+        <item name="shapeAppearanceLargeComponent">
+          @style/ShapeAppearance.MaterialComponents.LargeComponent
+        </item>
+
+        <!-- Motion -->
+        <item name="motionEasingStandard">@string/material_motion_easing_standard</item>
+        <item name="motionEasingEmphasized">@string/material_motion_easing_emphasized</item>
+        <item name="motionEasingDecelerated">@string/material_motion_easing_decelerated</item>
+        <item name="motionEasingAccelerated">@string/material_motion_easing_accelerated</item>
+        <item name="motionEasingLinear">@string/material_motion_easing_linear</item>
+
+        <item name="motionDurationShort1">@integer/material_motion_duration_short_1</item>
+        <item name="motionDurationShort2">@integer/material_motion_duration_short_2</item>
+        <item name="motionDurationMedium1">@integer/material_motion_duration_medium_1</item>
+        <item name="motionDurationMedium2">@integer/material_motion_duration_medium_2</item>
+        <item name="motionDurationLong1">@integer/material_motion_duration_long_1</item>
+        <item name="motionDurationLong2">@integer/material_motion_duration_long_2</item>
+
+        <item name="motionPath">@integer/material_motion_path</item>
+
+        <!-- Elevation Overlays -->
+        <item name="elevationOverlayEnabled">true</item>
+        <item name="elevationOverlayColor">?attr/colorOnSurface</item>
+
+        <!-- END Base.V14.Theme.MaterialComponents.Bridge -->
+
+        <!-- START Base.V14.Theme.MaterialComponents -->
+        <!--
+          ~ Only a subset of widget attributes being actually used are included here since there are
+          ~ too many of them and they need to be investigated on a case-by-case basis.
+          -->
+
+        <!-- Framework, AppCompat, or Design Widget styles -->
+        <item name="appBarLayoutStyle">@style/Widget.MaterialComponents.AppBarLayout.Surface</item>
+
+        <!-- END Base.V14.Theme.MaterialComponents -->
+    </style>
+    <style name="Base.ThemeOverlay.MaterialComponents.PlatformBridge" parent="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" />
+    <style name="ThemeOverlay.MaterialComponents.PlatformBridge" parent="Base.ThemeOverlay.AppCompat.PlatformBridge" />
+</resources>
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
index 3d41337..5f1f8df 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -25,6 +25,7 @@
     fun contains(key: String): Boolean
 
     /** Gets default value of given key. */
+    @Suppress("UNCHECKED_CAST")
     fun <T : Any> getDefaultValue(key: String, valueType: Class<T>): T? =
         when (valueType) {
             Boolean::class.javaObjectType -> false
@@ -56,6 +57,7 @@
 
     override fun contains(key: String): Boolean = sharedPreferences.contains(key)
 
+    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
     override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
         when (valueType) {
             Boolean::class.javaObjectType -> sharedPreferences.getBoolean(key, false)
@@ -68,6 +70,7 @@
         }
             as T?
 
+    @Suppress("UNCHECKED_CAST")
     override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
         if (value == null) {
             sharedPreferences.edit().remove(key).apply()
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
index 4aef0fc..53507fe 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver
 import android.content.Context
+import android.net.Uri
 import android.provider.Settings.Global
 import android.provider.Settings.SettingNotFoundException
 
@@ -29,11 +30,15 @@
 class SettingsGlobalStore private constructor(contentResolver: ContentResolver) :
     SettingsStore(contentResolver) {
 
+    override val uri: Uri
+        get() = Global.getUriFor("")
+
     override val tag: String
         get() = "SettingsGlobalStore"
 
     override fun contains(key: String): Boolean = Global.getString(contentResolver, key) != null
 
+    @Suppress("UNCHECKED_CAST")
     override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
         try {
             when (valueType) {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
index 9f41ecb..ca7fd7b 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver
 import android.content.Context
+import android.net.Uri
 import android.provider.Settings.Secure
 import android.provider.Settings.SettingNotFoundException
 
@@ -29,11 +30,15 @@
 class SettingsSecureStore private constructor(contentResolver: ContentResolver) :
     SettingsStore(contentResolver) {
 
+    override val uri: Uri
+        get() = Secure.getUriFor("")
+
     override val tag: String
         get() = "SettingsSecureStore"
 
     override fun contains(key: String): Boolean = Secure.getString(contentResolver, key) != null
 
+    @Suppress("UNCHECKED_CAST")
     override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
         try {
             when (valueType) {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
index 5981688..62d3fc3 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -21,13 +21,12 @@
 import android.net.Uri
 import android.os.Handler
 import android.os.Looper
-import android.provider.Settings
 import android.util.Log
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
 
 /** Base class of the Settings provider data stores. */
-open abstract class SettingsStore(protected val contentResolver: ContentResolver) :
+abstract class SettingsStore(protected val contentResolver: ContentResolver) :
     KeyedDataObservable<String>(), KeyValueStore {
 
     /**
@@ -70,13 +69,12 @@
     private fun onObserverAdded() {
         if (counter.getAndIncrement() != 0) return
         Log.i(tag, "registerContentObserver")
-        contentResolver.registerContentObserver(
-            Settings.Global.getUriFor(""),
-            true,
-            contentObserver,
-        )
+        contentResolver.registerContentObserver(uri, true, contentObserver)
     }
 
+    /** The URI to watch for any key change. */
+    protected abstract val uri: Uri
+
     override fun removeObserver(observer: KeyedObserver<String?>) =
         if (super.removeObserver(observer)) {
             onObserverRemoved()
@@ -99,6 +97,37 @@
         contentResolver.unregisterContentObserver(contentObserver)
     }
 
+    /** Gets the boolean value of given key. */
+    fun getBoolean(key: String): Boolean? = getValue(key, Boolean::class.javaObjectType)
+
+    /** Sets boolean value for given key, null value means delete the key from data store. */
+    fun setBoolean(key: String, value: Boolean?) =
+        setValue(key, Boolean::class.javaObjectType, value)
+
+    /** Gets the float value of given key. */
+    fun getFloat(key: String): Float? = getValue(key, Float::class.javaObjectType)
+
+    /** Sets float value for given key, null value means delete the key from data store. */
+    fun setFloat(key: String, value: Float?) = setValue(key, Float::class.javaObjectType, value)
+
+    /** Gets the int value of given key. */
+    fun getInt(key: String): Int? = getValue(key, Int::class.javaObjectType)
+
+    /** Sets int value for given key, null value means delete the key from data store. */
+    fun setInt(key: String, value: Int?) = setValue(key, Int::class.javaObjectType, value)
+
+    /** Gets the long value of given key. */
+    fun getLong(key: String): Long? = getValue(key, Long::class.javaObjectType)
+
+    /** Sets long value for given key, null value means delete the key from data store. */
+    fun setLong(key: String, value: Long?) = setValue(key, Long::class.javaObjectType, value)
+
+    /** Gets the string value of given key. */
+    fun getString(key: String): String? = getValue(key, String::class.javaObjectType)
+
+    /** Sets string value for given key, null value means delete the key from data store. */
+    fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
+
     /** Tag for logging. */
     abstract val tag: String
 }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
index 6cca7ed..20a74d3 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver
 import android.content.Context
+import android.net.Uri
 import android.provider.Settings.SettingNotFoundException
 import android.provider.Settings.System
 
@@ -29,11 +30,15 @@
 class SettingsSystemStore private constructor(contentResolver: ContentResolver) :
     SettingsStore(contentResolver) {
 
+    override val uri: Uri
+        get() = System.getUriFor("")
+
     override val tag: String
         get() = "SettingsSystemStore"
 
     override fun contains(key: String): Boolean = System.getString(contentResolver, key) != null
 
+    @Suppress("UNCHECKED_CAST")
     override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
         try {
             when (valueType) {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index ea4ac2c..635f690 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,10 +20,13 @@
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
 
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -67,7 +70,8 @@
     @VisibleForTesting
     DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) {
         mSecureSettings = secureSettings;
-        mPosturesHelper = new PosturesHelper(context);
+
+        mPosturesHelper = new PosturesHelper(context, getDeviceStateManager(context));
         mPostureRotationLockDefaults =
                 context.getResources()
                         .getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
@@ -76,6 +80,14 @@
         listenForSettingsChange();
     }
 
+    @Nullable
+    private DeviceStateManager getDeviceStateManager(Context context) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            return context.getSystemService(DeviceStateManager.class);
+        }
+        return null;
+    }
+
     /** Returns a singleton instance of this class */
     public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) {
         if (sSingleton == null) {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt
index 6a13eb8..14d59f2 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt
@@ -17,6 +17,12 @@
 package com.android.settingslib.devicestate
 
 import android.content.Context
+import android.hardware.devicestate.DeviceState
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+import android.hardware.devicestate.DeviceStateManager
 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
@@ -24,37 +30,68 @@
 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN
 import android.provider.Settings.Secure.DeviceStateRotationLockKey
 import com.android.internal.R
+import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags
 
 /** Helps to convert between device state and posture. */
-class PosturesHelper(context: Context) {
+class PosturesHelper(context: Context, deviceStateManager: DeviceStateManager?) {
 
-    private val foldedDeviceStates =
-        context.resources.getIntArray(R.array.config_foldedDeviceStates)
-    private val halfFoldedDeviceStates =
-        context.resources.getIntArray(R.array.config_halfFoldedDeviceStates)
-    private val unfoldedDeviceStates =
-        context.resources.getIntArray(R.array.config_openDeviceStates)
-    private val rearDisplayDeviceStates =
-        context.resources.getIntArray(R.array.config_rearDisplayDeviceStates)
+    private val postures: Map<Int, List<Int>>
 
-    @DeviceStateRotationLockKey
-    fun deviceStateToPosture(deviceState: Int): Int {
-        return when (deviceState) {
-            in foldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_FOLDED
-            in halfFoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
-            in unfoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_UNFOLDED
-            in rearDisplayDeviceStates -> DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
-            else -> DEVICE_STATE_ROTATION_KEY_UNKNOWN
+    init {
+        if (deviceStateManager != null && DeviceStateManagerFlags.deviceStatePropertyMigration()) {
+            postures =
+                deviceStateManager.supportedDeviceStates.groupBy { it.toPosture() }
+                    .filterKeys { it != DEVICE_STATE_ROTATION_KEY_UNKNOWN }
+                    .mapValues { it.value.map { it.identifier }}
+        } else {
+            val foldedDeviceStates =
+                context.resources.getIntArray(R.array.config_foldedDeviceStates).toList()
+            val halfFoldedDeviceStates =
+                context.resources.getIntArray(R.array.config_halfFoldedDeviceStates).toList()
+            val unfoldedDeviceStates =
+                context.resources.getIntArray(R.array.config_openDeviceStates).toList()
+            val rearDisplayDeviceStates =
+                context.resources.getIntArray(R.array.config_rearDisplayDeviceStates).toList()
+
+            postures =
+                mapOf(
+                    DEVICE_STATE_ROTATION_KEY_FOLDED to foldedDeviceStates,
+                    DEVICE_STATE_ROTATION_KEY_HALF_FOLDED to halfFoldedDeviceStates,
+                    DEVICE_STATE_ROTATION_KEY_UNFOLDED to unfoldedDeviceStates,
+                    DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY to rearDisplayDeviceStates
+                )
         }
     }
 
+    @DeviceStateRotationLockKey
+    fun deviceStateToPosture(deviceState: Int): Int {
+        return postures.filterValues { it.contains(deviceState) }.keys.firstOrNull()
+            ?: DEVICE_STATE_ROTATION_KEY_UNKNOWN
+    }
+
     fun postureToDeviceState(@DeviceStateRotationLockKey posture: Int): Int? {
-        return when (posture) {
-            DEVICE_STATE_ROTATION_KEY_FOLDED -> foldedDeviceStates.firstOrNull()
-            DEVICE_STATE_ROTATION_KEY_HALF_FOLDED -> halfFoldedDeviceStates.firstOrNull()
-            DEVICE_STATE_ROTATION_KEY_UNFOLDED -> unfoldedDeviceStates.firstOrNull()
-            DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY -> rearDisplayDeviceStates.firstOrNull()
-            else -> null
+        return postures[posture]?.firstOrNull()
+    }
+
+    /**
+     * Maps a [DeviceState] to the corresponding [DeviceStateRotationLockKey] value based on the
+     * properties of the state.
+     */
+    @DeviceStateRotationLockKey
+    private fun DeviceState.toPosture(): Int {
+        return if (hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) {
+            DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+        } else if (hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) {
+            DEVICE_STATE_ROTATION_KEY_FOLDED
+        } else if (hasProperties(
+                PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+                PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+            )) {
+            DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+        } else if (hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) {
+            DEVICE_STATE_ROTATION_KEY_UNFOLDED
+        } else {
+            DEVICE_STATE_ROTATION_KEY_UNKNOWN
         }
     }
 }
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index 8c5d877..8fb16d8 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -78,6 +78,10 @@
         for (activityClass in request.activityClasses) {
             add(activityClass)
         }
+        // Temporarily add all screens
+        for (key in PreferenceScreenRegistry.preferenceScreens.keys) {
+            addPreferenceScreenFromRegistry(key, Activity::class.java)
+        }
     }
 
     fun build() = builder.build()
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index cd8f584..12890fe 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -21,6 +21,7 @@
         "SettingsLibColor",
         "androidx.preference_preference",
         "lottie",
+        "SettingsLibSettingsTheme",
         "settingslib_illustrationpreference_flags_lib",
     ],
 
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index a0599bb..adc4f316 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -55,7 +55,7 @@
 /**
  * IllustrationPreference is a preference that can play lottie format animation
  */
-public class IllustrationPreference extends Preference {
+public class IllustrationPreference extends Preference implements GroupSectionDividerMixin {
 
     private static final String TAG = "IllustrationPreference";
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index e3f8fbb..2e3ee32 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -20,8 +20,6 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingTop="@dimen/settingslib_switchbar_margin"
     android:paddingBottom="@dimen/settingslib_switchbar_margin"
     android:orientation="vertical">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 255b2c9..3e0e184 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -20,8 +20,6 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingTop="@dimen/settingslib_switchbar_margin"
     android:paddingBottom="@dimen/settingslib_switchbar_margin"
     android:orientation="vertical">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
index 4425ef0..f75d9b2 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
@@ -20,8 +20,6 @@
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:paddingVertical="@dimen/settingslib_expressive_space_small1"
     android:orientation="vertical">
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index bf34db9..7c0eaea 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -18,11 +18,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="match_parent"
-    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+    android:layout_width="match_parent">
 
     <TextView
         android:id="@+id/switch_text"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
index bef6e35..fa908a4 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
@@ -18,6 +18,10 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:importantForAccessibility="no">
 
     <com.android.settingslib.widget.MainSwitchBar
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index d895c87..3394874 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -36,7 +36,8 @@
  * This component is used as the main switch of the page
  * to enable or disable the prefereces on the page.
  */
-public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener {
+public class MainSwitchPreference extends TwoStatePreference
+        implements OnCheckedChangeListener, GroupSectionDividerMixin {
 
     private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>();
 
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
index f39f3a0..33f2dbf 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
@@ -31,6 +31,7 @@
  * information:
  * - [PreferenceTitleProvider]: provide dynamic title content
  * - [PreferenceSummaryProvider]: provide dynamic summary content (e.g. based on preference value)
+ * - [PreferenceIconProvider]: provide dynamic icon content (e.g. based on flag)
  * - [PreferenceAvailabilityProvider]: provide preference availability (e.g. based on flag)
  * - [PreferenceLifecycleProvider]: provide the lifecycle callbacks and notify state change
  *
@@ -160,6 +161,19 @@
             this is PreferenceSummaryProvider -> getSummary(context)
             else -> null
         }
+
+    /**
+     * Returns the preference icon.
+     *
+     * Implement [PreferenceIconProvider] interface if icon content is provided dynamically
+     * (e.g. icon is provided based on flag value).
+     */
+    fun getPreferenceIcon(context: Context): Int =
+        when {
+            icon != 0 -> icon
+            this is PreferenceIconProvider -> getIcon(context)
+            else -> 0
+        }
 }
 
 /** Metadata of preference group. */
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index 48798da..6646d6c3 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -34,7 +34,7 @@
         ImmutableMap.of()
     }
 
-    private val preferenceScreens: PreferenceScreenMap
+    val preferenceScreens: PreferenceScreenMap
         get() = preferenceScreensSupplier.get()
 
     private var readWritePermitProvider: ReadWritePermitProvider? = null
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
index a3aa85d..98cba1c 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
@@ -41,6 +41,17 @@
 }
 
 /**
+ * Interface to provide dynamic preference icon.
+ *
+ * Implement this interface implies that the preference icon should not be cached for indexing.
+ */
+interface PreferenceIconProvider {
+
+    /** Provides preference icon. */
+    fun getIcon(context: Context): Int
+}
+
+/**
  * Interface to provide the state of preference availability.
  *
  * UI framework normally does not show the preference widget if it is unavailable.
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index 5e69895..ef3d372 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -62,12 +62,13 @@
     fun bind(preference: Preference, metadata: PreferenceMetadata) {
         metadata.apply {
             preference.key = key
-            if (icon != 0) {
-                preference.setIcon(icon)
+            val context = preference.context
+            val preferenceIcon = metadata.getPreferenceIcon(context)
+            if (preferenceIcon != 0) {
+                preference.setIcon(preferenceIcon)
             } else {
                 preference.icon = null
             }
-            val context = preference.context
             val isPreferenceScreen = preference is PreferenceScreen
             preference.peekExtras()?.clear()
             extras(context)?.let { preference.extras.putAll(it) }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
index 02acfca..c2728b4 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt
@@ -37,6 +37,7 @@
     override fun getString(key: String, defValue: String?): String? =
         keyValueStore.getValue(key, String::class.javaObjectType) ?: defValue
 
+    @Suppress("UNCHECKED_CAST")
     override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? =
         (keyValueStore.getValue(key, Set::class.javaObjectType) as Set<String>?) ?: defValues
 
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index a270681..d501f4f 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.os.Bundle
+import android.util.Log
 import androidx.annotation.XmlRes
 import androidx.preference.PreferenceFragmentCompat
 import androidx.preference.PreferenceScreen
@@ -40,9 +41,14 @@
         createPreferenceScreen(PreferenceScreenFactory(this))
 
     override fun createPreferenceScreen(factory: PreferenceScreenFactory): PreferenceScreen? {
+        preferenceScreenBindingHelper?.close()
+        preferenceScreenBindingHelper = null
+
         val context = factory.context
         fun createPreferenceScreenFromResource() =
-            factory.inflate(getPreferenceScreenResId(context))
+            factory.inflate(getPreferenceScreenResId(context))?.also {
+                Log.i(TAG, "Load screen " + it.key + " from resource")
+            }
 
         val screenCreator =
             getPreferenceScreenCreator(context) ?: return createPreferenceScreenFromResource()
@@ -50,10 +56,12 @@
         val preferenceHierarchy = screenCreator.getPreferenceHierarchy(context)
         val preferenceScreen =
             if (screenCreator.hasCompleteHierarchy()) {
+                Log.i(TAG, "Load screen " + screenCreator.key + " from hierarchy")
                 factory.getOrCreatePreferenceScreen().apply {
                     inflatePreferenceHierarchy(preferenceBindingFactory, preferenceHierarchy)
                 }
             } else {
+                Log.i(TAG, "Screen " + screenCreator.key + " is hybrid")
                 createPreferenceScreenFromResource()?.also {
                     bindRecursively(it, preferenceBindingFactory, preferenceHierarchy)
                 } ?: return null
@@ -81,6 +89,14 @@
 
     override fun onDestroy() {
         preferenceScreenBindingHelper?.close()
+        preferenceScreenBindingHelper = null
         super.onDestroy()
     }
+
+    protected fun getPreferenceKeysInHierarchy(): Set<String> =
+        preferenceScreenBindingHelper?.getPreferences()?.map { it.key }?.toSet() ?: setOf()
+
+    companion object {
+        private const val TAG = "PreferenceFragment"
+    }
 }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 3610894..efa1faf 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -20,8 +20,10 @@
 import android.os.Handler
 import android.os.Looper
 import androidx.preference.Preference
+import androidx.preference.PreferenceDataStore
 import androidx.preference.PreferenceGroup
 import androidx.preference.PreferenceScreen
+import com.android.settingslib.datastore.KeyValueStore
 import com.android.settingslib.datastore.KeyedDataObservable
 import com.android.settingslib.datastore.KeyedObservable
 import com.android.settingslib.datastore.KeyedObserver
@@ -45,7 +47,7 @@
     context: Context,
     private val preferenceBindingFactory: PreferenceBindingFactory,
     private val preferenceScreen: PreferenceScreen,
-    preferenceHierarchy: PreferenceHierarchy,
+    private val preferenceHierarchy: PreferenceHierarchy,
 ) : KeyedDataObservable<String>(), AutoCloseable {
 
     private val handler = Handler(Looper.getMainLooper())
@@ -133,6 +135,8 @@
         }
     }
 
+    fun getPreferences() = preferenceHierarchy.getAllPreferences()
+
     override fun close() {
         removeObserver(preferenceObserver)
         val context = preferenceScreen.context
@@ -179,15 +183,22 @@
         private fun PreferenceGroup.bindRecursively(
             preferenceBindingFactory: PreferenceBindingFactory,
             preferences: Map<String, PreferenceMetadata>,
+            storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(),
         ) {
             preferenceBindingFactory.bind(this, preferences[key])
             val count = preferenceCount
             for (index in 0 until count) {
                 val preference = getPreference(index)
                 if (preference is PreferenceGroup) {
-                    preference.bindRecursively(preferenceBindingFactory, preferences)
+                    preference.bindRecursively(preferenceBindingFactory, preferences, storages)
                 } else {
-                    preferenceBindingFactory.bind(preference, preferences[preference.key])
+                    preferences[preference.key]?.let {
+                        preferenceBindingFactory.getPreferenceBinding(it)?.bind(preference, it)
+                        (it as? PersistentPreference<*>)?.storage(context)?.let { storage ->
+                            preference.preferenceDataStore =
+                                storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
+                        }
+                    }
                 }
             }
         }
diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
index 4d5f85f..06214eb 100644
--- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
+++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
@@ -27,6 +27,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.atomic.AtomicBoolean
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,7 +37,7 @@
 abstract class CatalystScreenTestCase {
     @get:Rule val setFlagsRule = SetFlagsRule()
 
-    protected val context: Context = ApplicationProvider.getApplicationContext()
+    protected val appContext: Context = ApplicationProvider.getApplicationContext()
 
     /** Catalyst screen. */
     protected abstract val preferenceScreenCreator: PreferenceScreenCreator
@@ -49,15 +50,15 @@
      * catalyst screen (flag is enabled).
      */
     @Test
-    fun migration() {
+    open fun migration() {
         enableCatalystScreen()
-        assertThat(preferenceScreenCreator.isFlagEnabled(context)).isTrue()
-        val catalystScreen = stringifyPreferenceScreen()
-        Log.i("Catalyst", catalystScreen)
+        assertThat(preferenceScreenCreator.isFlagEnabled(appContext)).isTrue()
+        val catalystScreen = dumpPreferenceScreen()
+        Log.i(TAG, catalystScreen)
 
         disableCatalystScreen()
-        assertThat(preferenceScreenCreator.isFlagEnabled(context)).isFalse()
-        val legacyScreen = stringifyPreferenceScreen()
+        assertThat(preferenceScreenCreator.isFlagEnabled(appContext)).isFalse()
+        val legacyScreen = dumpPreferenceScreen()
 
         assertThat(catalystScreen).isEqualTo(legacyScreen)
     }
@@ -82,16 +83,34 @@
         setFlagsRule.disableFlags(flagName)
     }
 
-    private fun stringifyPreferenceScreen(): String {
+    private fun dumpPreferenceScreen(): String {
+        // Dump threads for troubleshooting when the test thread is stuck.
+        // Latest junit Timeout rule supports similar feature but it is not yet available on AOSP.
+        val taskFinished = AtomicBoolean()
+        Thread {
+                Thread.sleep(20000)
+                if (!taskFinished.get()) dumpThreads()
+            }
+            .apply {
+                isDaemon = true
+                start()
+            }
+
         @Suppress("UNCHECKED_CAST")
         val clazz = preferenceScreenCreator.fragmentClass() as Class<PreferenceFragmentCompat>
         val builder = StringBuilder()
-        FragmentScenario.launch(clazz).use {
-            it.onFragment { fragment -> fragment.preferenceScreen.toString(builder) }
+        launchFragmentScenario(clazz).use {
+            it.onFragment { fragment ->
+                taskFinished.set(true)
+                fragment.preferenceScreen.toString(builder)
+            }
         }
         return builder.toString()
     }
 
+    protected open fun launchFragmentScenario(fragmentClass: Class<PreferenceFragmentCompat>) =
+        FragmentScenario.launch(fragmentClass)
+
     private fun Preference.toString(builder: StringBuilder, indent: String = "") {
         val clazz = javaClass
         builder.append(indent).append(clazz).append(" {\n")
@@ -120,4 +139,16 @@
         }
         builder.append(indent).append("}\n")
     }
+
+    companion object {
+        const val TAG = "CatalystScreenTestCase"
+
+        fun dumpThreads() {
+            for ((thread, stack) in Thread.getAllStackTraces()) {
+                Log.i(TAG, "$thread")
+                for (frame in stack) Log.i(TAG, "  $frame")
+                Log.i(TAG, "")
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
new file mode 100644
index 0000000..952562e
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:baselineAligned="false">
+
+    <include layout="@layout/settingslib_icon_frame"/>
+
+    <include layout="@layout/settingslib_preference_frame"/>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingLeft="16dp"
+        android:paddingStart="16dp"
+        android:paddingRight="0dp"
+        android:paddingEnd="0dp"
+        android:orientation="vertical"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt
index 2f5daaa..ba5f5cf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.settingslib.widget
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+/**
+ * A base interface to indicate that a class should not have rounded corners.
+ *
+ * Classes implementing this interface will be treated as already handle rounded corners.
+ */
+interface GroupSectionDividerMixin
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
new file mode 100644
index 0000000..535d80f
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget
+
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.PreferenceScreen
+import androidx.recyclerview.widget.RecyclerView
+
+/** Base class for Settings to use PreferenceFragmentCompat */
+open abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
+
+    override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> {
+        if (SettingsThemeHelper.isExpressiveTheme(requireContext()))
+            return SettingsPreferenceGroupAdapter(preferenceScreen)
+        return super.onCreateAdapter(preferenceScreen)
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
new file mode 100644
index 0000000..98b7f76
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget
+
+import android.os.Handler
+import android.os.Looper
+import androidx.annotation.DrawableRes
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceGroupAdapter
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.theme.R
+
+/**
+ * A custom adapter for displaying settings preferences in a list, handling rounded corners
+ * for preference items within a group.
+ */
+open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
+    preferenceGroup: PreferenceGroup
+) : PreferenceGroupAdapter(preferenceGroup) {
+
+    private val mPreferenceGroup = preferenceGroup
+    private var mRoundCornerMappingList: ArrayList<Int> = ArrayList()
+
+    private var mNormalPaddingStart = 0
+    private var mGroupPaddingStart = 0
+    private var mNormalPaddingEnd = 0
+    private var mGroupPaddingEnd = 0
+
+    private val mHandler = Handler(Looper.getMainLooper())
+
+    private val syncRunnable = Runnable { updatePreferences() }
+
+    init {
+        val context = preferenceGroup.context
+        mNormalPaddingStart =
+            context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1)
+        mGroupPaddingStart = mNormalPaddingStart * 2
+        mNormalPaddingEnd =
+            context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1)
+        mGroupPaddingEnd = mNormalPaddingEnd * 2
+        updatePreferences()
+    }
+
+    override fun onPreferenceHierarchyChange(preference: Preference) {
+        super.onPreferenceHierarchyChange(preference)
+
+        // Post after super class has posted their sync runnable to update preferences.
+        mHandler.removeCallbacks(syncRunnable)
+        mHandler.post(syncRunnable)
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
+        super.onBindViewHolder(holder, position)
+        updateBackground(holder, position)
+    }
+
+    private fun updatePreferences() {
+        val oldList = ArrayList(mRoundCornerMappingList)
+        mRoundCornerMappingList = ArrayList()
+        mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup)
+        if (mRoundCornerMappingList != oldList) {
+            notifyDataSetChanged()
+        }
+    }
+
+    private fun mappingPreferenceGroup(cornerStyles: MutableList<Int>, group: PreferenceGroup) {
+        cornerStyles.clear()
+        cornerStyles.addAll(MutableList(itemCount) { 0 })
+
+        // the first item in to group
+        var startIndex = -1
+        // the last item in the group
+        var endIndex = -1
+        var currentParent: PreferenceGroup? = group
+        for (i in 0 until itemCount) {
+            when (val pref = getItem(i)) {
+                // the preference has round corner background, so we don't need to handle it.
+                is GroupSectionDividerMixin -> {
+                    cornerStyles[i] = 0
+                    startIndex = -1
+                    endIndex = -1
+                }
+
+                // PreferenceCategory should not have round corner background.
+                is PreferenceCategory -> {
+                    cornerStyles[i] = 0
+                    startIndex = -1
+                    endIndex = -1
+                    currentParent = pref
+                }
+
+                // ExpandablePreference is PreferenceGroup but it should handle round corner
+                is Expandable -> {
+                    // When ExpandablePreference is expanded, we treat is as the first item.
+                    if (pref.isExpanded()) {
+                        currentParent = pref as? PreferenceGroup
+                        startIndex = i
+                        cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP or ROUND_CORNER_CENTER
+                        endIndex = -1
+                    }
+                }
+
+                else -> {
+                    val parent = pref?.parent
+
+                    // item in the group should have round corner background.
+                    cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_CENTER
+                    if (parent === currentParent) {
+                        // find the first item in the group
+                        if (startIndex == -1) {
+                            startIndex = i
+                            cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP
+                        }
+
+                        // find the last item in the group, if we find the new last item, we should
+                        // remove the old last item round corner.
+                        if (endIndex == -1 || endIndex < i) {
+                            if (endIndex != -1) {
+                                cornerStyles[endIndex] =
+                                    cornerStyles[endIndex] and ROUND_CORNER_BOTTOM.inv()
+                            }
+                            endIndex = i
+                            cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM
+                        }
+                    } else {
+                        // this item is new group, we should reset the index.
+                        currentParent = parent
+                        startIndex = i
+                        cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP
+                        endIndex = i
+                        cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM
+                    }
+                }
+            }
+        }
+    }
+
+    /** handle roundCorner background  */
+    private fun updateBackground(holder: PreferenceViewHolder, position: Int) {
+        @DrawableRes val backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/)
+
+        val v = holder.itemView
+        val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
+        val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
+
+        v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+        v.setBackgroundResource(backgroundRes)
+    }
+
+    @DrawableRes
+    protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int {
+        val cornerType = mRoundCornerMappingList[position]
+
+        if ((cornerType and ROUND_CORNER_CENTER) == 0) {
+            return 0
+        }
+
+        return when {
+            (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) == 0 -> {
+                // the first
+                if (isSelected) R.drawable.settingslib_round_background_top_selected
+                else R.drawable.settingslib_round_background_top
+            }
+
+            (cornerType and ROUND_CORNER_BOTTOM) != 0 && (cornerType and ROUND_CORNER_TOP) == 0 -> {
+                // the last
+                if (isSelected) R.drawable.settingslib_round_background_bottom_selected
+                else R.drawable.settingslib_round_background_bottom
+            }
+
+            (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) != 0 -> {
+                // the only one preference
+                if (isSelected) R.drawable.settingslib_round_background_selected
+                else R.drawable.settingslib_round_background
+            }
+
+            else -> {
+                // in the center
+                if (isSelected) R.drawable.settingslib_round_background_center_selected
+                else R.drawable.settingslib_round_background_center
+            }
+        }
+    }
+
+    companion object {
+        private const val ROUND_CORNER_CENTER: Int = 1
+        private const val ROUND_CORNER_TOP: Int = 1 shl 1
+        private const val ROUND_CORNER_BOTTOM: Int = 1 shl 2
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index b69912a..73d0bec 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.7.0"
+    extra["jetpackComposeVersion"] = "1.7.3"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 2a251a5..8636524 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -23,6 +23,7 @@
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.banner.BannerPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
 import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
 import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
@@ -39,6 +40,7 @@
 import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
 import com.android.settingslib.spa.gallery.scaffold.NonScrollablePagerPageProvider
 import com.android.settingslib.spa.gallery.page.SliderPageProvider
+import com.android.settingslib.spa.gallery.preference.CheckBoxPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.IntroPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
@@ -46,6 +48,7 @@
 import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.TopIntroPreferencePageProvider
+import com.android.settingslib.spa.gallery.preference.TwoTargetButtonPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.ZeroStatePreferencePageProvider
 import com.android.settingslib.spa.gallery.scaffold.PagerMainPageProvider
@@ -105,6 +108,9 @@
                 CopyablePageProvider,
                 IntroPreferencePageProvider,
                 TopIntroPreferencePageProvider,
+                CheckBoxPreferencePageProvider,
+                TwoTargetButtonPreferencePageProvider,
+                CardPageProvider,
             ),
             rootPages = listOf(
                 HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
new file mode 100644
index 0000000..5659e2f
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.settingslib.spa.gallery.card
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Stars
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.card.SuggestionCard
+import com.android.settingslib.spa.widget.card.SuggestionCardModel
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+object CardPageProvider : SettingsPageProvider {
+    override val name = "Card"
+
+    override fun getTitle(arguments: Bundle?) = TITLE
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        RegularScaffold(title = TITLE) {
+            SuggestionCard()
+            SuggestionCardWithLongTitle()
+            SuggestionCardDismissible()
+        }
+    }
+
+    @Composable
+    private fun SuggestionCard() {
+        SuggestionCard(
+            SuggestionCardModel(
+                title = "Suggestion card",
+                description = "Suggestion card description",
+                imageVector = Icons.Filled.Stars,
+            )
+        )
+    }
+
+    @Composable
+    private fun SuggestionCardWithLongTitle() {
+        SuggestionCard(
+            SuggestionCardModel(
+                title = "Top level suggestion card with a really, really long title",
+                imageVector = Icons.Filled.Stars,
+                onClick = {},
+            )
+        )
+    }
+
+    @Composable
+    private fun SuggestionCardDismissible() {
+        var isVisible by rememberSaveable { mutableStateOf(true) }
+        SuggestionCard(
+            SuggestionCardModel(
+                title = "Suggestion card",
+                description = "Suggestion card description",
+                imageVector = Icons.Filled.Stars,
+                onDismiss = { isVisible = false },
+                isVisible = isVisible,
+            )
+        )
+    }
+
+    @Composable
+    fun Entry() {
+        Preference(
+            object : PreferenceModel {
+                override val title = TITLE
+                override val onClick = navigator(name)
+            }
+        )
+    }
+
+    private const val TITLE = "Sample Card"
+}
+
+@Preview
+@Composable
+private fun CardPagePreview() {
+    SettingsTheme { CardPageProvider.Page(null) }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
index c9c81aa..cb05504 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
@@ -19,50 +19,93 @@
 import android.os.Bundle
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.widget.dialog.AlertDialogButton
+import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogWithIcon
 import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.Category
 
 private const val TITLE = "Category: Dialog"
 
 object DialogMainPageProvider : SettingsPageProvider {
     override val name = "DialogMain"
-    private val owner = createSettingsPage()
 
-    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
-        SettingsEntryBuilder.create("AlertDialog", owner).setUiLayoutFn {
-            val alertDialogPresenter = rememberAlertDialogPresenter(
-                confirmButton = AlertDialogButton("Ok"),
-                dismissButton = AlertDialogButton("Cancel"),
-                title = "Title",
-                text = { Text("Text") },
-            )
-            Preference(object : PreferenceModel {
-                override val title = "Show AlertDialog"
-                override val onClick = alertDialogPresenter::open
-            })
-        }.build(),
-        SettingsEntryBuilder.create("NavDialog", owner).setUiLayoutFn {
-            Preference(object : PreferenceModel {
-                override val title = "Navigate to Dialog"
-                override val onClick = navigator(route = NavDialogProvider.name)
-            })
-        }.build(),
-    )
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        RegularScaffold(TITLE) {
+            Category {
+                AlertDialog()
+                AlertDialogWithIcon()
+                NavDialog()
+            }
+        }
+    }
 
     @Composable
     fun Entry() {
-        Preference(object : PreferenceModel {
-            override val title = TITLE
-            override val onClick = navigator(name)
-        })
+        Preference(
+            object : PreferenceModel {
+                override val title = TITLE
+                override val onClick = navigator(name)
+            }
+        )
     }
 
     override fun getTitle(arguments: Bundle?) = TITLE
 }
+
+@Composable
+private fun AlertDialog() {
+    val alertDialogPresenter =
+        rememberAlertDialogPresenter(
+            confirmButton = AlertDialogButton("Ok"),
+            dismissButton = AlertDialogButton("Cancel"),
+            title = "Title",
+            text = { Text("Text") },
+        )
+    Preference(
+        object : PreferenceModel {
+            override val title = "Show AlertDialog"
+            override val onClick = alertDialogPresenter::open
+        }
+    )
+}
+
+@Composable
+private fun AlertDialogWithIcon() {
+    var openDialog by rememberSaveable { mutableStateOf(false) }
+    val close = { openDialog = false }
+    val open = { openDialog = true }
+    if (openDialog) {
+        SettingsAlertDialogWithIcon(
+            title = "Title",
+            onDismissRequest = close,
+            confirmButton = AlertDialogButton("OK", onClick = close),
+            dismissButton = AlertDialogButton("Dismiss", onClick = close),
+        ) {}
+    }
+    Preference(
+        object : PreferenceModel {
+            override val title = "Show AlertDialogWithIcon"
+            override val onClick = open
+        }
+    )
+}
+
+@Composable
+private fun NavDialog() {
+    Preference(
+        object : PreferenceModel {
+            override val title = "Navigate to Dialog"
+            override val onClick = navigator(route = NavDialogProvider.name)
+        }
+    )
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 4d77ea1..ebfc0c5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -27,6 +27,7 @@
 import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
 import com.android.settingslib.spa.gallery.banner.BannerPageProvider
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
 import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
 import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
@@ -80,6 +81,7 @@
                 DialogMainPageProvider.Entry()
                 EditorMainPageProvider.Entry()
                 BannerPageProvider.Entry()
+                CardPageProvider.Entry()
                 CopyablePageProvider.Entry()
             }
         }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/CheckBoxPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/CheckBoxPreferencePageProvider.kt
new file mode 100644
index 0000000..c2b67cb
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/CheckBoxPreferencePageProvider.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AirplanemodeActive
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.CheckboxPreference
+import com.android.settingslib.spa.widget.preference.CheckboxPreferenceModel
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.Category
+import com.android.settingslib.spa.widget.ui.SettingsIcon
+
+private const val TITLE = "Sample CheckBoxPreference"
+
+object CheckBoxPreferencePageProvider : SettingsPageProvider {
+    override val name = "CheckBoxPreference"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        RegularScaffold(TITLE) {
+            Category {
+                var checked1 by rememberSaveable { mutableStateOf(true) }
+                CheckboxPreference(
+                    object : CheckboxPreferenceModel {
+                        override val title = "Use Dark theme"
+                        override val checked = { checked1 }
+                        override val onCheckedChange = { newChecked: Boolean ->
+                            checked1 = newChecked
+                        }
+                    }
+                )
+                var checked2 by rememberSaveable { mutableStateOf(false) }
+                CheckboxPreference(
+                    object : CheckboxPreferenceModel {
+                        override val title = "Use Dark theme"
+                        override val summary = { "Summary" }
+                        override val checked = { checked2 }
+                        override val onCheckedChange = { newChecked: Boolean ->
+                            checked2 = newChecked
+                        }
+                    }
+                )
+                var checked3 by rememberSaveable { mutableStateOf(true) }
+                CheckboxPreference(
+                    object : CheckboxPreferenceModel {
+                        override val title = "Use Dark theme"
+                        override val summary = { "Summary" }
+                        override val checked = { checked3 }
+                        override val onCheckedChange = { newChecked: Boolean ->
+                            checked3 = newChecked
+                        }
+                        override val icon =
+                            @Composable {
+                                SettingsIcon(imageVector = Icons.Outlined.AirplanemodeActive)
+                            }
+                    }
+                )
+            }
+        }
+    }
+
+    @Composable
+    fun Entry() {
+        Preference(
+            object : PreferenceModel {
+                override val title = TITLE
+                override val onClick = navigator(name)
+            }
+        )
+    }
+
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
index 831b439..3cfb536 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
@@ -36,11 +36,13 @@
             Category {
                 PreferencePageProvider.Entry()
                 ListPreferencePageProvider.Entry()
+                CheckBoxPreferencePageProvider.Entry()
             }
             Category {
                 SwitchPreferencePageProvider.Entry()
                 MainSwitchPreferencePageProvider.Entry()
                 TwoTargetSwitchPreferencePageProvider.Entry()
+                TwoTargetButtonPreferencePageProvider.Entry()
             }
             Category {
                 ZeroStatePreferencePageProvider.Entry()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetButtonPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetButtonPreferencePageProvider.kt
new file mode 100644
index 0000000..c6e834a
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetButtonPreferencePageProvider.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Add
+import androidx.compose.material.icons.outlined.Info
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.TwoTargetButtonPreference
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.Category
+
+private const val TITLE = "Sample TwoTargetButtonPreference"
+
+object TwoTargetButtonPreferencePageProvider : SettingsPageProvider {
+    override val name = "TwoTargetButtonPreference"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        RegularScaffold(TITLE) {
+            Category {
+                SampleTwoTargetButtonPreference()
+                SampleTwoTargetButtonPreferenceWithSummary()
+            }
+        }
+    }
+
+    @Composable
+    fun Entry() {
+        Preference(
+            object : PreferenceModel {
+                override val title = TITLE
+                override val onClick = navigator(name)
+            }
+        )
+    }
+}
+
+@Composable
+private fun SampleTwoTargetButtonPreference() {
+    TwoTargetButtonPreference(
+        title = "TwoTargetButton",
+        summary = { "" },
+        buttonIcon = Icons.Outlined.Info,
+        buttonIconDescription = "info",
+        onClick = {},
+        onButtonClick = {},
+    )
+}
+
+@Composable
+private fun SampleTwoTargetButtonPreferenceWithSummary() {
+    TwoTargetButtonPreference(
+        title = "TwoTargetButton",
+        summary = { "summary" },
+        buttonIcon = Icons.Outlined.Add,
+        buttonIconDescription = "info",
+        onClick = {},
+        onButtonClick = {},
+    )
+}
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 8f8275b..914f06c 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,7 +54,7 @@
 dependencies {
     api(project(":SettingsLibColor"))
     api("androidx.appcompat:appcompat:1.7.0")
-    api("androidx.compose.material3:material3:1.3.0")
+    api("androidx.compose.material3:material3:1.4.0-alpha01")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index ab95162..08bedf9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -22,11 +22,13 @@
 object SettingsDimension {
     val paddingTiny = 2.dp
     val paddingExtraSmall = 4.dp
+    val paddingExtraSmall1 = 6.dp
     val paddingSmall = if (isSpaExpressiveEnabled) 8.dp else 4.dp
     val paddingExtraSmall5 = 10.dp
     val paddingExtraSmall6 = 12.dp
     val paddingLarge = 16.dp
     val paddingExtraLarge = 24.dp
+    val paddingExtraLarge1 = 28.dp
 
     val spinnerHorizontalPadding = paddingExtraLarge
     val spinnerVerticalPadding = paddingLarge
@@ -36,6 +38,7 @@
     val actionIconPadding = 4.dp
 
     val itemIconSize = 24.dp
+    val itemIconContainerSizeSmall = 40.dp
     val itemIconContainerSize = 72.dp
     val itemPaddingStart = if (isSpaExpressiveEnabled) paddingLarge else paddingExtraLarge
     val itemPaddingEnd = paddingLarge
@@ -46,6 +49,12 @@
         end = itemPaddingEnd,
         bottom = itemPaddingVertical,
     )
+    val footerItemPadding = PaddingValues(
+        start = paddingExtraLarge1,
+        top = itemPaddingVertical,
+        end = itemPaddingEnd,
+        bottom = itemPaddingVertical,
+    )
     val textFieldPadding = PaddingValues(
         start = itemPaddingStart,
         end = itemPaddingEnd,
@@ -85,4 +94,9 @@
     val illustrationMaxHeight = 300.dp
     val illustrationPadding = paddingLarge
     val illustrationCornerRadius = 28.dp
+
+    val preferenceMinHeight = 72.dp
+
+    val spinnerOptionMinHeight = 48.dp
+    val spinnerIconSize = 20.dp
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
index c787715..61607bc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
@@ -24,7 +24,11 @@
 
     val CornerMedium = RoundedCornerShape(12.dp)
 
-    val categoryCorner = RoundedCornerShape(20.dp)
+    val CornerMedium2 = RoundedCornerShape(20.dp)
+
+    val CornerLarge = RoundedCornerShape(24.dp)
 
     val CornerExtraLarge = RoundedCornerShape(28.dp)
+
+    val CornerExtraLarge1 = RoundedCornerShape(40.dp)
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt
new file mode 100644
index 0000000..2126634
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.card
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+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.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Stars
+import androidx.compose.material.icons.outlined.Stars
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.toSemiBoldWeight
+
+data class SuggestionCardModel(
+    val title: String,
+    val description: String? = null,
+    val imageVector: ImageVector,
+
+    /**
+     * A dismiss button will be displayed if this is not null.
+     *
+     * And this callback will be called when user clicks the button.
+     */
+    val onDismiss: (() -> Unit)? = null,
+    val isVisible: Boolean = true,
+    val onClick: (() -> Unit)? = null,
+)
+
+@Composable
+fun SuggestionCard(model: SuggestionCardModel) {
+    AnimatedVisibility(visible = model.isVisible) {
+        Row(
+            verticalAlignment = Alignment.CenterVertically,
+            modifier =
+                Modifier.padding(
+                        horizontal = SettingsDimension.paddingLarge,
+                        vertical = SettingsDimension.paddingSmall,
+                    )
+                    .fillMaxWidth()
+                    .heightIn(min = SettingsDimension.preferenceMinHeight)
+                    .clip(SettingsShape.CornerExtraLarge1)
+                    .background(MaterialTheme.colorScheme.secondaryContainer)
+                    .then(model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier)
+                    .padding(SettingsDimension.paddingExtraSmall6),
+        ) {
+            SuggestionCardIcon(model.imageVector)
+            Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+            Column(modifier = Modifier.weight(1f).semantics(mergeDescendants = true) {}) {
+                SuggestionCardTitle(model.title)
+                if (model.description != null) SuggestionCardDescription(model.description)
+            }
+            if (model.onDismiss != null) {
+                Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+                SuggestionCardDismissButton(model.onDismiss)
+            }
+        }
+    }
+}
+
+@Composable
+private fun SuggestionCardIcon(imageVector: ImageVector) {
+    Box(
+        modifier =
+            Modifier.padding(SettingsDimension.paddingSmall)
+                .size(SettingsDimension.itemIconContainerSizeSmall)
+                .clip(CircleShape)
+                .background(MaterialTheme.colorScheme.secondary),
+        contentAlignment = Alignment.Center,
+    ) {
+        Icon(
+            imageVector = imageVector,
+            contentDescription = null,
+            modifier = Modifier.size(SettingsDimension.itemIconSize),
+            tint = MaterialTheme.colorScheme.onSecondary,
+        )
+    }
+}
+
+@Composable
+private fun SuggestionCardTitle(title: String) {
+    Text(
+        text = title,
+        style = MaterialTheme.typography.titleMedium.toSemiBoldWeight(),
+        modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny),
+        color = MaterialTheme.colorScheme.onSecondaryContainer,
+    )
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun SuggestionCardDescription(description: String) {
+    Text(
+        text = description,
+        style = MaterialTheme.typography.bodySmallEmphasized,
+        modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny),
+        color = MaterialTheme.colorScheme.onSecondaryContainer,
+    )
+}
+
+@Composable
+private fun SuggestionCardDismissButton(onDismiss: () -> Unit) {
+    IconButton(shape = CircleShape, onClick = onDismiss) {
+        Icon(
+            imageVector = Icons.Filled.Close,
+            contentDescription =
+                stringResource(androidx.compose.material3.R.string.m3c_snackbar_dismiss),
+            modifier = Modifier.size(SettingsDimension.itemIconSize),
+            tint = MaterialTheme.colorScheme.onSecondaryContainer,
+        )
+    }
+}
+
+@Preview
+@Composable
+private fun SuggestionCardPreview() {
+    SettingsTheme {
+        SuggestionCard(
+            SuggestionCardModel(
+                title = "Suggestion card",
+                description = "Suggestion card description",
+                imageVector = Icons.Outlined.Stars,
+                onDismiss = {},
+                onClick = {},
+            )
+        )
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogWithIcon.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogWithIcon.kt
index 58a83fa..4cf270d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogWithIcon.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogWithIcon.kt
@@ -17,7 +17,6 @@
 package com.android.settingslib.spa.widget.dialog
 
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
@@ -26,12 +25,13 @@
 import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.Button
 import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.window.DialogProperties
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 
 @Composable
 fun SettingsAlertDialogWithIcon(
@@ -57,7 +57,9 @@
             title?.let {
                 {
                     CenterRow {
-                        Text(it, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
+                        if (isSpaExpressiveEnabled)
+                            Text(it, style = MaterialTheme.typography.bodyLarge)
+                        else Text(it)
                     }
                 }
             },
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
index c68ec78..acb96be 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
@@ -35,6 +36,7 @@
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.min
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
 import com.android.settingslib.spa.framework.theme.SettingsShape
@@ -62,7 +64,8 @@
                 .semantics(mergeDescendants = true) {}
                 .then(
                     if (isSpaExpressiveEnabled)
-                        Modifier.clip(SettingsShape.CornerExtraSmall)
+                        Modifier.heightIn(min = SettingsDimension.preferenceMinHeight)
+                            .clip(SettingsShape.CornerExtraSmall)
                             .background(MaterialTheme.colorScheme.surfaceBright)
                     else Modifier
                 )
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
index b28e88e..e6a2366 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -17,6 +17,7 @@
 package com.android.settingslib.spa.widget.preference
 
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material3.MaterialTheme
@@ -25,6 +26,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.min
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -35,13 +37,19 @@
 fun MainSwitchPreference(model: SwitchPreferenceModel) {
     EntryHighlight {
         Surface(
-            modifier = Modifier.padding(SettingsDimension.itemPaddingEnd),
-            color = when (model.checked()) {
-                true -> MaterialTheme.colorScheme.primaryContainer
-                else -> MaterialTheme.colorScheme.secondaryContainer
-            },
-            shape = if (isSpaExpressiveEnabled) CircleShape
-            else SettingsShape.CornerExtraLarge,
+            modifier =
+                Modifier.padding(SettingsDimension.itemPaddingEnd)
+                    .then(
+                        if (isSpaExpressiveEnabled)
+                            Modifier.heightIn(min = SettingsDimension.preferenceMinHeight)
+                        else Modifier
+                    ),
+            color =
+                when (model.checked()) {
+                    true -> MaterialTheme.colorScheme.primaryContainer
+                    else -> MaterialTheme.colorScheme.secondaryContainer
+                },
+            shape = if (isSpaExpressiveEnabled) CircleShape else SettingsShape.CornerExtraLarge,
         ) {
             InternalSwitchPreference(
                 title = model.title,
@@ -61,16 +69,20 @@
 private fun MainSwitchPreferencePreview() {
     SettingsTheme {
         Column {
-            MainSwitchPreference(object : SwitchPreferenceModel {
-                override val title = "Use Dark theme"
-                override val checked = { true }
-                override val onCheckedChange: (Boolean) -> Unit = {}
-            })
-            MainSwitchPreference(object : SwitchPreferenceModel {
-                override val title = "Use Dark theme"
-                override val checked = { false }
-                override val onCheckedChange: (Boolean) -> Unit = {}
-            })
+            MainSwitchPreference(
+                object : SwitchPreferenceModel {
+                    override val title = "Use Dark theme"
+                    override val checked = { true }
+                    override val onCheckedChange: (Boolean) -> Unit = {}
+                }
+            )
+            MainSwitchPreference(
+                object : SwitchPreferenceModel {
+                    override val title = "Use Dark theme"
+                    override val checked = { false }
+                    override val onCheckedChange: (Boolean) -> Unit = {}
+                }
+            )
         }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ZeroStatePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ZeroStatePreference.kt
index b771f36..5419223 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ZeroStatePreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ZeroStatePreference.kt
@@ -54,7 +54,7 @@
     val zeroStateShape = remember {
         RoundedPolygon.star(
             numVerticesPerRadius = 6,
-            innerRadius = 0.75f,
+            innerRadius = 0.8f,
             rounding = CornerRounding(0.3f)
         )
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 2c55779..693fb35 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -46,6 +46,7 @@
 import androidx.compose.material3.TopAppBarState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.NonRestartableComposable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.derivedStateOf
@@ -326,6 +327,9 @@
     // Sets the app bar's height offset limit to hide just the bottom title area and keep top title
     // visible when collapsed.
     scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx.floatValue
+    if (isSpaExpressiveEnabled) {
+        LaunchedEffect(scrollBehavior?.state?.heightOffsetLimit) { scrollBehavior?.collapse() }
+    }
 
     // Obtain the container Color from the TopAppBarColors using the `collapsedFraction`, as the
     // bottom part of this TwoRowsTopAppBar changes color at the same rate the app bar expands or
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index 7d8ee79..60b1e639 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -39,7 +39,6 @@
 import com.android.settingslib.spa.framework.compose.horizontalValues
 import com.android.settingslib.spa.framework.compose.verticalValues
 import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 import com.android.settingslib.spa.framework.theme.settingsBackground
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -56,9 +55,6 @@
 ) {
     ActivityTitle(title)
     val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
-    if (isSpaExpressiveEnabled) {
-        LaunchedEffect(scrollBehavior.state.heightOffsetLimit) { scrollBehavior.collapse() }
-    }
 
     Scaffold(
         modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
index 6c5581f..66680fa 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
@@ -49,7 +49,9 @@
         text = title,
         modifier =
             Modifier.padding(
-                start = SettingsDimension.itemPaddingStart,
+                start =
+                    if (isSpaExpressiveEnabled) SettingsDimension.paddingSmall
+                    else SettingsDimension.itemPaddingStart,
                 top = 20.dp,
                 end =
                     if (isSpaExpressiveEnabled) SettingsDimension.paddingSmall
@@ -67,16 +69,16 @@
  */
 @Composable
 fun Category(title: String? = null, content: @Composable ColumnScope.() -> Unit) {
+    var displayTitle by remember { mutableStateOf(false) }
     Column(
         modifier =
-            if (isSpaExpressiveEnabled)
+            if (isSpaExpressiveEnabled && displayTitle)
                 Modifier.padding(
                     horizontal = SettingsDimension.paddingLarge,
                     vertical = SettingsDimension.paddingSmall,
                 )
             else Modifier
     ) {
-        var displayTitle by remember { mutableStateOf(false) }
         if (title != null && displayTitle) CategoryTitle(title = title)
         Column(
             modifier =
@@ -85,7 +87,7 @@
                     }
                     .then(
                         if (isSpaExpressiveEnabled)
-                            Modifier.fillMaxWidth().clip(SettingsShape.categoryCorner)
+                            Modifier.fillMaxWidth().clip(SettingsShape.CornerMedium2)
                         else Modifier
                     ),
             verticalArrangement =
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
index 6b2db90..6e4fd78 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -19,9 +19,14 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Check
 import androidx.compose.material.icons.outlined.ExpandLess
 import androidx.compose.material.icons.outlined.ExpandMore
 import androidx.compose.material3.Button
@@ -38,20 +43,19 @@
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 
-data class SpinnerOption(
-    val id: Int,
-    val text: String,
-)
+data class SpinnerOption(val id: Int, val text: String)
 
 @Composable
 fun Spinner(options: List<SpinnerOption>, selectedId: Int?, setId: (id: Int) -> Unit) {
@@ -62,51 +66,101 @@
     var expanded by rememberSaveable { mutableStateOf(false) }
 
     Box(
-        modifier = Modifier
-            .padding(
-                start = SettingsDimension.itemPaddingStart,
-                top = SettingsDimension.itemPaddingAround,
-                end = SettingsDimension.itemPaddingEnd,
-                bottom = SettingsDimension.itemPaddingAround,
-            )
-            .selectableGroup(),
-    ) {
-        val contentPadding = if (isSpaExpressiveEnabled) PaddingValues(
-            horizontal = SettingsDimension.spinnerHorizontalPadding,
-            vertical = SettingsDimension.spinnerVerticalPadding
-        ) else PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
-        Button(
-            modifier = Modifier.semantics { role = Role.DropdownList },
-            onClick = { expanded = true },
-            colors = ButtonDefaults.buttonColors(
-                containerColor = MaterialTheme.colorScheme.primaryContainer,
-                contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
-            ),
-            contentPadding = contentPadding,
-        ) {
-            SpinnerText(options.find { it.id == selectedId })
-            ExpandIcon(expanded)
-        }
-        DropdownMenu(
-            expanded = expanded,
-            onDismissRequest = { expanded = false },
-            modifier = Modifier.background(MaterialTheme.colorScheme.secondaryContainer),
-        ) {
-            for (option in options) {
-                DropdownMenuItem(
-                    text = {
-                        SpinnerText(
-                            option = option,
-                            modifier = Modifier.padding(end = 24.dp),
-                            color = MaterialTheme.colorScheme.onSecondaryContainer,
-                        )
-                    },
-                    onClick = {
-                        expanded = false
-                        setId(option.id)
-                    },
-                    contentPadding = contentPadding,
+        modifier =
+            Modifier.padding(
+                    start = SettingsDimension.itemPaddingStart,
+                    top = SettingsDimension.itemPaddingAround,
+                    end = SettingsDimension.itemPaddingEnd,
+                    bottom = SettingsDimension.itemPaddingAround,
                 )
+                .selectableGroup()
+    ) {
+        if (isSpaExpressiveEnabled) {
+            Button(
+                modifier = Modifier.semantics { role = Role.DropdownList },
+                onClick = { expanded = true },
+                colors =
+                    ButtonDefaults.buttonColors(
+                        containerColor = MaterialTheme.colorScheme.secondaryContainer,
+                        contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
+                    ),
+                contentPadding =
+                    PaddingValues(
+                        horizontal = SettingsDimension.spinnerHorizontalPadding,
+                        vertical = SettingsDimension.spinnerVerticalPadding,
+                    ),
+            ) {
+                SpinnerText(options.find { it.id == selectedId })
+                ExpandIcon(expanded)
+            }
+            DropdownMenu(
+                expanded = expanded,
+                onDismissRequest = { expanded = false },
+                shape = SettingsShape.CornerLarge,
+                modifier =
+                    Modifier.background(MaterialTheme.colorScheme.surfaceContainerLow)
+                        .padding(horizontal = SettingsDimension.paddingSmall),
+            ) {
+                for (option in options) {
+                    val selected = option.id == selectedId
+                    DropdownMenuItem(
+                        text = { SpinnerOptionText(option = option, selected) },
+                        onClick = {
+                            expanded = false
+                            setId(option.id)
+                        },
+                        contentPadding =
+                            PaddingValues(
+                                horizontal = SettingsDimension.paddingSmall,
+                                vertical = SettingsDimension.paddingExtraSmall1,
+                            ),
+                        modifier =
+                            Modifier.heightIn(min = SettingsDimension.spinnerOptionMinHeight)
+                                .then(
+                                    if (selected)
+                                        Modifier.clip(SettingsShape.CornerMedium2)
+                                            .background(MaterialTheme.colorScheme.primaryContainer)
+                                    else Modifier
+                                ),
+                    )
+                }
+            }
+        } else {
+            val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
+            Button(
+                modifier = Modifier.semantics { role = Role.DropdownList },
+                onClick = { expanded = true },
+                colors =
+                    ButtonDefaults.buttonColors(
+                        containerColor = MaterialTheme.colorScheme.primaryContainer,
+                        contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+                    ),
+                contentPadding = contentPadding,
+            ) {
+                SpinnerText(options.find { it.id == selectedId })
+                ExpandIcon(expanded)
+            }
+            DropdownMenu(
+                expanded = expanded,
+                onDismissRequest = { expanded = false },
+                modifier = Modifier.background(MaterialTheme.colorScheme.secondaryContainer),
+            ) {
+                for (option in options) {
+                    DropdownMenuItem(
+                        text = {
+                            SpinnerText(
+                                option = option,
+                                modifier = Modifier.padding(end = 24.dp),
+                                color = MaterialTheme.colorScheme.onSecondaryContainer,
+                            )
+                        },
+                        onClick = {
+                            expanded = false
+                            setId(option.id)
+                        },
+                        contentPadding = contentPadding,
+                    )
+                }
             }
         }
     }
@@ -115,10 +169,11 @@
 @Composable
 internal fun ExpandIcon(expanded: Boolean) {
     Icon(
-        imageVector = when {
-            expanded -> Icons.Outlined.ExpandLess
-            else -> Icons.Outlined.ExpandMore
-        },
+        imageVector =
+            when {
+                expanded -> Icons.Outlined.ExpandLess
+                else -> Icons.Outlined.ExpandMore
+            },
         contentDescription = null,
     )
 }
@@ -131,18 +186,42 @@
 ) {
     Text(
         text = option?.text ?: "",
-        modifier = modifier
-            .padding(end = SettingsDimension.itemPaddingEnd)
-            .then(
-                if (!isSpaExpressiveEnabled)
-                    Modifier.padding(vertical = SettingsDimension.itemPaddingAround)
-                else Modifier
-            ),
+        modifier =
+            modifier
+                .padding(end = SettingsDimension.itemPaddingEnd)
+                .then(
+                    if (!isSpaExpressiveEnabled)
+                        Modifier.padding(vertical = SettingsDimension.itemPaddingAround)
+                    else Modifier
+                ),
         color = color,
         style = MaterialTheme.typography.labelLarge,
     )
 }
 
+@Composable
+private fun SpinnerOptionText(option: SpinnerOption?, selected: Boolean) {
+    Row {
+        if (selected) {
+            Icon(
+                imageVector = Icons.Outlined.Check,
+                modifier = Modifier.size(SettingsDimension.spinnerIconSize),
+                tint = MaterialTheme.colorScheme.onPrimaryContainer,
+                contentDescription = null,
+            )
+            Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+        }
+        Text(
+            text = option?.text ?: "",
+            modifier = Modifier.padding(end = SettingsDimension.itemPaddingEnd),
+            color =
+                if (selected) MaterialTheme.colorScheme.onPrimaryContainer
+                else MaterialTheme.colorScheme.onSurface,
+            style = MaterialTheme.typography.labelLarge,
+        )
+    }
+}
+
 @Preview(showBackground = true)
 @Composable
 private fun SpinnerPreview() {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsBannerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsBannerTest.kt
similarity index 100%
rename from packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsBannerTest.kt
rename to packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsBannerTest.kt
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleBannerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsCollapsibleBannerTest.kt
similarity index 100%
rename from packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleBannerTest.kt
rename to packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsCollapsibleBannerTest.kt
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt
new file mode 100644
index 0000000..96bfb3d
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.card
+
+import android.content.Context
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Star
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.isNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SuggestionCardTest {
+    @get:Rule val composeTestRule = createComposeRule()
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Test
+    fun suggestionCard_contentDisplayed() {
+        setContent()
+
+        composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+        composeTestRule.onNodeWithText(DESCRIPTION).assertIsDisplayed()
+    }
+
+    @Test
+    fun suggestionCard_dismiss() {
+        setContent()
+        composeTestRule
+            .onNodeWithContentDescription(
+                context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss)
+            )
+            .performClick()
+
+        composeTestRule.onNodeWithText(TITLE).isNotDisplayed()
+        composeTestRule.onNodeWithText(DESCRIPTION).isNotDisplayed()
+    }
+
+    private fun setContent() {
+        composeTestRule.setContent {
+            var isVisible by rememberSaveable { mutableStateOf(true) }
+            SuggestionCard(
+                SuggestionCardModel(
+                    title = TITLE,
+                    description = DESCRIPTION,
+                    imageVector = Icons.Outlined.Star,
+                    isVisible = isVisible,
+                    onDismiss = { isVisible = false },
+                )
+            )
+        }
+    }
+
+    private companion object {
+        const val TITLE = "Title"
+        const val DESCRIPTION = "Description"
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index f306918..d89d397 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -39,6 +39,8 @@
 import com.android.settingslib.development.DevelopmentSettingsEnabler
 import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
 import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
+import com.android.settingslib.spa.widget.preference.IntroAppPreference
 import com.android.settingslib.spa.widget.ui.CopyableBody
 import com.android.settingslib.spa.widget.ui.SettingsBody
 import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -48,23 +50,53 @@
 class AppInfoProvider(private val packageInfo: PackageInfo) {
     @Composable
     fun AppInfo(displayVersion: Boolean = false, isClonedAppPage: Boolean = false) {
-        Column(
-            modifier = Modifier
-                .fillMaxWidth()
-                .padding(
-                    horizontal = SettingsDimension.itemPaddingStart,
-                    vertical = SettingsDimension.itemPaddingVertical,
-                )
-                .semantics(mergeDescendants = true) {},
-            horizontalAlignment = Alignment.CenterHorizontally,
-        ) {
+        if (isSpaExpressiveEnabled) {
+            val appRepository = rememberAppRepository()
             val app = checkNotNull(packageInfo.applicationInfo)
-            Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
-                AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
+            val title = appRepository.produceLabel(app, isClonedAppPage).value
+
+            val descriptions = mutableListOf<String>()
+            if (app.isInstantApp) {
+                descriptions.add(
+                    stringResource(
+                        com.android.settingslib.widget.preference.app.R.string.install_type_instant
+                    )
+                )
             }
-            AppLabel(app, isClonedAppPage)
-            InstallType(app)
-            if (displayVersion) AppVersion()
+            if (displayVersion) {
+                val versionName = packageInfo.versionNameBidiWrapped
+                if (versionName != null) descriptions.add(versionName)
+            }
+
+            IntroAppPreference(
+                title = title,
+                descriptions = descriptions,
+                appIcon = {
+                    Image(
+                        painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
+                        contentDescription = appRepository.produceIconContentDescription(app).value,
+                    )
+                },
+            )
+        } else {
+            Column(
+                modifier =
+                    Modifier.fillMaxWidth()
+                        .padding(
+                            horizontal = SettingsDimension.itemPaddingStart,
+                            vertical = SettingsDimension.itemPaddingVertical,
+                        )
+                        .semantics(mergeDescendants = true) {},
+                horizontalAlignment = Alignment.CenterHorizontally,
+            ) {
+                val app = checkNotNull(packageInfo.applicationInfo)
+                Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
+                    AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
+                }
+                AppLabel(app, isClonedAppPage)
+                InstallType(app)
+                if (displayVersion) AppVersion()
+            }
         }
     }
 
@@ -89,19 +121,24 @@
     @Composable
     fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
         val context = LocalContext.current
-        val footer = remember(packageInfo, showPackageName) {
-            val list = mutableListOf<String>()
-            packageInfo.versionNameBidiWrapped?.let {
-                list += context.getString(R.string.version_text, it)
+        val footer =
+            remember(packageInfo, showPackageName) {
+                val list = mutableListOf<String>()
+                packageInfo.versionNameBidiWrapped?.let {
+                    list += context.getString(R.string.version_text, it)
+                }
+                if (showPackageName) {
+                    list += packageInfo.packageName
+                }
+                list.joinToString(separator = System.lineSeparator())
             }
-            if (showPackageName) {
-                list += packageInfo.packageName
-            }
-            list.joinToString(separator = System.lineSeparator())
-        }
         if (footer.isBlank()) return
         HorizontalDivider()
-        Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+        Column(
+            modifier =
+                if (isSpaExpressiveEnabled) Modifier.padding(SettingsDimension.footerItemPadding)
+                else Modifier.padding(SettingsDimension.itemPadding)
+        ) {
             CopyableBody(footer)
         }
     }
@@ -109,9 +146,7 @@
     @Composable
     private fun rememberIsDevelopmentSettingsEnabled(): Boolean {
         val context = LocalContext.current
-        return remember {
-            DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
-        }
+        return remember { DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context) }
     }
 
     private companion object {
diff --git a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
index 9b1ccef..62573fe 100644
--- a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
+++ b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
@@ -32,7 +32,7 @@
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0,
     defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
 
     private val iconTint: Int = context.getColor(
         com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSecondaryContainer
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 79c3ff9..5a8763f 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -149,3 +149,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "audio_sharing_developer_option"
+    namespace: "cross_device_experiences"
+    description: "Gates whether to enable audio sharing developer option"
+    bug: "368401233"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 0a8c6bf..2f158c8 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Jou toestel moet herselflaai om hierdie verandering toe te pas. Herselflaai nou of kanselleer."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Bedrade oorfoon"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Oorfoon"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-oudio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoonsok"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-oudio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Af"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Diensverskaffernetwerk verander tans"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 56420e2..1040f37 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"የእርስዎን መሣሪያ ይህ ለው ለማመልከት እንደገና መነሣት አለበት። አሁን እንደገና ያስነሡ ወይም ይተዉት።"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ባለገመድ የራስ ላይ ማዳመጫ"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"የጆሮ ማዳመጫ"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ኦዲዮ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"የማይክሮፎን መሰኪያ"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ማይክሮፎን"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ኦዲዮ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ማይክሮፎን"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ብሉቱዝ ማይክሮፎን"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"አብራ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"አጥፋ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"የአገልግሎት አቅራቢ አውታረ መረብን በመቀየር ላይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 88fadbc..9e1db5f 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"يجب إعادة تشغيل جهازك ليتم تطبيق هذا التغيير. يمكنك إعادة التشغيل الآن أو إلغاء التغيير."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"سماعات رأس سلكية"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"سماعات رأس"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏مكبر صوت USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مقبس الميكروفون"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏ميكروفون USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏مكبر صوت USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏ميكروفون USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ميكروفون يعمل بالبلوتوث"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"مفعّلة"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"إيقاف"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"جارٍ تغيير شبكة مشغِّل شبكة الجوّال."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index fca2953..76a3936 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই সলনিটো কার্যকৰী হ’বলৈ আপোনাৰ ডিভাইচটো ৰিবুট কৰিবই লাগিব। এতিয়াই ৰিবুট কৰক অথবা বাতিল কৰক।"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"তাঁৰযুক্ত হেডফ’ন"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"হেডফ’ন"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ইউএছবি অডিঅ\'"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকৰ জেক"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ইউএছবি মাইক্ৰ’ফ’ন"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ইউএছবি অডিঅ\'"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ইউএছবি মাইক্ৰ’ফ’ন"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ব্লুটুথ মাইক্ৰ’ফ’ন"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"অন"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"অফ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"বাহক নেটৱৰ্কৰ পৰিৱৰ্তন"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 5ba6186..2d9e1d9 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Naqilli qulaqlıq"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Qulaqlıq"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon yuvası"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofonu"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktiv"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Deaktiv"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator şəbəkəsinin dəyişilməsi"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 28eaeba..01f0ff2 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate da restartujete uređaj da bi se ova promena primenila. Restartujte ga odmah ili otkažite."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utikač za mikrofon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključeno"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promena mreže mobilnog operatera"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index e1da65e..f710b1d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Перазагрузіце прыладу, каб прымяніць гэта змяненне. Перазагрузіце ці скасуйце."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Правадныя навушнікі"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Навушнікі"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аўдыяпрылада USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Раздым для мікрафона"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Мікрафон USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аўдыяпрылада USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Мікрафон USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Мікрафон з Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Уключана"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Выключана"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Змяненне аператара сеткі"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index b0c2354..c5c4c91 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да бъде приложена тази промяна, устройството ви трябва да бъде рестартирано. Рестартирайте сега или анулирайте."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Слушалки с кабел"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалки"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аудиоустройство с USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Жак за микрофон"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Микрофон с USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аудиоустройство с USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Микрофон с USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон с Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Включване"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Изключване"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Промяна на мрежата на оператора"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index e861b57..be6b9be 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই পরিবর্তনটি প্রয়োগ করার জন্য আপনার ডিভাইসটি অবশ্যই রিবুট করতে হবে। এখনই রিবুট করুন বা বাতিল করুন।"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"তারযুক্ত হেডফোন"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"হেডফোন"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB অডিও"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকের জ্যাক"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB মাইক্রোফোন"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB অডিও"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB মাইক্রোফোন"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT মাইক্রোফোন"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"চালু আছে"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"বন্ধ আছে"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"পরিষেবা প্রদানকারীর নেটওয়ার্ক পরিবর্তন করা হচ্ছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 8844503d..f343430 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate ponovo pokrenuti uređaj da se ova promjena primijeni. Ponovo pokrenite odmah ili otkažite."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Priključak za mikrofon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključi"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključi"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promjena mreže mobilnog operatera"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index b905ae2..3557b10 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Has de reiniciar el teu dispositiu perquè s\'apliquin els canvis. Reinicia\'l ara o cancel·la."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculars amb cable"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculars"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Àudio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Connector per al micròfon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micròfon USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Àudio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micròfon USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micròfon Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activa"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivat"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"S\'està canviant la xarxa de l\'operador de telefonia mòbil"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index a69374f..ae47c46 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aby se tato změna projevila, je třeba zařízení restartovat. Restartujte zařízení nebo zrušte akci."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kabelová sluchátka"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Sluchátka"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofonu"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnout"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Vypnout"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Probíhá změna sítě operátora"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index f14c04b..82d7021 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at anvende denne ændring. Genstart nu, eller annuller."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Høretelefoner med ledning"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Høretelefoner"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lydenhed"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Stik til mikrofon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lydenhed"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Til"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Fra"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Skift af mobilnetværk"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 972bb1f..00ae7e4 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Damit diese Änderung übernommen wird, musst du dein Gerät neu starten. Du kannst es jetzt neu starten oder den Vorgang abbrechen."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kabelgebundene Kopfhörer"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kopfhörer"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-Audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonanschluss"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-Mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-Audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-Mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-Mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"An"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Aus"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobilfunknetzwerk wird gewechselt"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 86a4cb9..7dbe274 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Για να εφαρμοστεί αυτή η αλλαγή, θα πρέπει να επανεκκινήσετε τη συσκευή σας. Επανεκκίνηση τώρα ή ακύρωση."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Ενσύρματα ακουστικά"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Ακουστικά"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Ήχος USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Υποδοχή μικροφώνου"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Μικρόφωνο USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Ήχος USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Μικρόφωνο USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Μικρόφωνο BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ενεργό"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ανενεργό"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Αλλαγή δικτύου εταιρείας κινητής τηλεφωνίας"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 7296c96..f29a02b 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index c07bd34..81489fe 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -686,9 +686,9 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphone"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
+    <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Wired audio"</string>
     <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Wired microphone"</string>
     <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string>
     <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 7296c96..f29a02b 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 7296c96..f29a02b 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c933c7e..07b8d88 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Debes reiniciar el dispositivo para que se aplique el cambio. Reinícialo ahora o cancela la acción."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculares"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector para micrófono"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activar"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivar"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio de proveedor de red"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6215e77..56355b1 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reinicia ahora o cancela la acción."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculares"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector jack para micrófono"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambiando la red del operador"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 81926e5..6582864 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Selle muudatuse rakendamiseks tuleb seade taaskäivitada. Taaskäivitage kohe või tühistage."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Juhtmega kõrvaklapid"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kõrvaklapid"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-heli"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoni pistikupesa"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-heli"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Sees"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Väljas"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operaatori võrku muudetakse"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 45c2daa..f9ec6f7 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aldaketa aplikatzeko, berrabiarazi egin behar da gailua. Berrabiaraz ezazu orain, edo utzi bertan behera."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Entzungailu kableduna"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Entzungailua"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB bidezko audioa"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonoaren konektorea"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB bidezko mikrofonoa"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB bidezko audioa"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB bidezko mikrofonoa"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth bidezko mikrofonoa"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktibatu"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desaktibatu"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operadorearen sarea aldatzen"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index af96924..9c704c5 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"برای اعمال این تغییر، دستگاه باید بازراه‌اندازی شود. یا اکنون بازراه‌اندازی کنید یا لغو کنید."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"هدفون سیمی"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"هدفون"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏بلندگوی USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"فیش میکروفون"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏میکروفون USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏بلندگوی USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏میکروفون USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"میکروفون بلوتوث"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"روشن"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"خاموش"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"تغییر شبکه شرکت مخابراتی"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index ab4ac1e..193ec70 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Laitteesi on käynnistettävä uudelleen, jotta muutos tulee voimaan. Käynnistä uudelleen nyt tai peru."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Langalliset kuulokkeet"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kuulokkeet"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoniliitäntä"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoni"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoni"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoni"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Päällä"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ei käytössä"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operaattorin verkko muuttuu"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 05157b7..b527990 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -393,7 +393,7 @@
     <string name="disable_overlays_summary" msgid="1954852414363338166">"Toujours utiliser le GPU pour la composition écran"</string>
     <string name="simulate_color_space" msgid="1206503300335835151">"Simuler espace colorimétrique"</string>
     <string name="enable_opengl_traces_title" msgid="4638773318659125196">"Enable OpenGL traces"</string>
-    <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Désact. routage audio USB"</string>
+    <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Désactiver le routage audio USB"</string>
     <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Désactiver routage automatique appareils audio USB"</string>
     <string name="debug_layout" msgid="1659216803043339741">"Afficher les contours"</string>
     <string name="debug_layout_summary" msgid="8825829038287321978">"Afficher les limites, les marges de clip, etc."</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Votre appareil doit être redémarré pour que ce changement prenne effet. Redémarrez-le maintenant ou annulez la modification."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Écouteurs filaires"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Écouteurs"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio par USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Prise du microphone"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microphone USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio par USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microphone USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microphone BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activé"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Désactivé"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Changer de réseau de fournisseur de services"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 31616a7..fb2bcf3 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Vous devez redémarrer l\'appareil pour que cette modification soit appliquée. Redémarrez maintenant ou annulez l\'opération."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Casque filaire"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Casque audio"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Connecteur micro"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micro USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micro USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micro Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Allumé"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Éteint"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Modification du réseau de l\'opérateur"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 2fccfba..d6e9f3f 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necesario reiniciar o teu dispositivo para aplicar este cambio. Reiníciao agora ou cancela o cambio."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculares"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector do micrófono"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activada"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivada"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio de rede do operador"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 3209b07..b804dd4 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"આ ફેરફારને લાગુ કરવા માટે તમારા ડિવાઇસને રીબૂટ કરવાની જરૂર છે. હમણાં જ રીબૂટ કરો કે રદ કરો."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"વાયરવાળો હૅડફોન"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"હૅડફોન"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ઑડિયો"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"માઇક જૅક"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB માઇક્રોફોન"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ઑડિયો"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB માઇક્રોફોન"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT માઇક્રોફોન"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ચાલુ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"બંધ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"કૅરીઅર નેટવર્કમાં ફેરફાર થઈ રહ્યો છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index d023dc7..7a22465 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -207,7 +207,7 @@
   <string-array name="window_animation_scale_entries">
     <item msgid="2675263395797191850">"एनिमेशन बंद"</item>
     <item msgid="5790132543372767872">"एनिमेशन स्‍केल .5x"</item>
-    <item msgid="2529692189302148746">"एनिमेशन स्‍केल 1x"</item>
+    <item msgid="2529692189302148746">"ऐनिमेशन स्‍केल 1 गुना"</item>
     <item msgid="8072785072237082286">"एनिमेशन स्‍केल 1.5x"</item>
     <item msgid="3531560925718232560">"एनिमेशन स्‍केल 2x"</item>
     <item msgid="4542853094898215187">"एनिमेशन स्‍केल 5x"</item>
@@ -225,7 +225,7 @@
   <string-array name="animator_duration_scale_entries">
     <item msgid="6416998593844817378">"एनिमेशन बंद"</item>
     <item msgid="875345630014338616">"एनिमेशन स्‍केल .5x"</item>
-    <item msgid="2753729231187104962">"एनिमेशन स्‍केल 1x"</item>
+    <item msgid="2753729231187104962">"ऐनिमेशन स्‍केल 1 गुना"</item>
     <item msgid="1368370459723665338">"एनिमेशन स्‍केल 1.5x"</item>
     <item msgid="5768005350534383389">"एनिमेशन स्‍केल 2x"</item>
     <item msgid="3728265127284005444">"एनिमेशन स्‍केल 5x"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index ef93e78..7c5e66c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -288,7 +288,7 @@
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM अनलॉक करने की अनुमति दें?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"चेतावनी: इस सेटिंग के चालू रहने पर डिवाइस सुरक्षा सुविधाएं इस डिवाइस पर काम नहीं करेंगी."</string>
     <string name="mock_location_app" msgid="6269380172542248304">"मॉक लोकेशन के लिए ऐप्लिकेशन चुनें"</string>
-    <string name="mock_location_app_not_set" msgid="6972032787262831155">"मॉक लोकेशन के लिए ऐप्लिकेशन सेट नहीं है"</string>
+    <string name="mock_location_app_not_set" msgid="6972032787262831155">"मॉक लोकेशन के लिए कोई ऐप्लिकेशन सेट नहीं है"</string>
     <string name="mock_location_app_set" msgid="4706722469342913843">"मॉक लोकेशन के लिए ऐप्लिकेशन: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"नेटवर्किंग"</string>
     <string name="wifi_display_certification" msgid="1805579519992520381">"वायरलेस डिसप्ले सर्टिफ़िकेशन"</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"बदली गई सेटिंग को लागू करने के लिए, डिवाइस को रीस्टार्ट करना होगा. अपने डिवाइस को रीस्टार्ट करें या रद्द करें."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तार वाला हेडफ़ोन"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफ़ोन"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"यूएसबी ऑडियो"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक्रोफ़ोन जैक"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"यूएसबी माइक्रोफ़ोन"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"यूएसबी ऑडियो"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"यूएसबी माइक्रोफ़ोन"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ब्लूटूथ माइक्रोफ़ोन"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"चालू है"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"बंद है"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"मोबाइल और इंटरनेट सेवा देने वाली कंपनी का नेटवर्क बदल रहा है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 7fda17b..fc474dd 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -287,7 +287,7 @@
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Neka kôd za pokretanje sustava bude otključan"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Želite li dopustiti OEM otključavanje?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"UPOZORENJE: značajke za zaštitu uređaja neće funkcionirati na ovom uređaju dok je ta postavka uključena."</string>
-    <string name="mock_location_app" msgid="6269380172542248304">"Odabir aplikacije za lažnu lokaciju"</string>
+    <string name="mock_location_app" msgid="6269380172542248304">"Odaberi aplikaciju za lažnu lokaciju"</string>
     <string name="mock_location_app_not_set" msgid="6972032787262831155">"Aplikacija za lažnu lokaciju nije postavljena"</string>
     <string name="mock_location_app_set" msgid="4706722469342913843">"Aplikacija za lažnu lokaciju: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"Umrežavanje"</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Uređaj se mora ponovno pokrenuti da bi se ta promjena primijenila. Ponovo pokrenite uređaj odmah ili odustanite."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB zvučnik"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utičnica za mikrofon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB zvučnik"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključeno"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promjena mreže mobilnog operatera"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 838965f..95b2892 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Az eszközt újra kell indítani, hogy a módosítás megtörténjen. Indítsa újra most, vagy vesse el a módosítást."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Vezetékes fejhallgató"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fejhallgató"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hangeszköz"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jack csatlakozója"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hangeszköz"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Be"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ki"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Szolgáltatói hálózat váltása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 6ad7007..51190f3 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Սարքն անհրաժեշտ է վերագործարկել, որպեսզի փոփոխությունը կիրառվի։ Վերագործարկեք հիմա կամ չեղարկեք փոփոխությունը։"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Լարով ականջակալ"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Ականջակալ"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB աուդիո"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Խոսափողի հարակցիչ"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB խոսափող"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB աուդիո"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB խոսափող"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth խոսափող"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Միացնել"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Անջատել"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Օպերատորի ցանցի փոփոխություն"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index c6f0800..84280cf 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -287,7 +287,7 @@
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Izinkan bootloader dibuka kuncinya"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Izinkan buka kunci OEM?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"PERINGATAN: Fitur perlindungan perangkat tidak akan berfungsi di perangkat ini saat setelan diaktifkan."</string>
-    <string name="mock_location_app" msgid="6269380172542248304">"Pilih aplikasi lokasi palsu"</string>
+    <string name="mock_location_app" msgid="6269380172542248304">"Pilih aplikasi lokasi simulasi"</string>
     <string name="mock_location_app_not_set" msgid="6972032787262831155">"Tidak ada aplikasi lokasi simulasi yang disetel"</string>
     <string name="mock_location_app_set" msgid="4706722469342913843">"Aplikasi lokasi palsu: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"Jaringan"</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Perangkat Anda harus di-reboot agar perubahan ini diterapkan. Reboot sekarang atau batalkan."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Headphone berkabel"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Colokan mikrofon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktif"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Nonaktif"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Jaringan operator berubah"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 2688e0b..daadae6 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Endurræsa þarf tækið til að þessi breyting taki gildi. Endurræstu núna eða hættu við."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Heyrnartól með snúru"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Heyrnartól"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hljóð"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Hljóðnematengi"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-hljóðnemi"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hljóð"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-hljóðnemi"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-hljóðnemi"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Kveikt"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Slökkt"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Skiptir um farsímakerfi"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index c0f1557..36aa2b3 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Per applicare questa modifica, devi riavviare il dispositivo. Riavvia ora o annulla."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Cuffie con cavo"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Cuffie"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Jack per microfono"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfono USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfono USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfono BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio della rete dell\'operatore"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index f1020ca..942b99b 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -416,7 +416,7 @@
     <string name="verbose_vendor_logging_notification_action" msgid="1190831050259046071">"הפעלה ליום אחד נוסף"</string>
     <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"מתבצעת השבתה אחרי יום אחד"</string>
     <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"בוצעה הפעלה ללא הגבלת זמן"</string>
-    <string name="window_animation_scale_title" msgid="5236381298376812508">"קנה מידה לאנימציה של חלון"</string>
+    <string name="window_animation_scale_title" msgid="5236381298376812508">"מהירות אנימציית מעבר במסכים"</string>
     <string name="transition_animation_scale_title" msgid="1278477690695439337">"קנה מידה לאנימציית מעבר"</string>
     <string name="animator_duration_scale_title" msgid="7082913931326085176">"קנה מידה למשך זמן אנימציה"</string>
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"יצירת הדמיה של תצוגות משניות"</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"צריך להפעיל מחדש את המכשיר כדי להחיל את השינוי. יש להפעיל מחדש עכשיו או לבטל."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"אוזניות חוטיות"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"אוזניות"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏אודיו ב-USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"שקע למיקרופון"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏מיקרופון ב-USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏אודיו ב-USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏מיקרופון ב-USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"‏מיקרופון BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"פועלת"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"מצב כבוי"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"רשת ספק משתנה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index ae68a03..5924ab2 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"この変更を適用するには、デバイスの再起動が必要です。今すぐ再起動するか、キャンセルしてください。"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線ヘッドフォン"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ヘッドフォン"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB オーディオ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"マイク差込口"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB マイク"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB オーディオ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB マイク"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT マイク"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ON"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"OFF"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"携帯通信会社のネットワークを変更します"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 850ddf1..5f3d54f 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ამ ცვლილების ასამოქმედებლად თქვენი მოწყობილობა უნდა გადაიტვირთოს. გადატვირთეთ ახლავე ან გააუქმეთ."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"სადენიანი ყურსასმენი"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ყურსასმენი"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB აუდიო"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"მიკროფონის ჯეკი"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB მიკროფონი"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB აუდიო"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB მიკროფონი"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT მიკროფონი"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ჩართვა"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"გამორთვა"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ოპერატორის ქსელის შეცვლა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 0cf0e04..bb6e353 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бұл өзгеріс күшіне енуі үшін, құрылғыны қайта жүктеу керек. Қазір қайта жүктеңіз не бас тартыңыз."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Сымды құлақаспап"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Құлақаспап"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофон ұяшығы"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофоны"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Қосу"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Өшіру"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Оператор желісін өзгерту"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 0d22701..20ed723 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ត្រូវតែ​ចាប់ផ្ដើម​ឧបករណ៍​របស់អ្នក​ឡើងវិញ ដើម្បីឱ្យ​ការផ្លាស់ប្ដូរ​នេះ​មានប្រសិទ្ធភាព។ ចាប់ផ្ដើមឡើងវិញ​ឥឡូវនេះ ឬ​បោះបង់​។"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"កាស​មានខ្សែ"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"កាស"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ឧបករណ៍បំពងសំឡេង USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ឌុយ​មីក្រូហ្វូន"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"មីក្រូហ្វូន USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ឧបករណ៍បំពងសំឡេង USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"មីក្រូហ្វូន USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"មីក្រូហ្វូន BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"បើក"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"បិទ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"បណ្តាញ​ក្រុមហ៊ុនសេវាទូរសព្ទ​កំពុងផ្លាស់ប្តូរ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 3dc663a..50a1234 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ಈ ಬದಲಾವಣೆ ಅನ್ವಯವಾಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ರೀಬೂಟ್ ಮಾಡಬೇಕು. ಇದೀಗ ರೀಬೂಟ್ ಮಾಡಿ ಅಥವಾ ರದ್ದುಗೊಳಿಸಿ."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ವೈಯರ್ ಹೊಂದಿರುವ ಹೆಡ್‌ಫೋನ್"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ಹೆಡ್‌ಫೋನ್"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ಆಡಿಯೋ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ಮೈಕ್‌ ಜ್ಯಾಕ್‌"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ಮೈಕ್ರೊಫೋನ್‌"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ಆಡಿಯೋ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ಮೈಕ್ರೊಫೋನ್‌"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ಮೈಕ್ರೊಫೋನ್"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ಆನ್ ಆಗಿದೆ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ಆಫ್"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ವಾಹಕ ನೆಟ್‌ವರ್ಕ್ ಬದಲಾಯಿಸುವಿಕೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 9f714e1..f9949ce 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"변경사항을 적용하려면 기기를 재부팅해야 합니다. 지금 재부팅하거나 취소하세요."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"유선 헤드폰"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"헤드폰"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 오디오"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"마이크 잭"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 마이크"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 오디오"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 마이크"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"블루투스 마이크"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"사용"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"사용 안 함"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"이동통신사 네트워크 변경"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index d7a3913..40bbb35 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бул өзгөрүү күчүнө кириши үчүн, түзмөктү өчүрүп күйгүзүңүз. Азыр же кийинчерээк өчүрүп күйгүзсөңүз болот."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Зымдуу гарнитура"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Гарнитура"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофондун оюкчасы"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофону"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Күйгүзүү"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Өчүрүү"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Байланыш оператору өзгөртүлүүдө"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index e57dae8..b5f8922 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ທ່ານຕ້ອງປິດເປີດອຸປະກອນຄືນໃໝ່ເພື່ອນຳໃຊ້ການປ່ຽນແປງນີ້. ປິດເປີດໃໝ່ດຽວນີ້ ຫຼື ຍົກເລີກ."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ຫູຟັງແບບມີສາຍ"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ຫູຟັງ"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ສຽງ USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ຊ່ອງສຽງໄມ"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ໄມໂຄຣໂຟນ USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ສຽງ USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ໄມໂຄຣໂຟນ USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ໄມໂຄຣໂຟນ BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ເປີດ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ປິດ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ການປ່ຽນເຄືອຂ່າຍຜູ້ໃຫ້ບໍລິການ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index a4d5b9b..fd23ece 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kad pakeitimas būtų pritaikytas, įrenginį reikia paleisti iš naujo. Dabar paleiskite iš naujo arba atšaukite."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Laidinės ausinės"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Ausinės"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB garsas"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofono jungtis"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofonas"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB garsas"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofonas"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"„Bluetooth“ mikrofonas"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Įjungta"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Išjungta"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Keičiamas operatoriaus tinklas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index bf6f80c..4fbbbe9 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Lai šīs izmaiņas tiktu piemērotas, nepieciešama ierīces atkārtota palaišana. Atkārtoti palaidiet to tūlīt vai atceliet izmaiņas."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Vadu austiņas"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Austiņas"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofona ligzda"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofons"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofons"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofons"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ieslēgts"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Izslēgts"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobilo sakaru operatora tīkla mainīšana"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index f664f72..ea53b93 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да се примени променава, уредот мора да се рестартира. Рестартирајте сега или откажете."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичени слушалки"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалка"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудио"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Приклучок за микрофон"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудио"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон со Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вклучено"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Исклучено"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Променување на мрежата на операторот"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 49af83d..43ac862 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ഈ മാറ്റം ബാധകമാകുന്നതിന് നിങ്ങളുടെ ഉപകരണം റീബൂട്ട് ചെയ്യേണ്ടതുണ്ട്. ഇപ്പോൾ റീബൂട്ട് ചെയ്യുകയോ റദ്ദാക്കുകയോ ചെയ്യുക."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"വയേർഡ് ഹെഡ്ഫോൺ"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ഹെഡ്ഫോൺ"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ഓഡിയോ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"മൈക്ക് ജാക്ക്"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB മൈക്രോഫോൺ"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ഓഡിയോ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB മൈക്രോഫോൺ"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT മൈക്രോഫോൺ"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ഓണാണ്"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ഓഫാണ്"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"കാരിയർ നെറ്റ്‌വർക്ക് മാറ്റൽ"</string>
diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml
index 2ebbb13..84c707f 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -225,7 +225,7 @@
   <string-array name="animator_duration_scale_entries">
     <item msgid="6416998593844817378">"Дүрс амилуулалт идэвхгүй"</item>
     <item msgid="875345630014338616">"Дүрс амилуулах далайц .5x"</item>
-    <item msgid="2753729231187104962">"Дүрс амилуулах далайц 1x"</item>
+    <item msgid="2753729231187104962">"Анимацийн масштаб 1x"</item>
     <item msgid="1368370459723665338">"Дүрс амилуулах далайц 1.5x"</item>
     <item msgid="5768005350534383389">"Дүрс амилуулалтын далайц 2x"</item>
     <item msgid="3728265127284005444">"Дүрс амилуулалтын далайц 5x"</item>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 2d010fe..d3b67c7 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл цуцлана уу."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Утастай чихэвч"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Чихэвч"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофоны чихэвчний оролт"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Асаах"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Унтраах"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Оператор компанийн сүлжээг өөрчилж байна"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index bea1500..9d22c00 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"हा बदल लागू करण्यासाठी तुमचे डिव्हाइस रीबूट करणे आवश्यक आहे. आता रीबूट करा किंवा रद्द करा."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"वायर्ड हेडफोन"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफोन"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ऑडिओ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक जॅक"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB मायक्रोफोन"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ऑडिओ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB मायक्रोफोन"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT मायक्रोफोन"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"सुरू करा"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"बंद करा"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"वाहक नेटवर्क बदलत आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index f045490..22ff9dd 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Peranti anda mesti dibut semula supaya perubahan ini berlaku. But semula sekarang atau batalkan."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fon kepala berwayar"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fon kepala"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Bicu mikrofon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Hidup"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Mati"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rangkaian pembawa berubah"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 4bdb50f..9a33712 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ဤအပြောင်းအလဲ ထည့်သွင်းရန် သင့်စက်ကို ပြန်လည်စတင်ရမည်။ ယခု ပြန်လည်စတင်ပါ သို့မဟုတ် ပယ်ဖျက်ပါ။"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ကြိုးတပ်နားကြပ်"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"နားကြပ်"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB အသံ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"မိုက်ဂျက်ပင်"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB မိုက်ခရိုဖုန်း"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB အသံ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB မိုက်ခရိုဖုန်း"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT မိုက်ခရိုဖုန်း"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ဖွင့်"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ပိတ်"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ဝန်ဆောင်မှုပေးသူ ကွန်ရက် ပြောင်းလဲနေသည်။"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 5de7069..3b03d73 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten din må startes på nytt for at denne endringen skal tre i kraft. Start på nytt nå eller avbryt."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Hodetelefoner med kabel"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Hodetelefoner"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lyd"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonkontakt"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lyd"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Av"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Bytting av operatørnettverk"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index ec5da45..84c8466 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"यो परिवर्तन लागू गर्न तपाईंको यन्त्र अनिवार्य रूपमा रिबुट गर्नु पर्छ। अहिले रिबुट गर्नुहोस् वा रद्द गर्नुहोस्।"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तारयुक्त हेडफोन"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफोन"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB अडियो"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइकको ज्याक"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB माइक्रोफोन"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB अडियो"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB माइक्रोफोन"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT माइक्रोफोन"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"अन छ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"अफ छ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"सेवा प्रदायकको नेटवर्क परिवर्तन गर्ने आइकन"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f6c5c9c..83df29a 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aan"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Je apparaat moet opnieuw worden opgestart om deze wijziging toe te passen. Start nu opnieuw op of annuleer de wijziging."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Bedrade koptelefoon"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Koptelefoon"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Microfoonaansluiting"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-microfoon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-microfoon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-microfoon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Uit"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Netwerk van provider wordt gewijzigd"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 2ab4d13..d03c8f3 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ଏହି ପରିବର୍ତ୍ତନ ଲାଗୁ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ନିଶ୍ଚିତ ରୂପେ ରିବୁଟ୍ କରାଯିବା ଆବଶ୍ୟକ। ବର୍ତ୍ତମାନ ରିବୁଟ୍ କରନ୍ତୁ କିମ୍ବା ବାତିଲ କରନ୍ତୁ।"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ତାରଯୁକ୍ତ ହେଡଫୋନ"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ହେଡଫୋନ"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ଅଡିଓ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ମାଇକ ଜେକ"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ମାଇକ୍ରୋଫୋନ"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ଅଡିଓ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ମାଇକ୍ରୋଫୋନ"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ମାଇକ୍ରୋଫୋନ"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ଚାଲୁ ଅଛି"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ବନ୍ଦ ଅଛି"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"କେରିଅର୍‍ ନେଟ୍‌ୱର୍କ ବଦଳୁଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f054ed5..1d48207 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ। ਹੁਣੇ ਰੀਬੂਟ ਕਰੋ ਜਾਂ ਰੱਦ ਕਰੋ।"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ਤਾਰ ਵਾਲੇ ਹੈੱਡਫ਼ੋਨ"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ਹੈੱਡਫ਼ੋਨ"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ਆਡੀਓ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ਮਾਈਕ ਜੈਕ"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ਆਡੀਓ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ਚਾਲੂ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ਬੰਦ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ਕੈਰੀਅਰ ਨੈੱਟਵਰਕ ਦੀ ਬਦਲੀ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 3786e02..e8492d3 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -686,12 +686,11 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Wprowadzenie zmiany wymaga ponownego uruchomienia urządzenia. Uruchom ponownie teraz lub anuluj."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Słuchawki przewodowe"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Słuchawki"</string>
+    <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Przewodowe urządzenie audio"</string>
     <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Dźwięk przez USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Gniazdo mikrofonu"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon przewodowy"</string>
     <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
-    <skip />
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Włączono"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Wyłączono"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Zmiana sieci operatora"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index 96f0c1a..bb18c47 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -206,19 +206,19 @@
   </string-array>
   <string-array name="window_animation_scale_entries">
     <item msgid="2675263395797191850">"Animação desligada"</item>
-    <item msgid="5790132543372767872">"Escala da animação 0,5 x"</item>
-    <item msgid="2529692189302148746">"Escala da animação 1x"</item>
+    <item msgid="5790132543372767872">"Escala de animação 0,5 x"</item>
+    <item msgid="2529692189302148746">"Escala de animação 1x"</item>
     <item msgid="8072785072237082286">"Escala de animação 1,5 x"</item>
-    <item msgid="3531560925718232560">"Escala da animação 2x"</item>
+    <item msgid="3531560925718232560">"Escala de animação 2x"</item>
     <item msgid="4542853094898215187">"Escala de animação 5 x"</item>
     <item msgid="5643881346223901195">"Escala de animação 10 x"</item>
   </string-array>
   <string-array name="transition_animation_scale_entries">
     <item msgid="3376676813923486384">"Animação desligada"</item>
-    <item msgid="753422683600269114">"Escala da animação 0,5 x"</item>
-    <item msgid="3695427132155563489">"Escala da animação 1x"</item>
+    <item msgid="753422683600269114">"Escala de animação 0,5 x"</item>
+    <item msgid="3695427132155563489">"Escala de animação 1x"</item>
     <item msgid="9032615844198098981">"Escala de animação 1,5 x"</item>
-    <item msgid="8473868962499332073">"Escala da animação 2x"</item>
+    <item msgid="8473868962499332073">"Escala de animação 2x"</item>
     <item msgid="4403482320438668316">"Escala de animação 5 x"</item>
     <item msgid="169579387974966641">"Escala de animação 10 x"</item>
   </string-array>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 3a8b2b8..20bd69a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -417,8 +417,8 @@
     <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"É desativado depois de 1 dia"</string>
     <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Ativado indefinidamente"</string>
     <string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação da janela"</string>
-    <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
-    <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração do Animator"</string>
+    <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala anim. de transição"</string>
+    <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala duração Animator"</string>
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular telas secundárias"</string>
     <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fone de ouvido"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desativado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Alteração de rede da operadora"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 7dbabb8..488dd5a 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reiniciar o dispositivo para aplicar esta alteração. Reinicie agora ou cancele."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auscultadores com fios"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auscultadores"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ligado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desligado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rede do operador em mudança."</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index 96f0c1a..bb18c47 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -206,19 +206,19 @@
   </string-array>
   <string-array name="window_animation_scale_entries">
     <item msgid="2675263395797191850">"Animação desligada"</item>
-    <item msgid="5790132543372767872">"Escala da animação 0,5 x"</item>
-    <item msgid="2529692189302148746">"Escala da animação 1x"</item>
+    <item msgid="5790132543372767872">"Escala de animação 0,5 x"</item>
+    <item msgid="2529692189302148746">"Escala de animação 1x"</item>
     <item msgid="8072785072237082286">"Escala de animação 1,5 x"</item>
-    <item msgid="3531560925718232560">"Escala da animação 2x"</item>
+    <item msgid="3531560925718232560">"Escala de animação 2x"</item>
     <item msgid="4542853094898215187">"Escala de animação 5 x"</item>
     <item msgid="5643881346223901195">"Escala de animação 10 x"</item>
   </string-array>
   <string-array name="transition_animation_scale_entries">
     <item msgid="3376676813923486384">"Animação desligada"</item>
-    <item msgid="753422683600269114">"Escala da animação 0,5 x"</item>
-    <item msgid="3695427132155563489">"Escala da animação 1x"</item>
+    <item msgid="753422683600269114">"Escala de animação 0,5 x"</item>
+    <item msgid="3695427132155563489">"Escala de animação 1x"</item>
     <item msgid="9032615844198098981">"Escala de animação 1,5 x"</item>
-    <item msgid="8473868962499332073">"Escala da animação 2x"</item>
+    <item msgid="8473868962499332073">"Escala de animação 2x"</item>
     <item msgid="4403482320438668316">"Escala de animação 5 x"</item>
     <item msgid="169579387974966641">"Escala de animação 10 x"</item>
   </string-array>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 3a8b2b8..20bd69a 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -417,8 +417,8 @@
     <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"É desativado depois de 1 dia"</string>
     <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Ativado indefinidamente"</string>
     <string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação da janela"</string>
-    <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
-    <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração do Animator"</string>
+    <string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala anim. de transição"</string>
+    <string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala duração Animator"</string>
     <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simular telas secundárias"</string>
     <string name="debug_applications_category" msgid="5394089406638954196">"Apps"</string>
     <string name="immediately_destroy_activities" msgid="1826287490705167403">"Não manter atividades"</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fone de ouvido"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desativado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Alteração de rede da operadora"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 9e0eb4f..edd0356 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să repornești dispozitivul. Repornește-l acum sau anulează."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Căști cu fir"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Căști"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mufă pentru microfon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfon USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfon USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfon BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activat"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Dezactivat"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Se schimbă rețeaua operatorului"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index dfde272..55fbfa5 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Чтобы изменение вступило в силу, необходимо перезапустить устройство. Вы можете сделать это сейчас или позже."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Проводные наушники"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Наушники"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудиоустройство"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофонный разъем"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудиоустройство"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вкл."</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Выкл."</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Сменить сеть"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index fe011c7..d48824a 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"මෙම වෙනස යෙදීමට ඔබේ උපාංගය නැවත පණ ගැන්විය යුතුය. දැන් නැවත පණ ගන්වන්න හෝ අවලංගු කරන්න."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"රැහැන්ගත හෙඩ්ෆෝන්"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"හෙඩ්ෆෝන්"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ශ්‍රව්‍ය"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"මයික් ජැක්කුව"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB මයික්‍රෆෝනය"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ශ්‍රව්‍ය"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB මයික්‍රෆෝනය"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT මයික්‍රෆෝනය"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ක්‍රියාත්මකයි"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ක්‍රියාවිරහිතයි"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"වාහක ජාලය වෙනස් වෙමින්"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 46ab80a..b807482 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Zmena sa prejaví až po reštarte zariadenia. Môžete ho teraz reštartovať alebo akciu zrušiť."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Slúchadlá s káblom"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slúchadlá"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk cez USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofónu"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofón s rozhraním USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk cez USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofón s rozhraním USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofón Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnúť"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Vypnúť"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mení sa sieť operátora"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 0ea0d43..a4c0622 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Napravo je treba znova zagnati, da bo ta sprememba uveljavljena. Znova zaženite zdaj ali prekličite."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žične slušalke"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalke"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvok USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Vtič za mikrofon"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvok USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vklop"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Izklop"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Spreminjanje omrežja operaterja"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 54e75b2..19c4211 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pajisja jote duhet të riniset që ky ndryshim të zbatohet. Rinise tani ose anuloje."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kufje me tela"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kufje"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Pajisja audio me USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Fisha e mikrofonit"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofoni me USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Pajisja audio me USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofoni me USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofoni me Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktive"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Joaktive"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rrjeti i operatorit celular po ndryshohet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 742e6ca..1862910 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Морате да рестартујете уређај да би се ова промена применила. Рестартујте га одмах или откажите."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичане слушалице"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалице"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Утикач за микрофон"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Укључено"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Искључено"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Промена мреже мобилног оператера"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index b726921..1ee80c5 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten måste startas om för att ändringen ska börja gälla. Starta om nu eller avbryt."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Hörlur med kabel"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Hörlur"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-ljud"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonuttag"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-ljud"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Av"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Byter leverantörsnätverk"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index adc21db..d33eafe 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kipokea sauti cha kichwani chenye waya"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kipokea sauti cha kichwani"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Sauti ya USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Pini ya maikrofoni"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Maikrofoni ya USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Sauti ya USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Maikrofoni ya USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Maikrofoni ya Bluetooth"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Umewashwa"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Umezimwa"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mabadiliko katika mtandao wa mtoa huduma"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 0e26985..2c0781d 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"இந்த மாற்றங்கள் செயல்படுத்தப்பட உங்கள் சாதனத்தை மறுபடி தொடங்க வேண்டும். இப்போதே மறுபடி தொடங்கவும் அல்லது ரத்துசெய்யவும்."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"வயர்டு ஹெட்ஃபோன்"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ஹெட்ஃபோன்"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ஆடியோ"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"மைக் ஜாக்"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB மைக்ரோஃபோன்"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ஆடியோ"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB மைக்ரோஃபோன்"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT மைக்ரோஃபோன்"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ஆன்"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ஆஃப்"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"மொபைல் நிறுவன நெட்வொர்க்கை மாற்றும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 0f835fa..145af84 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ఈ మార్పును వర్తింపజేయాలంటే మీరు మీ పరికరాన్ని తప్పనిసరిగా రీబూట్ చేయాలి. ఇప్పుడే రీబూట్ చేయండి లేదా రద్దు చేయండి."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"వైర్ ఉన్న హెడ్‌ఫోన్"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"హెడ్‌ఫోన్"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ఆడియో"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"మైక్ జాక్"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB మైక్రోఫోన్"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ఆడియో"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB మైక్రోఫోన్"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT మైక్రోఫోన్"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ఆన్‌లో ఉంది"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ఆఫ్‌లో ఉంది"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"క్యారియర్ నెట్‌వర్క్ మారుతోంది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b5deb55..ae6585c 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"คุณต้องรีบูตอุปกรณ์เพื่อให้การเปลี่ยนแปลงนี้มีผล รีบูตเลยหรือยกเลิก"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"หูฟังแบบใช้สาย"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"หูฟัง"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"เสียง USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ช่องเสียบไมค์"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ไมโครโฟน USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"เสียง USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ไมโครโฟน USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ไมโครโฟน BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"เปิด"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ปิด"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"การเปลี่ยนเครือข่ายผู้ให้บริการ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index b664108..6e60e2b 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Dapat i-reboot ang iyong device para mailapat ang pagbabagong ito. Mag-reboot ngayon o kanselahin."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired na headphone"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Jack ng mikropono"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB na mikropono"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB na mikropono"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT na mikropono"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Naka-on"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Naka-off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Nagpapalit ng carrier network"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 7cee87e..8d709b9 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazınızın yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kablolu kulaklık"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Kulaklık"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ses cihazı"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jakı"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ses cihazı"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofonu"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Açık"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Kapalı"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operatör ağı değiştiriliyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index dd7f157..7b1086c 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Щоб застосувати ці зміни, потрібний перезапуск. Перезапустіть пристрій або скасуйте зміни."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Дротові навушники"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Навушники"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудіо"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Гніздо для мікрофона"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-мікрофон"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудіо"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-мікрофон"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-мікрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Увімкнено"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Вимкнено"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Змінення мережі оператора"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 23162cf..db00474 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -94,7 +94,7 @@
     <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"منسلک ہے (فون کے علاوہ)، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"منسلک ہے (میڈیا کے علاوہ)، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"منسلک ہے (فون یا میڈیا کے علاوہ)، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"فعال۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
+    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"‏فعال۔ ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"‏فعال۔ L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، ‏R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری۔"</string>
     <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"فعال ہے۔ بایاں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"فعال ہے۔ دایاں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
@@ -112,7 +112,7 @@
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"فعال ہے (بایاں اور دایاں)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"فعال (صرف میڈیا)۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"‏فعال (صرف میڈیا)۔ L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، ‏R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری۔"</string>
-    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
+    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"‏منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
     <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"‏منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، ‏R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> بیٹری۔"</string>
     <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ بائيں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
     <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"منسلک ہے (آڈیو کے اشتراک کو سپورٹ کرتا ہے)۔ دائيں: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> بیٹری۔"</string>
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"اس تبدیلی کو لاگو کرنے کے ليے آپ کے آلہ کو ریبوٹ کرنا ضروری ہے۔ ابھی ریبوٹ کریں یا منسوخ کریں۔"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"تار والا ہیڈ فون"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ہیڈ فون"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏‫USB آڈیو"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مائیک جیک"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏‫USB مائیکروفون"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"‏‫USB آڈیو"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"‏‫USB مائیکروفون"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"‏‫BT مائیکروفون"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"آن"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"آف"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"کیریئر نیٹ ورک کی تبدیلی"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 8fa256f..833863e 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar kuchga kirishi uchun qurilmani oʻchirib yoqing. Buni hozir yoki keyinroq bajarishingiz mumkin."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Simli quloqlik"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Quloqlik"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon ulagichi"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Yoniq"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Oʻchiq"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobil tarmoqni o‘zgartirish"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b4e09e0..fba3a13 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bạn phải khởi động lại thiết bị để áp dụng sự thay đổi này. Hãy khởi động lại ngay hoặc hủy."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Tai nghe có dây"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Tai nghe"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Âm thanh qua cổng USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Giắc cắm micrô"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrô USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Âm thanh qua cổng USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrô USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrô BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Đang bật"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Đang tắt"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Thay đổi mạng của nhà mạng"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index f9ce1c3..29a4590 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"设备必须重新启动才能应用此更改。您可以立即重新启动或取消。"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有线耳机"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"头戴式耳机"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音频"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麦克风插孔"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麦克风"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音频"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麦克风"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"蓝牙麦克风"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"开启"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"关闭"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"运营商网络正在更改"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index c50c7ba..71fd6ee 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"你的裝置必須重新開機,才能套用此變更。請立即重新開機或取消。"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線耳機"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"耳機"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"關閉"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"流動網絡供應商網絡正在變更"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index c5885de..b5700bc 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"裝置必須重新啟動才能套用這項變更。請立即重新啟動或取消變更。"</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線耳機"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"耳機"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"關閉"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"電信業者網路正在進行變更"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index a39b436..2f822b3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -686,12 +686,13 @@
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kufanele idivayisi yakho iqaliswe ukuze lolu shintsho lusebenze. Qalisa manje noma khansela."</string>
     <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Amahedifoni anentambo"</string>
-    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Amahedifoni"</string>
-    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Umsindo we-USB"</string>
-    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Umgodi we-earphone ye-mic"</string>
-    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Imakrofoni ye-USB"</string>
-    <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) -->
+    <!-- no translation found for media_transfer_headphone_name (1157798825650178478) -->
     <skip />
+    <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Umsindo we-USB"</string>
+    <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) -->
+    <skip />
+    <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Imakrofoni ye-USB"</string>
+    <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Imakrofoni ye-BT"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vuliwe"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Valiwe"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Inethiwekhi yenkampani yenethiwekhi iyashintsha"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 739c7d6..fd2a1cb 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -819,6 +819,11 @@
     <!-- Summary of checkbox setting that enables the terminal app. [CHAR LIMIT=64] -->
     <string name="enable_terminal_summary">Enable terminal app that offers local shell access</string>
 
+    <!-- Title of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=32] -->
+    <string name="enable_linux_terminal_title">Linux development environment</string>
+    <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=64] -->
+    <string name="enable_linux_terminal_summary">Run Linux terminal on Android</string>
+
     <!-- HDCP checking title, used for debug purposes only. [CHAR LIMIT=25] -->
     <string name="hdcp_checking_title">HDCP checking</string>
     <!-- HDCP checking dialog title, used for debug purposes only. [CHAR LIMIT=25] -->
@@ -1640,13 +1645,13 @@
     <string name="media_transfer_wired_headphone_name">Wired headphone</string>
 
     <!-- Name of the 3.5mm headphone, used in desktop devices. [CHAR LIMIT=50] -->
-    <string name="media_transfer_headphone_name">Headphone</string>
+    <string name="media_transfer_headphone_name">Wired audio</string>
 
     <!-- Name of the usb audio device speaker, used in desktop devices. [CHAR LIMIT=50] -->
     <string name="media_transfer_usb_audio_name">USB audio</string>
 
     <!-- Name of the 3.5mm audio device mic. [CHAR LIMIT=50] -->
-    <string name="media_transfer_wired_device_mic_name">Mic jack</string>
+    <string name="media_transfer_wired_device_mic_name">Wired microphone</string>
 
     <!-- Name of the usb audio device mic. [CHAR LIMIT=50] -->
     <string name="media_transfer_usb_device_mic_name">USB microphone</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 8b6351e..7fdb32c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -60,7 +60,7 @@
         mBtManager = localBtManager;
         mHearingAidDeviceManager = new HearingAidDeviceManager(context, localBtManager,
                 mCachedDevices);
-        mCsipDeviceManager = new CsipDeviceManager(localBtManager, mCachedDevices);
+        mCsipDeviceManager = new CsipDeviceManager(context, localBtManager, mCachedDevices);
     }
 
     public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 6dab224..b9f16ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -21,8 +21,10 @@
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
+import android.content.Context;
 import android.os.Build;
 import android.os.ParcelUuid;
+import android.os.UserManager;
 import android.util.Log;
 
 import androidx.annotation.ChecksSdkIntAtLeast;
@@ -45,9 +47,11 @@
 
     private final LocalBluetoothManager mBtManager;
     private final List<CachedBluetoothDevice> mCachedDevices;
+    private final Context mContext;
 
-    CsipDeviceManager(LocalBluetoothManager localBtManager,
+    CsipDeviceManager(Context context, LocalBluetoothManager localBtManager,
             List<CachedBluetoothDevice> cachedDevices) {
+        mContext = context;
         mBtManager = localBtManager;
         mCachedDevices = cachedDevices;
     }
@@ -379,7 +383,11 @@
                 preferredMainDevice.refresh();
                 hasChanged = true;
             }
-            syncAudioSharingSourceIfNeeded(preferredMainDevice);
+            if (isWorkProfile()) {
+                log("addMemberDevicesIntoMainDevice: skip sync source for work profile");
+            } else {
+                syncAudioSharingSourceIfNeeded(preferredMainDevice);
+            }
         }
         if (hasChanged) {
             log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: "
@@ -388,6 +396,11 @@
         return hasChanged;
     }
 
+    private boolean isWorkProfile() {
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        return userManager != null && userManager.isManagedProfile();
+    }
+
     private void syncAudioSharingSourceIfNeeded(CachedBluetoothDevice mainDevice) {
         boolean isAudioSharingEnabled = BluetoothUtils.isAudioSharingEnabled();
         if (isAudioSharingEnabled) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 364e95c..6a9d568 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -18,6 +18,8 @@
 
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
 
+import static java.util.stream.Collectors.toList;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.bluetooth.BluetoothAdapter;
@@ -64,6 +66,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
@@ -84,6 +87,8 @@
     public static final String EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE = "BT_DEVICE_TO_AUTO_ADD_SOURCE";
     public static final String EXTRA_START_LE_AUDIO_SHARING = "START_LE_AUDIO_SHARING";
     public static final String EXTRA_PAIR_AND_JOIN_SHARING = "PAIR_AND_JOIN_SHARING";
+    public static final String BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID =
+            "bluetooth_le_broadcast_primary_device_group_id";
     public static final int BROADCAST_STATE_UNKNOWN = 0;
     public static final int BROADCAST_STATE_ON = 1;
     public static final int BROADCAST_STATE_OFF = 2;
@@ -1121,6 +1126,10 @@
 
     /** Update fallback active device if needed. */
     public void updateFallbackActiveDeviceIfNeeded() {
+        if (isWorkProfile(mContext)) {
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded for work profile.");
+            return;
+        }
         if (mServiceBroadcast == null) {
             Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to broadcast profile is null");
             return;
@@ -1135,71 +1144,114 @@
             Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to assistant profile is null");
             return;
         }
-        List<BluetoothDevice> devicesInBroadcast = getDevicesInBroadcast();
-        if (devicesInBroadcast.isEmpty()) {
+        Map<Integer, List<BluetoothDevice>> deviceGroupsInBroadcast = getDeviceGroupsInBroadcast();
+        if (deviceGroupsInBroadcast.isEmpty()) {
             Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to no sinks in broadcast");
             return;
         }
-        List<BluetoothDevice> devices =
-                BluetoothAdapter.getDefaultAdapter().getMostRecentlyConnectedDevices();
-        BluetoothDevice targetDevice = null;
-        // Find the earliest connected device in sharing session.
-        int targetDeviceIdx = -1;
-        for (BluetoothDevice device : devicesInBroadcast) {
-            if (devices.contains(device)) {
-                int idx = devices.indexOf(device);
-                if (idx > targetDeviceIdx) {
-                    targetDeviceIdx = idx;
-                    targetDevice = device;
+        int targetGroupId = BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+        int fallbackActiveGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast(
+                mContext.getContentResolver());
+        if (Flags.audioSharingHysteresisModeFix()) {
+            int userPreferredPrimaryGroupId = getUserPreferredPrimaryGroupId();
+            if (userPreferredPrimaryGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
+                    && deviceGroupsInBroadcast.containsKey(userPreferredPrimaryGroupId)) {
+                if (userPreferredPrimaryGroupId == fallbackActiveGroupId) {
+                    Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, already user preferred");
+                    return;
+                } else {
+                    targetGroupId = userPreferredPrimaryGroupId;
                 }
             }
+            if (targetGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+                // If there is no user preferred primary device, set the earliest connected
+                // device in sharing session as the fallback.
+                targetGroupId = getEarliestConnectedDeviceGroup(deviceGroupsInBroadcast);
+            }
+        } else {
+            // Set the earliest connected device in sharing session as the fallback.
+            targetGroupId = getEarliestConnectedDeviceGroup(deviceGroupsInBroadcast);
         }
-        if (targetDevice == null) {
-            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, target is null");
+        Log.d(TAG, "updateFallbackActiveDeviceIfNeeded, target group id = " + targetGroupId);
+        if (targetGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) return;
+        if (targetGroupId == fallbackActiveGroupId) {
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, already is fallback");
             return;
         }
-        CachedBluetoothDevice targetCachedDevice = mDeviceManager.findDevice(targetDevice);
+        CachedBluetoothDevice targetCachedDevice = getMainDevice(
+                deviceGroupsInBroadcast.get(targetGroupId));
         if (targetCachedDevice == null) {
-            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, fail to find cached bt device");
-            return;
-        }
-        int fallbackActiveGroupId = getFallbackActiveGroupId();
-        if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
-                && BluetoothUtils.getGroupId(targetCachedDevice) == fallbackActiveGroupId) {
-            Log.d(
-                    TAG,
-                    "Skip updateFallbackActiveDeviceIfNeeded, already is fallback: "
-                            + fallbackActiveGroupId);
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, fail to find main device");
             return;
         }
         Log.d(
                 TAG,
                 "updateFallbackActiveDeviceIfNeeded, set active device: "
-                        + targetDevice.getAnonymizedAddress());
+                        + targetCachedDevice.getDevice());
         targetCachedDevice.setActive();
     }
 
-    private List<BluetoothDevice> getDevicesInBroadcast() {
+    @NonNull
+    private Map<Integer, List<BluetoothDevice>> getDeviceGroupsInBroadcast() {
         boolean hysteresisModeFixEnabled = Flags.audioSharingHysteresisModeFix();
         List<BluetoothDevice> connectedDevices = mServiceBroadcastAssistant.getConnectedDevices();
         return connectedDevices.stream()
                 .filter(
-                        bluetoothDevice -> {
+                        device -> {
                             List<BluetoothLeBroadcastReceiveState> sourceList =
-                                    mServiceBroadcastAssistant.getAllSources(
-                                            bluetoothDevice);
+                                    mServiceBroadcastAssistant.getAllSources(device);
                             return !sourceList.isEmpty() && sourceList.stream().anyMatch(
                                     source -> hysteresisModeFixEnabled
                                             ? BluetoothUtils.isSourceMatched(source, mBroadcastId)
                                             : BluetoothUtils.isConnected(source));
                         })
-                .collect(Collectors.toList());
+                .collect(Collectors.groupingBy(
+                        device -> BluetoothUtils.getGroupId(mDeviceManager.findDevice(device))));
     }
 
-    private int getFallbackActiveGroupId() {
+    private int getEarliestConnectedDeviceGroup(
+            @NonNull Map<Integer, List<BluetoothDevice>> deviceGroups) {
+        List<BluetoothDevice> devices =
+                BluetoothAdapter.getDefaultAdapter().getMostRecentlyConnectedDevices();
+        // Find the earliest connected device in sharing session.
+        int targetDeviceIdx = -1;
+        int targetGroupId = BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+        for (Map.Entry<Integer, List<BluetoothDevice>> entry : deviceGroups.entrySet()) {
+            for (BluetoothDevice device : entry.getValue()) {
+                if (devices.contains(device)) {
+                    int idx = devices.indexOf(device);
+                    if (idx > targetDeviceIdx) {
+                        targetDeviceIdx = idx;
+                        targetGroupId = entry.getKey();
+                    }
+                }
+            }
+        }
+        return targetGroupId;
+    }
+
+    @Nullable
+    private CachedBluetoothDevice getMainDevice(@Nullable List<BluetoothDevice> devices) {
+        if (devices == null || devices.size() == 1) return null;
+        List<CachedBluetoothDevice> cachedDevices =
+                devices.stream()
+                        .map(device -> mDeviceManager.findDevice(device))
+                        .filter(Objects::nonNull)
+                        .collect(toList());
+        for (CachedBluetoothDevice cachedDevice : cachedDevices) {
+            if (!cachedDevice.getMemberDevice().isEmpty()) {
+                return cachedDevice;
+            }
+        }
+        CachedBluetoothDevice mainDevice = cachedDevices.isEmpty() ? null : cachedDevices.get(0);
+        return mainDevice;
+    }
+
+    private int getUserPreferredPrimaryGroupId() {
+        // TODO: use real key name in SettingsProvider
         return Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                "bluetooth_le_broadcast_fallback_active_group_id",
+                mContentResolver,
+                BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID,
                 BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
index c9f9d1b..a4c5a00d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -42,6 +42,7 @@
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 
 /**
  * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app and the
@@ -63,6 +64,7 @@
     private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
     private BluetoothLeBroadcastMetadata.Builder mBuilder;
     private boolean mIsProfileReady;
+    private Executor mExecutor;
     // Cached assistant callbacks being register before service is connected.
     private final Map<BluetoothLeBroadcastAssistant.Callback, Executor> mCachedCallbackExecutorMap =
             new ConcurrentHashMap<>();
@@ -98,15 +100,19 @@
                     }
 
                     mProfileManager.callServiceConnectedListeners();
-                    mIsProfileReady = true;
-                    if (DEBUG) {
-                        Log.d(
-                                TAG,
-                                "onServiceConnected, register mCachedCallbackExecutorMap = "
-                                        + mCachedCallbackExecutorMap);
+                    if (!mIsProfileReady) {
+                        mIsProfileReady = true;
+                        registerServiceCallBack(mExecutor, mAssistantCallback);
+                        if (DEBUG) {
+                            Log.d(
+                                    TAG,
+                                    "onServiceConnected, register mCachedCallbackExecutorMap = "
+                                            + mCachedCallbackExecutorMap);
+                        }
+                        mCachedCallbackExecutorMap.forEach(
+                                (callback, executor) -> registerServiceCallBack(executor,
+                                        callback));
                     }
-                    mCachedCallbackExecutorMap.forEach(
-                            (callback, executor) -> registerServiceCallBack(executor, callback));
                 }
 
                 @Override
@@ -119,17 +125,71 @@
                         Log.d(TAG, "Bluetooth service disconnected");
                     }
                     mProfileManager.callServiceDisconnectedListeners();
-                    mIsProfileReady = false;
-                    mCachedCallbackExecutorMap.clear();
+                    if (mIsProfileReady) {
+                        mIsProfileReady = false;
+                        unregisterServiceCallBack(mAssistantCallback);
+                        mCachedCallbackExecutorMap.clear();
+                    }
                 }
             };
 
+    private final BluetoothLeBroadcastAssistant.Callback mAssistantCallback =
+            new BluetoothLeBroadcastAssistant.Callback() {
+                @Override
+                public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+                }
+
+                @Override
+                public void onSearchStarted(int reason) {}
+
+                @Override
+                public void onSearchStartFailed(int reason) {}
+
+                @Override
+                public void onSearchStopped(int reason) {}
+
+                @Override
+                public void onSearchStopFailed(int reason) {}
+
+                @Override
+                public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {}
+
+                @Override
+                public void onSourceAddFailed(
+                        @NonNull BluetoothDevice sink,
+                        @NonNull BluetoothLeBroadcastMetadata source,
+                        int reason) {}
+
+                @Override
+                public void onSourceModified(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
+                @Override
+                public void onSourceModifyFailed(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
+                @Override
+                public void onSourceRemoved(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
+                @Override
+                public void onSourceRemoveFailed(
+                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+
+                @Override
+                public void onReceiveStateChanged(
+                        @NonNull BluetoothDevice sink,
+                        int sourceId,
+                        @NonNull BluetoothLeBroadcastReceiveState state) {}
+            };
+
     public LocalBluetoothLeBroadcastAssistant(
             Context context,
             CachedBluetoothDeviceManager deviceManager,
             LocalBluetoothProfileManager profileManager) {
         mProfileManager = profileManager;
         mDeviceManager = deviceManager;
+        mExecutor = Executors.newSingleThreadExecutor();
         BluetoothAdapter.getDefaultAdapter()
                 .getProfileProxy(
                         context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt
index 24815fa..91a99ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt
@@ -16,7 +16,6 @@
 
 package com.android.settingslib.bluetooth
 
-import android.bluetooth.BluetoothAdapter
 import android.bluetooth.BluetoothDevice
 import android.bluetooth.BluetoothLeBroadcastAssistant
 import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -82,9 +81,5 @@
             ConcurrentUtils.DIRECT_EXECUTOR,
             callback,
         )
-        awaitClose {
-            if (BluetoothAdapter.getDefaultAdapter()?.isEnabled == true) {
-                unregisterServiceCallBack(callback)
-            }
-        }
+        awaitClose { unregisterServiceCallBack(callback) }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt
index 0bcf7fe..07abb6b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt
@@ -68,3 +68,44 @@
                 awaitClose { unregisterServiceCallBack(listener) }
             }
             .buffer(capacity = Channel.CONFLATED)
+
+/** [Flow] for [BluetoothLeBroadcast.Callback] onPlaybackStarted event */
+val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit>
+    get() =
+        callbackFlow {
+            val listener =
+                object : BluetoothLeBroadcast.Callback {
+                    override fun onBroadcastStarted(reason: Int, broadcastId: Int) {}
+
+                    override fun onBroadcastStartFailed(reason: Int) {
+                    }
+
+                    override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
+                    }
+
+                    override fun onBroadcastStopFailed(reason: Int) {
+                    }
+
+                    override fun onPlaybackStarted(reason: Int, broadcastId: Int) {
+                        launch { trySend(Unit) }
+                    }
+
+                    override fun onPlaybackStopped(reason: Int, broadcastId: Int) {
+                    }
+
+                    override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {}
+
+                    override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {}
+
+                    override fun onBroadcastMetadataChanged(
+                        broadcastId: Int,
+                        metadata: BluetoothLeBroadcastMetadata
+                    ) {}
+                }
+            registerServiceCallBack(
+                ConcurrentUtils.DIRECT_EXECUTOR,
+                listener,
+            )
+            awaitClose { unregisterServiceCallBack(listener) }
+        }
+            .buffer(capacity = Channel.CONFLATED)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
index 9cf4907..c85756e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
@@ -20,5 +20,5 @@
 import com.android.settingslib.bluetooth.devicesettings.IGetDeviceSettingsConfigCallback;
 
 interface IDeviceSettingsConfigProviderService {
-   oneway void getDeviceSettingsConfig(in DeviceInfo device, in IGetDeviceSettingsConfigCallback callback);
+   void getDeviceSettingsConfig(in DeviceInfo device, in IGetDeviceSettingsConfigCallback callback);
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index 4af0504..a33fcc6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -23,6 +23,7 @@
 import android.content.ServiceConnection
 import android.os.IBinder
 import android.os.IInterface
+import android.os.RemoteException
 import android.text.TextUtils
 import android.util.Log
 import com.android.settingslib.bluetooth.BluetoothUtils
@@ -55,6 +56,7 @@
 import kotlinx.coroutines.flow.emitAll
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.flatMapConcat
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
@@ -100,6 +102,9 @@
     private var isServiceEnabled =
         coroutineScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
             val states = getSettingsProviderServices()?.values ?: return@async false
+            if (states.isEmpty()) {
+                return@async true
+            }
             combine(states) { it.toList() }
                 .mapNotNull { allStatus ->
                     if (allStatus.any { it is ServiceConnectionStatus.Failed }) {
@@ -114,7 +119,7 @@
                         null
                     }
                 }
-                .first()
+                .firstOrNull() ?: false
         }
 
     private var config =
@@ -131,9 +136,15 @@
                         is ServiceConnectionStatus.Connected ->
                             flowOf(
                                 getDeviceSettingsConfigFromService(
-                                    deviceInfo { setBluetoothAddress(cachedDevice.address) },
-                                    it.service,
-                                )
+                                        deviceInfo { setBluetoothAddress(cachedDevice.address) },
+                                        it.service,
+                                    )
+                                    .also { config ->
+                                        Log.i(
+                                            TAG,
+                                            "device setting config for $cachedDevice is $config",
+                                        )
+                                    }
                             )
                         ServiceConnectionStatus.Connecting -> flowOf()
                         ServiceConnectionStatus.Failed -> flowOf(null)
@@ -146,21 +157,26 @@
         deviceInfo: DeviceInfo,
         service: IDeviceSettingsConfigProviderService,
     ): DeviceSettingsConfig? = suspendCancellableCoroutine { continuation ->
-        service.getDeviceSettingsConfig(
-            deviceInfo,
-            object : IGetDeviceSettingsConfigCallback.Stub() {
-                override fun onResult(
-                    status: DeviceSettingsConfigServiceStatus,
-                    config: DeviceSettingsConfig?,
-                ) {
-                    if (!status.success) {
-                        continuation.resume(null)
-                    } else {
-                        continuation.resume(config)
+        try {
+            service.getDeviceSettingsConfig(
+                deviceInfo,
+                object : IGetDeviceSettingsConfigCallback.Stub() {
+                    override fun onResult(
+                        status: DeviceSettingsConfigServiceStatus,
+                        config: DeviceSettingsConfig?,
+                    ) {
+                        if (!status.success) {
+                            continuation.resume(null)
+                        } else {
+                            continuation.resume(config)
+                        }
                     }
-                }
-            },
-        )
+                },
+            )
+        } catch (e: RemoteException) {
+            Log.i(TAG, "Fail to get config")
+            continuation.resume(null)
+        }
     }
 
     private val settingIdToItemMapping =
@@ -298,13 +314,16 @@
                 val serviceConnection =
                     object : ServiceConnection {
                         override fun onServiceConnected(name: ComponentName, service: IBinder) {
+                            Log.i(TAG, "Service connected for $intent")
                             launch { send(ServiceConnectionStatus.Connected(transform(service))) }
                         }
 
                         override fun onServiceDisconnected(name: ComponentName?) {
+                            Log.i(TAG, "Service disconnected for $intent")
                             launch { send(ServiceConnectionStatus.Connecting) }
                         }
                     }
+                Log.i(TAG, "Try to bind service for $intent")
                 if (
                     !context.bindService(
                         intent,
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index d4d2b48..d91c6bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -30,11 +30,12 @@
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.settingslib.R;
 
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.function.Predicate;
 
 /**
@@ -82,38 +83,55 @@
     private final DisplayManager mDisplayManager;
 
     /**
-     * The text description of the density values of the default display.
+     * The text description of the density values.
      */
-    private String[] mDefaultDisplayDensityEntries;
+    @Nullable
+    private final String[] mEntries;
 
     /**
-     * The density values of the default display.
+     * The density values.
      */
-    private int[] mDefaultDisplayDensityValues;
+    @Nullable
+    private final int[] mValues;
 
-    /**
-     * The density values, indexed by display unique ID.
-     */
-    private final Map<String, int[]> mValuesPerDisplay = new HashMap();
+    private final int mDefaultDensity;
+    private final int mCurrentIndex;
 
-    private int mDefaultDensityForDefaultDisplay;
-    private int mCurrentIndex = -1;
-
-    public DisplayDensityUtils(Context context) {
+    public DisplayDensityUtils(@NonNull Context context) {
         this(context, INTERNAL_ONLY);
     }
 
     /**
-     * Creates an instance that stores the density values for the displays that satisfy
-     * the predicate.
+     * Creates an instance that stores the density values for the smallest display that satisfies
+     * the predicate. It is enough to store the values for one display because the same density
+     * should be set to all the displays that satisfy the predicate.
      * @param context The context
      * @param predicate Determines what displays the density should be set for. The default display
      *                  must satisfy this predicate.
      */
-    public DisplayDensityUtils(Context context, Predicate predicate) {
+    public DisplayDensityUtils(@NonNull Context context,
+            @NonNull Predicate<DisplayInfo> predicate) {
         mPredicate = predicate;
         mDisplayManager = context.getSystemService(DisplayManager.class);
 
+        Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        DisplayInfo defaultDisplayInfo = new DisplayInfo();
+        if (!defaultDisplay.getDisplayInfo(defaultDisplayInfo)) {
+            Log.w(LOG_TAG, "Cannot fetch display info for the default display");
+            mEntries = null;
+            mValues = null;
+            mDefaultDensity = 0;
+            mCurrentIndex = -1;
+            return;
+        }
+        if (!mPredicate.test(defaultDisplayInfo)) {
+            throw new IllegalArgumentException(
+                    "Predicate must not filter out the default display.");
+        }
+
+        int idOfSmallestDisplay = Display.DEFAULT_DISPLAY;
+        int minDimensionPx = Math.min(defaultDisplayInfo.logicalWidth,
+                defaultDisplayInfo.logicalHeight);
         for (Display display : mDisplayManager.getDisplays(
                 DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
             DisplayInfo info = new DisplayInfo();
@@ -122,121 +140,123 @@
                 continue;
             }
             if (!mPredicate.test(info)) {
-                if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                    throw new IllegalArgumentException("Predicate must not filter out the default "
-                            + "display.");
-                }
                 continue;
             }
-
-            final int defaultDensity = DisplayDensityUtils.getDefaultDensityForDisplay(
-                    display.getDisplayId());
-            if (defaultDensity <= 0) {
-                Log.w(LOG_TAG, "Cannot fetch default density for display "
-                        + display.getDisplayId());
-                continue;
+            int minDimension = Math.min(info.logicalWidth, info.logicalHeight);
+            if (minDimension < minDimensionPx) {
+                minDimensionPx = minDimension;
+                idOfSmallestDisplay = display.getDisplayId();
             }
-
-            final Resources res = context.getResources();
-
-            final int currentDensity = info.logicalDensityDpi;
-            int currentDensityIndex = -1;
-
-            // Compute number of "larger" and "smaller" scales for this display.
-            final int minDimensionPx = Math.min(info.logicalWidth, info.logicalHeight);
-            final int maxDensity =
-                    DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
-            final float maxScaleDimen = context.getResources().getFraction(
-                    R.fraction.display_density_max_scale, 1, 1);
-            final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity);
-            final float minScale = context.getResources().getFraction(
-                    R.fraction.display_density_min_scale, 1, 1);
-            final float minScaleInterval = context.getResources().getFraction(
-                    R.fraction.display_density_min_scale_interval, 1, 1);
-            final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval,
-                    0, SUMMARIES_LARGER.length);
-            final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval,
-                    0, SUMMARIES_SMALLER.length);
-
-            String[] entries = new String[1 + numSmaller + numLarger];
-            int[] values = new int[entries.length];
-            int curIndex = 0;
-
-            if (numSmaller > 0) {
-                final float interval = (1 - minScale) / numSmaller;
-                for (int i = numSmaller - 1; i >= 0; i--) {
-                    // Round down to a multiple of 2 by truncating the low bit.
-                    final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
-                    if (currentDensity == density) {
-                        currentDensityIndex = curIndex;
-                    }
-                    entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
-                    values[curIndex] = density;
-                    curIndex++;
-                }
-            }
-
-            if (currentDensity == defaultDensity) {
-                currentDensityIndex = curIndex;
-            }
-            values[curIndex] = defaultDensity;
-            entries[curIndex] = res.getString(SUMMARY_DEFAULT);
-            curIndex++;
-
-            if (numLarger > 0) {
-                final float interval = (maxScale - 1) / numLarger;
-                for (int i = 0; i < numLarger; i++) {
-                    // Round down to a multiple of 2 by truncating the low bit.
-                    final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
-                    if (currentDensity == density) {
-                        currentDensityIndex = curIndex;
-                    }
-                    values[curIndex] = density;
-                    entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
-                    curIndex++;
-                }
-            }
-
-            final int displayIndex;
-            if (currentDensityIndex >= 0) {
-                displayIndex = currentDensityIndex;
-            } else {
-                // We don't understand the current density. Must have been set by
-                // someone else. Make room for another entry...
-                int newLength = values.length + 1;
-                values = Arrays.copyOf(values, newLength);
-                values[curIndex] = currentDensity;
-
-                entries = Arrays.copyOf(entries, newLength);
-                entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
-
-                displayIndex = curIndex;
-            }
-
-            if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                mDefaultDensityForDefaultDisplay = defaultDensity;
-                mCurrentIndex = displayIndex;
-                mDefaultDisplayDensityEntries = entries;
-                mDefaultDisplayDensityValues = values;
-            }
-            mValuesPerDisplay.put(info.uniqueId, values);
         }
+
+        final int defaultDensity =
+                DisplayDensityUtils.getDefaultDensityForDisplay(idOfSmallestDisplay);
+        if (defaultDensity <= 0) {
+            Log.w(LOG_TAG, "Cannot fetch default density for display " + idOfSmallestDisplay);
+            mEntries = null;
+            mValues = null;
+            mDefaultDensity = 0;
+            mCurrentIndex = -1;
+            return;
+        }
+
+        final Resources res = context.getResources();
+
+        final int currentDensity = defaultDisplayInfo.logicalDensityDpi;
+        int currentDensityIndex = -1;
+
+        // Compute number of "larger" and "smaller" scales for this display.
+        final int maxDensity =
+                DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
+        final float maxScaleDimen = context.getResources().getFraction(
+                R.fraction.display_density_max_scale, 1, 1);
+        final float maxScale = Math.min(maxScaleDimen, maxDensity / (float) defaultDensity);
+        final float minScale = context.getResources().getFraction(
+                R.fraction.display_density_min_scale, 1, 1);
+        final float minScaleInterval = context.getResources().getFraction(
+                R.fraction.display_density_min_scale_interval, 1, 1);
+        final int numLarger = (int) MathUtils.constrain((maxScale - 1) / minScaleInterval,
+                0, SUMMARIES_LARGER.length);
+        final int numSmaller = (int) MathUtils.constrain((1 - minScale) / minScaleInterval,
+                0, SUMMARIES_SMALLER.length);
+
+        String[] entries = new String[1 + numSmaller + numLarger];
+        int[] values = new int[entries.length];
+        int curIndex = 0;
+
+        if (numSmaller > 0) {
+            final float interval = (1 - minScale) / numSmaller;
+            for (int i = numSmaller - 1; i >= 0; i--) {
+                // Round down to a multiple of 2 by truncating the low bit.
+                final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
+                if (currentDensity == density) {
+                    currentDensityIndex = curIndex;
+                }
+                entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
+                values[curIndex] = density;
+                curIndex++;
+            }
+        }
+
+        if (currentDensity == defaultDensity) {
+            currentDensityIndex = curIndex;
+        }
+        values[curIndex] = defaultDensity;
+        entries[curIndex] = res.getString(SUMMARY_DEFAULT);
+        curIndex++;
+
+        if (numLarger > 0) {
+            final float interval = (maxScale - 1) / numLarger;
+            for (int i = 0; i < numLarger; i++) {
+                // Round down to a multiple of 2 by truncating the low bit.
+                final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
+                if (currentDensity == density) {
+                    currentDensityIndex = curIndex;
+                }
+                values[curIndex] = density;
+                entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
+                curIndex++;
+            }
+        }
+
+        final int displayIndex;
+        if (currentDensityIndex >= 0) {
+            displayIndex = currentDensityIndex;
+        } else {
+            // We don't understand the current density. Must have been set by
+            // someone else. Make room for another entry...
+            int newLength = values.length + 1;
+            values = Arrays.copyOf(values, newLength);
+            values[curIndex] = currentDensity;
+
+            entries = Arrays.copyOf(entries, newLength);
+            entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
+
+            displayIndex = curIndex;
+        }
+
+        mDefaultDensity = defaultDensity;
+        mCurrentIndex = displayIndex;
+        mEntries = entries;
+        mValues = values;
     }
 
-    public String[] getDefaultDisplayDensityEntries() {
-        return mDefaultDisplayDensityEntries;
+    @Nullable
+    public String[] getEntries() {
+        return mEntries;
     }
 
-    public int[] getDefaultDisplayDensityValues() {
-        return mDefaultDisplayDensityValues;
+    @Nullable
+    public int[] getValues() {
+        return mValues;
     }
 
-    public int getCurrentIndexForDefaultDisplay() {
+    public int getCurrentIndex() {
         return mCurrentIndex;
     }
 
-    public int getDefaultDensityForDefaultDisplay() {
-        return mDefaultDensityForDefaultDisplay;
+    public int getDefaultDensity() {
+        return mDefaultDensity;
     }
 
     /**
@@ -311,15 +331,9 @@
                     if (!mPredicate.test(info)) {
                         continue;
                     }
-                    if (!mValuesPerDisplay.containsKey(info.uniqueId)) {
-                        Log.w(LOG_TAG, "Unable to save forced display density setting "
-                                + "for display " + info.uniqueId);
-                        continue;
-                    }
 
                     final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
-                    wm.setForcedDisplayDensityForUser(displayId,
-                            mValuesPerDisplay.get(info.uniqueId)[index], userId);
+                    wm.setForcedDisplayDensityForUser(displayId, mValues[index], userId);
                 }
             } catch (RemoteException exc) {
                 Log.w(LOG_TAG, "Unable to save forced display density setting");
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
index 1d17b00..83ee975 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
@@ -49,19 +49,23 @@
 
     private final boolean mIsVolumeFixed;
 
+    private final String mProductName;
+
     private InputMediaDevice(
             @NonNull Context context,
             @NonNull String id,
             @AudioDeviceType int audioDeviceInfoType,
             int maxVolume,
             int currentVolume,
-            boolean isVolumeFixed) {
+            boolean isVolumeFixed,
+            @Nullable String productName) {
         super(context, /* info= */ null, /* item= */ null);
         mId = id;
         mAudioDeviceInfoType = audioDeviceInfoType;
         mMaxVolume = maxVolume;
         mCurrentVolume = currentVolume;
         mIsVolumeFixed = isVolumeFixed;
+        mProductName = productName;
         initDeviceRecord();
     }
 
@@ -72,13 +76,20 @@
             @AudioDeviceType int audioDeviceInfoType,
             int maxVolume,
             int currentVolume,
-            boolean isVolumeFixed) {
+            boolean isVolumeFixed,
+            @Nullable String productName) {
         if (!isSupportedInputDevice(audioDeviceInfoType)) {
             return null;
         }
 
         return new InputMediaDevice(
-                context, id, audioDeviceInfoType, maxVolume, currentVolume, isVolumeFixed);
+                context,
+                id,
+                audioDeviceInfoType,
+                maxVolume,
+                currentVolume,
+                isVolumeFixed,
+                productName);
     }
 
     public @AudioDeviceType int getAudioDeviceInfoType() {
@@ -98,18 +109,27 @@
         };
     }
 
+    @Nullable
+    public String getProductName() {
+        return mProductName;
+    }
+
     @Override
     public @NonNull String getName() {
-        CharSequence name = switch (mAudioDeviceInfoType) {
-            case TYPE_WIRED_HEADSET -> mContext.getString(
-                    R.string.media_transfer_wired_device_mic_name);
-            case TYPE_USB_DEVICE, TYPE_USB_HEADSET, TYPE_USB_ACCESSORY -> mContext.getString(
-                    R.string.media_transfer_usb_device_mic_name);
-            case TYPE_BLUETOOTH_SCO -> mContext.getString(
-                    R.string.media_transfer_bt_device_mic_name);
+        return switch (mAudioDeviceInfoType) {
+            case TYPE_WIRED_HEADSET ->
+                    mContext.getString(R.string.media_transfer_wired_device_mic_name);
+            case TYPE_USB_DEVICE, TYPE_USB_HEADSET, TYPE_USB_ACCESSORY ->
+                    // The product name is assumed to be a well-formed string if it's not null.
+                    mProductName != null
+                            ? mProductName
+                            : mContext.getString(R.string.media_transfer_usb_device_mic_name);
+            case TYPE_BLUETOOTH_SCO ->
+                    mProductName != null
+                            ? mProductName
+                            : mContext.getString(R.string.media_transfer_bt_device_mic_name);
             default -> mContext.getString(R.string.media_transfer_this_device_name_desktop);
         };
-        return name.toString();
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index 9164b64..4f315a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -22,6 +22,7 @@
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
+import android.media.AudioDeviceInfo.AudioDeviceType;
 import android.media.AudioManager;
 import android.media.MediaRecorder;
 import android.os.Handler;
@@ -63,7 +64,7 @@
 
     @VisibleForTesting final List<MediaDevice> mInputMediaDevices = new CopyOnWriteArrayList<>();
 
-    private MediaDevice mSelectedInputDevice;
+    private @AudioDeviceType int mSelectedInputDeviceType;
 
     private final Collection<InputDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
     private final Object mCallbackLock = new Object();
@@ -73,12 +74,12 @@
             new AudioDeviceCallback() {
                 @Override
                 public void onAudioDevicesAdded(@NonNull AudioDeviceInfo[] addedDevices) {
-                    dispatchInputDeviceListUpdate();
+                    applyDefaultSelectedTypeToAllPresets();
                 }
 
                 @Override
                 public void onAudioDevicesRemoved(@NonNull AudioDeviceInfo[] removedDevices) {
-                    dispatchInputDeviceListUpdate();
+                    applyDefaultSelectedTypeToAllPresets();
                 }
             };
 
@@ -92,9 +93,12 @@
         mAudioManager.addOnPreferredDevicesForCapturePresetChangedListener(
                 new HandlerExecutor(handler),
                 this::onPreferredDevicesForCapturePresetChangedListener);
+
+        applyDefaultSelectedTypeToAllPresets();
     }
 
-    private void onPreferredDevicesForCapturePresetChangedListener(
+    @VisibleForTesting
+    void onPreferredDevicesForCapturePresetChangedListener(
             @MediaRecorder.SystemSource int capturePreset,
             @NonNull List<AudioDeviceAttributes> devices) {
         if (capturePreset == MediaRecorder.AudioSource.MIC) {
@@ -117,12 +121,30 @@
         }
     }
 
+    // TODO(b/355684672): handle edge case where there are two devices with the same type. Only
+    // using a single mSelectedInputDeviceType might not be enough to recognize the correct device.
     public @Nullable MediaDevice getSelectedInputDevice() {
-        return mSelectedInputDevice;
+        for (MediaDevice device : mInputMediaDevices) {
+            if (((InputMediaDevice) device).getAudioDeviceInfoType() == mSelectedInputDeviceType) {
+                return device;
+            }
+        }
+        return null;
     }
 
-    private void dispatchInputDeviceListUpdate() {
-        // Get selected input device.
+    private void applyDefaultSelectedTypeToAllPresets() {
+        mSelectedInputDeviceType = retrieveDefaultSelectedDeviceType();
+        AudioDeviceAttributes deviceAttributes =
+                createInputDeviceAttributes(mSelectedInputDeviceType);
+        setPreferredDeviceForAllPresets(deviceAttributes);
+    }
+
+    private AudioDeviceAttributes createInputDeviceAttributes(@AudioDeviceType int type) {
+        // Address is not used.
+        return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_INPUT, type, /* address= */ "");
+    }
+
+    private @AudioDeviceType int retrieveDefaultSelectedDeviceType() {
         List<AudioDeviceAttributes> attributesOfSelectedInputDevices =
                 mAudioManager.getDevicesForAttributes(INPUT_ATTRIBUTES);
         int selectedInputDeviceAttributesType;
@@ -138,7 +160,10 @@
             }
             selectedInputDeviceAttributesType = attributesOfSelectedInputDevices.get(0).getType();
         }
+        return selectedInputDeviceAttributesType;
+    }
 
+    private void dispatchInputDeviceListUpdate() {
         // Get all input devices.
         AudioDeviceInfo[] audioDeviceInfos =
                 mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
@@ -151,11 +176,11 @@
                             info.getType(),
                             getMaxInputGain(),
                             getCurrentInputGain(),
-                            isInputGainFixed());
+                            isInputGainFixed(),
+                            getProductNameFromAudioDeviceInfo(info));
             if (mediaDevice != null) {
-                if (info.getType() == selectedInputDeviceAttributesType) {
+                if (info.getType() == mSelectedInputDeviceType) {
                     mediaDevice.setState(STATE_SELECTED);
-                    mSelectedInputDevice = mediaDevice;
                 }
                 mInputMediaDevices.add(mediaDevice);
             }
@@ -169,13 +194,32 @@
         }
     }
 
+    /**
+     * Gets the product name for the given {@link AudioDeviceInfo}.
+     *
+     * @return The product name for the given {@link AudioDeviceInfo}, or null if a suitable name
+     *     cannot be found.
+     */
+    @Nullable
+    private String getProductNameFromAudioDeviceInfo(AudioDeviceInfo deviceInfo) {
+        CharSequence productName = deviceInfo.getProductName();
+        if (productName == null) {
+            return null;
+        }
+        String productNameString = productName.toString();
+        if (productNameString.isBlank()) {
+            return null;
+        }
+        return productNameString;
+    }
+
     public void selectDevice(@NonNull MediaDevice device) {
-        if (!(device instanceof InputMediaDevice)) {
+        if (!(device instanceof InputMediaDevice inputMediaDevice)) {
             Slog.w(TAG, "This device is not an InputMediaDevice: " + device.getName());
             return;
         }
 
-        if (device.equals(mSelectedInputDevice)) {
+        if (inputMediaDevice.getAudioDeviceInfoType() == mSelectedInputDeviceType) {
             Slog.w(TAG, "This device is already selected: " + device.getName());
             return;
         }
@@ -186,12 +230,11 @@
             return;
         }
 
-        // TODO(b/355684672): apply address for BT devices.
+        // Update mSelectedInputDeviceType directly based on user action.
+        mSelectedInputDeviceType = inputMediaDevice.getAudioDeviceInfoType();
+
         AudioDeviceAttributes deviceAttributes =
-                new AudioDeviceAttributes(
-                        AudioDeviceAttributes.ROLE_INPUT,
-                        ((InputMediaDevice) device).getAudioDeviceInfoType(),
-                        /* address= */ "");
+                createInputDeviceAttributes(inputMediaDevice.getAudioDeviceInfoType());
         try {
             setPreferredDeviceForAllPresets(deviceAttributes);
         } catch (IllegalArgumentException e) {
@@ -212,13 +255,14 @@
 
     public int getMaxInputGain() {
         // TODO (b/357123335): use real input gain implementation.
-        // Using 15 for now since it matches the max index for output.
-        return 15;
+        // Using 100 for now since it matches the maximum input gain index in classic ChromeOS.
+        return 100;
     }
 
     public int getCurrentInputGain() {
         // TODO (b/357123335): use real input gain implementation.
-        return 8;
+        // Show a fixed full gain in UI before it really works per UX requirement.
+        return 100;
     }
 
     public boolean isInputGainFixed() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index feaf7fb..b94e906 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -15,6 +15,7 @@
  */
 package com.android.settingslib.media;
 
+import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_DOCK;
 import static android.media.MediaRoute2Info.TYPE_HDMI;
@@ -29,8 +30,6 @@
 import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
 
 import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
@@ -43,6 +42,8 @@
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.settingslib.R;
 import com.android.settingslib.media.flags.Flags;
@@ -72,7 +73,7 @@
             return context.getString(R.string.media_transfer_this_device_name_tv);
         } else if (isTablet()) {
             return context.getString(R.string.media_transfer_this_device_name_tablet);
-        } else if (inputRoutingEnabledAndIsDesktop()) {
+        } else if (inputRoutingEnabledAndIsDesktop(context)) {
             return context.getString(R.string.media_transfer_this_device_name_desktop);
         } else {
             return context.getString(R.string.media_transfer_this_device_name);
@@ -88,7 +89,7 @@
             case TYPE_WIRED_HEADSET:
             case TYPE_WIRED_HEADPHONES:
                 name =
-                        inputRoutingEnabledAndIsDesktop()
+                        inputRoutingEnabledAndIsDesktop(context)
                                 ? context.getString(R.string.media_transfer_headphone_name)
                                 : context.getString(R.string.media_transfer_wired_headphone_name);
                 break;
@@ -96,7 +97,7 @@
             case TYPE_USB_HEADSET:
             case TYPE_USB_ACCESSORY:
                 name =
-                        inputRoutingEnabledAndIsDesktop()
+                        inputRoutingEnabledAndIsDesktop(context)
                                 ? context.getString(R.string.media_transfer_usb_audio_name)
                                 : context.getString(R.string.media_transfer_wired_headphone_name);
                 break;
@@ -149,14 +150,13 @@
                 .contains("tablet");
     }
 
-    static boolean isDesktop() {
-        return Arrays.asList(SystemProperties.get("ro.build.characteristics").split(","))
-                .contains("desktop");
+    public static boolean isDesktop(@NonNull Context context) {
+        return context.getPackageManager().hasSystemFeature(FEATURE_PC);
     }
 
-    static boolean inputRoutingEnabledAndIsDesktop() {
+    public static boolean inputRoutingEnabledAndIsDesktop(@NonNull Context context) {
         return com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl()
-                && isDesktop();
+                && isDesktop(context);
     }
 
     // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index 43d7946..23be7ba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -78,6 +78,10 @@
         mutableModesFlow.value = (mutableModesFlow.value.filter { it.id != modeId }) + mode
     }
 
+    fun clearModes() {
+        mutableModesFlow.value = listOf()
+    }
+
     fun getMode(id: String): ZenMode? {
         return mutableModesFlow.value.find { it.id == id }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
index 2f8105a..b41e970 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
@@ -74,6 +74,8 @@
     /** The headset groupId to volume map during audio sharing. */
     val volumeMap: StateFlow<GroupIdToVolumes>
 
+    suspend fun audioSharingAvailable(): Boolean
+
     /** Set the volume of secondary headset during audio sharing. */
     suspend fun setSecondaryVolume(
         @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
@@ -216,6 +218,12 @@
         }
             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyMap())
 
+    override suspend fun audioSharingAvailable(): Boolean {
+        return withContext(backgroundCoroutineContext) {
+            BluetoothUtils.isAudioSharingEnabled()
+        }
+    }
+
     override suspend fun setSecondaryVolume(
         @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
         volume: Int
@@ -262,6 +270,8 @@
         MutableStateFlow(BluetoothCsipSetCoordinator.GROUP_ID_INVALID)
     override val volumeMap: StateFlow<GroupIdToVolumes> = MutableStateFlow(emptyMap())
 
+    override suspend fun audioSharingAvailable(): Boolean = false
+
     override suspend fun setSecondaryVolume(
         @IntRange(from = AUDIO_SHARING_VOLUME_MIN.toLong(), to = AUDIO_SHARING_VOLUME_MAX.toLong())
         volume: Int
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 52c2a87..9f9aaf5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -16,13 +16,22 @@
 
 package com.android.settingslib.devicestate;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 
@@ -42,7 +51,10 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -53,6 +65,8 @@
     @Mock private Context mMockContext;
     @Mock private Resources mMockResources;
 
+    @Mock private DeviceStateManager mDeviceStateManager;
+
     private DeviceStateRotationLockSettingsManager mManager;
     private int mNumSettingsChanges = 0;
     private final ContentObserver mContentObserver = new ContentObserver(null) {
@@ -70,6 +84,9 @@
         when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockContext.getContentResolver()).thenReturn(context.getContentResolver());
+        when(mMockContext.getSystemService(DeviceStateManager.class)).thenReturn(
+                mDeviceStateManager);
+        when(mDeviceStateManager.getSupportedDeviceStates()).thenReturn(createDeviceStateList());
         when(mMockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
                 .thenReturn(new String[]{"0:1", "1:0:2", "2:2"});
         when(mMockResources.getIntArray(R.array.config_foldedDeviceStates))
@@ -180,4 +197,29 @@
                 value,
                 UserHandle.USER_CURRENT);
     }
+
+    private List<DeviceState> createDeviceStateList() {
+        List<DeviceState> deviceStates = new ArrayList<>();
+        deviceStates.add(createDeviceState(0 /* identifier */, "folded",
+                new HashSet<>(List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)),
+                new HashSet<>(List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED))));
+        deviceStates.add(createDeviceState(1 /* identifier */, "half_folded",
+                new HashSet<>(List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)),
+                new HashSet<>(
+                        List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN))));
+        deviceStates.add(createDeviceState(2, "unfolded",
+                new HashSet<>(List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)),
+                new HashSet<>(List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))));
+
+        return deviceStates;
+    }
+
+    private DeviceState createDeviceState(int identifier, @NonNull String name,
+            @NonNull Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties,
+            @NonNull Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties) {
+        DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder(
+                identifier, name).setPhysicalProperties(systemProperties).setPhysicalProperties(
+                physicalProperties).build();
+        return new DeviceState(deviceStateConfiguration);
+    }
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt
index d91c2fa..7a905cb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt
@@ -18,6 +18,16 @@
 
 import android.content.Context
 import android.content.res.Resources
+import android.hardware.devicestate.DeviceState
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN
+import android.hardware.devicestate.DeviceStateManager
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
@@ -32,14 +42,40 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags
+import org.mockito.Mockito.`when` as whenever
 
 private const val DEVICE_STATE_UNKNOWN = 0
-private const val DEVICE_STATE_CLOSED = 1
-private const val DEVICE_STATE_HALF_FOLDED = 2
-private const val DEVICE_STATE_OPEN = 3
-private const val DEVICE_STATE_REAR_DISPLAY = 4
+private val DEVICE_STATE_CLOSED = DeviceState(
+    DeviceState.Configuration.Builder(/* identifier= */ 1, "CLOSED")
+        .setSystemProperties(setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY))
+        .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED))
+        .build()
+)
+private val DEVICE_STATE_HALF_FOLDED = DeviceState(
+    DeviceState.Configuration.Builder(/* identifier= */ 2, "HALF_FOLDED")
+        .setSystemProperties(setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY))
+        .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN))
+        .build()
+)
+private val DEVICE_STATE_OPEN = DeviceState(
+    DeviceState.Configuration.Builder(/* identifier= */ 3, "OPEN")
+        .setSystemProperties(setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY))
+        .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))
+        .build()
+)
+private val DEVICE_STATE_REAR_DISPLAY = DeviceState(
+    DeviceState.Configuration.Builder(/* identifier= */ 4, "REAR_DISPLAY")
+        .setSystemProperties(
+            setOf(
+                PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                PROPERTY_FEATURE_REAR_DISPLAY
+            )
+        )
+        .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED))
+        .build()
+)
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -51,6 +87,8 @@
 
     @Mock private lateinit var resources: Resources
 
+    @Mock private lateinit var deviceStateManager: DeviceStateManager
+
     private lateinit var posturesHelper: PosturesHelper
 
     @Before
@@ -59,30 +97,39 @@
 
         whenever(context.resources).thenReturn(resources)
         whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
-            .thenReturn(intArrayOf(DEVICE_STATE_CLOSED))
+            .thenReturn(intArrayOf(DEVICE_STATE_CLOSED.identifier))
         whenever(resources.getIntArray(R.array.config_halfFoldedDeviceStates))
-            .thenReturn(intArrayOf(DEVICE_STATE_HALF_FOLDED))
+            .thenReturn(intArrayOf(DEVICE_STATE_HALF_FOLDED.identifier))
         whenever(resources.getIntArray(R.array.config_openDeviceStates))
-            .thenReturn(intArrayOf(DEVICE_STATE_OPEN))
+            .thenReturn(intArrayOf(DEVICE_STATE_OPEN.identifier))
         whenever(resources.getIntArray(R.array.config_rearDisplayDeviceStates))
-            .thenReturn(intArrayOf(DEVICE_STATE_REAR_DISPLAY))
+            .thenReturn(intArrayOf(DEVICE_STATE_REAR_DISPLAY.identifier))
+        whenever(deviceStateManager.supportedDeviceStates).thenReturn(
+            listOf(
+                DEVICE_STATE_CLOSED,
+                DEVICE_STATE_HALF_FOLDED,
+                DEVICE_STATE_OPEN,
+                DEVICE_STATE_REAR_DISPLAY
+            )
+        )
 
-        posturesHelper = PosturesHelper(context)
+        posturesHelper = PosturesHelper(context, deviceStateManager)
     }
 
     @Test
-    fun deviceStateToPosture_mapsCorrectly() {
+    @RequiresFlagsDisabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun deviceStateToPosture_mapsCorrectly_overlayConfigurationValues() {
         expect
-            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED))
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED.identifier))
             .isEqualTo(DEVICE_STATE_ROTATION_KEY_FOLDED)
         expect
-            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED.identifier))
             .isEqualTo(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
         expect
-            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN))
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN.identifier))
             .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
         expect
-            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY.identifier))
             .isEqualTo(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
         expect
             .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_UNKNOWN))
@@ -90,19 +137,58 @@
     }
 
     @Test
-    fun postureToDeviceState_mapsCorrectly() {
+    @RequiresFlagsEnabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun deviceStateToPosture_mapsCorrectly_deviceStateManager() {
+        expect
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED.identifier))
+            .isEqualTo(DEVICE_STATE_ROTATION_KEY_FOLDED)
+        expect
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED.identifier))
+            .isEqualTo(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+        expect
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN.identifier))
+            .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+        expect
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY.identifier))
+            .isEqualTo(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+        expect
+            .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_UNKNOWN))
+            .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNKNOWN)
+    }
+
+    @Test
+    @RequiresFlagsDisabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun postureToDeviceState_mapsCorrectly_overlayConfigurationValues() {
         expect
             .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED))
-            .isEqualTo(DEVICE_STATE_CLOSED)
+            .isEqualTo(DEVICE_STATE_CLOSED.identifier)
         expect
             .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED))
-            .isEqualTo(DEVICE_STATE_HALF_FOLDED)
+            .isEqualTo(DEVICE_STATE_HALF_FOLDED.identifier)
         expect
             .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED))
-            .isEqualTo(DEVICE_STATE_OPEN)
+            .isEqualTo(DEVICE_STATE_OPEN.identifier)
         expect
             .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY))
-            .isEqualTo(DEVICE_STATE_REAR_DISPLAY)
+            .isEqualTo(DEVICE_STATE_REAR_DISPLAY.identifier)
+        expect.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNKNOWN)).isNull()
+    }
+
+    @Test
+    @RequiresFlagsEnabled(DeviceStateManagerFlags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun postureToDeviceState_mapsCorrectly_deviceStateManager() {
+        expect
+            .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED))
+            .isEqualTo(DEVICE_STATE_CLOSED.identifier)
+        expect
+            .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED))
+            .isEqualTo(DEVICE_STATE_HALF_FOLDED.identifier)
+        expect
+            .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED))
+            .isEqualTo(DEVICE_STATE_OPEN.identifier)
+        expect
+            .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY))
+            .isEqualTo(DEVICE_STATE_REAR_DISPLAY.identifier)
         expect.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNKNOWN)).isNull()
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index b1489be..22b3150 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -36,6 +37,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.TelephonyManager;
 
@@ -96,6 +98,8 @@
     private LocalBluetoothProfileManager mLocalProfileManager;
     @Mock
     private BluetoothUtils.ErrorListener mErrorListener;
+    @Mock
+    private LocalBluetoothLeBroadcast mBroadcast;
 
     private Context mContext;
     private Intent mIntent;
@@ -107,7 +111,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(RuntimeEnvironment.application);
 
         mBluetoothEventManager =
                 new BluetoothEventManager(
@@ -208,28 +212,14 @@
      */
     @Test
     public void dispatchProfileConnectionStateChanged_flagOff_noUpdateFallbackDevice() {
-        ShadowBluetoothAdapter shadowBluetoothAdapter =
-                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
-        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
-        when(broadcast.isProfileReady()).thenReturn(true);
-        LocalBluetoothLeBroadcastAssistant assistant =
-                mock(LocalBluetoothLeBroadcastAssistant.class);
-        when(assistant.isProfileReady()).thenReturn(true);
-        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
-        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
-        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
-        when(mBtManager.getProfileManager()).thenReturn(profileManager);
+        setUpAudioSharing(/* enableFlag= */ false, /* enableFeature= */ true, /* enableProfile= */
+                true, /* workProfile= */ false);
         mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                 mCachedBluetoothDevice,
                 BluetoothProfile.STATE_DISCONNECTED,
                 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
 
-        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
     }
 
     /**
@@ -239,28 +229,14 @@
      */
     @Test
     public void dispatchProfileConnectionStateChanged_notSupport_noUpdateFallbackDevice() {
-        ShadowBluetoothAdapter shadowBluetoothAdapter =
-                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
-        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
-                BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
-        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
-        when(broadcast.isProfileReady()).thenReturn(true);
-        LocalBluetoothLeBroadcastAssistant assistant =
-                mock(LocalBluetoothLeBroadcastAssistant.class);
-        when(assistant.isProfileReady()).thenReturn(true);
-        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
-        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
-        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
-        when(mBtManager.getProfileManager()).thenReturn(profileManager);
+        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ false, /* enableProfile= */
+                true, /* workProfile= */ false);
         mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                 mCachedBluetoothDevice,
                 BluetoothProfile.STATE_DISCONNECTED,
                 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
 
-        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
     }
 
     /**
@@ -270,28 +246,14 @@
      */
     @Test
     public void dispatchProfileConnectionStateChanged_profileNotReady_noUpdateFallbackDevice() {
-        ShadowBluetoothAdapter shadowBluetoothAdapter =
-                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
-        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
-        when(broadcast.isProfileReady()).thenReturn(false);
-        LocalBluetoothLeBroadcastAssistant assistant =
-                mock(LocalBluetoothLeBroadcastAssistant.class);
-        when(assistant.isProfileReady()).thenReturn(true);
-        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
-        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
-        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
-        when(mBtManager.getProfileManager()).thenReturn(profileManager);
+        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
+                false, /* workProfile= */ false);
         mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                 mCachedBluetoothDevice,
                 BluetoothProfile.STATE_DISCONNECTED,
                 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
 
-        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
     }
 
     /**
@@ -301,28 +263,31 @@
      */
     @Test
     public void dispatchProfileConnectionStateChanged_notAssistantProfile_noUpdateFallbackDevice() {
-        ShadowBluetoothAdapter shadowBluetoothAdapter =
-                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
-        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
-        when(broadcast.isProfileReady()).thenReturn(true);
-        LocalBluetoothLeBroadcastAssistant assistant =
-                mock(LocalBluetoothLeBroadcastAssistant.class);
-        when(assistant.isProfileReady()).thenReturn(true);
-        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
-        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
-        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
-        when(mBtManager.getProfileManager()).thenReturn(profileManager);
+        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
+                true, /* workProfile= */ false);
         mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                 mCachedBluetoothDevice,
                 BluetoothProfile.STATE_DISCONNECTED,
                 BluetoothProfile.LE_AUDIO);
 
-        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
+    }
+
+    /**
+     * dispatchProfileConnectionStateChanged should not call {@link
+     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when triggered for
+     * work profile.
+     */
+    @Test
+    public void dispatchProfileConnectionStateChanged_workProfile_noUpdateFallbackDevice() {
+        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
+                true, /* workProfile= */ true);
+        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
+                mCachedBluetoothDevice,
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+
+        verify(mBroadcast).updateFallbackActiveDeviceIfNeeded();
     }
 
     /**
@@ -332,28 +297,40 @@
      */
     @Test
     public void dispatchProfileConnectionStateChanged_audioSharing_updateFallbackDevice() {
-        ShadowBluetoothAdapter shadowBluetoothAdapter =
-                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
-        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
-                BluetoothStatusCodes.FEATURE_SUPPORTED);
-        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
-        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
-        when(broadcast.isProfileReady()).thenReturn(true);
-        LocalBluetoothLeBroadcastAssistant assistant =
-                mock(LocalBluetoothLeBroadcastAssistant.class);
-        when(assistant.isProfileReady()).thenReturn(true);
-        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
-        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
-        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
-        when(mBtManager.getProfileManager()).thenReturn(profileManager);
+        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
+                true, /* workProfile= */ false);
         mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                 mCachedBluetoothDevice,
                 BluetoothProfile.STATE_DISCONNECTED,
                 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
 
-        verify(broadcast).updateFallbackActiveDeviceIfNeeded();
+        verify(mBroadcast).updateFallbackActiveDeviceIfNeeded();
+    }
+
+    private void setUpAudioSharing(boolean enableFlag, boolean enableFeature,
+            boolean enableProfile, boolean workProfile) {
+        if (enableFlag) {
+            mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        } else {
+            mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        }
+        ShadowBluetoothAdapter shadowBluetoothAdapter =
+                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        int code = enableFeature ? BluetoothStatusCodes.FEATURE_SUPPORTED
+                : BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
+        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(code);
+        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(code);
+        when(mBroadcast.isProfileReady()).thenReturn(enableProfile);
+        LocalBluetoothLeBroadcastAssistant assistant =
+                mock(LocalBluetoothLeBroadcastAssistant.class);
+        when(assistant.isProfileReady()).thenReturn(enableProfile);
+        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
+        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
+        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
+        when(mBtManager.getProfileManager()).thenReturn(profileManager);
+        UserManager userManager = mock(UserManager.class);
+        when(mContext.getSystemService(UserManager.class)).thenReturn(userManager);
+        when(userManager.isManagedProfile()).thenReturn(workProfile);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
index b180b69..fd14d1f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
@@ -39,6 +39,7 @@
 import android.content.Context;
 import android.os.Looper;
 import android.os.Parcel;
+import android.os.UserManager;
 import android.platform.test.flag.junit.SetFlagsRule;
 
 import com.android.settingslib.flags.Flags;
@@ -104,6 +105,8 @@
     private LocalBluetoothLeBroadcast mBroadcast;
     @Mock
     private LocalBluetoothLeBroadcastAssistant mAssistant;
+    @Mock
+    private UserManager mUserManager;
 
     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
     private CachedBluetoothDevice mCachedDevice1;
@@ -128,7 +131,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(RuntimeEnvironment.application);
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mShadowBluetoothAdapter.setEnabled(true);
         mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
@@ -356,6 +359,37 @@
     }
 
     @Test
+    public void
+            addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_workProfile_doNothing() {
+        // Condition: The preferredDevice is main and there is another main device in top list
+        // Expected Result: return true and there is the preferredDevice in top list
+        CachedBluetoothDevice preferredDevice = mCachedDevice1;
+        mCachedDevice1.getMemberDevice().clear();
+        mCachedDevices.clear();
+        mCachedDevices.add(preferredDevice);
+        mCachedDevices.add(mCachedDevice2);
+        mCachedDevices.add(mCachedDevice3);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
+        when(mBroadcast.isEnabled(null)).thenReturn(true);
+        BluetoothLeBroadcastMetadata metadata = Mockito.mock(BluetoothLeBroadcastMetadata.class);
+        when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata);
+        BluetoothLeBroadcastReceiveState state = Mockito.mock(
+                BluetoothLeBroadcastReceiveState.class);
+        when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
+        when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
+        when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+        when(mUserManager.isManagedProfile()).thenReturn(true);
+
+        assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
+                .isTrue();
+        assertThat(mCachedDevices.contains(preferredDevice)).isTrue();
+        assertThat(mCachedDevices.contains(mCachedDevice2)).isFalse();
+        assertThat(mCachedDevices.contains(mCachedDevice3)).isTrue();
+        assertThat(preferredDevice.getMemberDevice()).contains(mCachedDevice2);
+        verify(mAssistant, never()).addSource(mDevice1, metadata, /* isGroupOp= */ false);
+    }
+
+    @Test
     public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_syncSource() {
         // Condition: The preferredDevice is main and there is another main device in top list
         // Expected Result: return true and there is the preferredDevice in top list
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
index 30e4637..7775b91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
@@ -41,6 +41,10 @@
     private final int MAX_VOLUME = 1;
     private final int CURRENT_VOLUME = 0;
     private final boolean IS_VOLUME_FIXED = true;
+    private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic";
+    private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
+    private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset";
+    private static final String PRODUCT_NAME_BT_HEADSET = "My Bluetooth Headset";
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
@@ -60,7 +64,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_BUILTIN_MIC);
         assertThat(builtinMediaDevice).isNotNull();
         assertThat(builtinMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_microphone);
     }
@@ -74,7 +79,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_BUILTIN_MIC);
         assertThat(builtinMediaDevice).isNotNull();
         assertThat(builtinMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_this_device_name_desktop));
@@ -89,7 +95,8 @@
                         AudioDeviceInfo.TYPE_WIRED_HEADSET,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_WIRED_HEADSET);
         assertThat(wiredMediaDevice).isNotNull();
         assertThat(wiredMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_wired_device_mic_name));
@@ -104,7 +111,23 @@
                         AudioDeviceInfo.TYPE_USB_HEADSET,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_USB_HEADSET);
+        assertThat(usbMediaDevice).isNotNull();
+        assertThat(usbMediaDevice.getName()).isEqualTo(PRODUCT_NAME_USB_HEADSET);
+    }
+
+    @Test
+    public void getName_returnCorrectName_usbHeadset_nullProductName() {
+        InputMediaDevice usbMediaDevice =
+                InputMediaDevice.create(
+                        mContext,
+                        String.valueOf(USB_HEADSET_ID),
+                        AudioDeviceInfo.TYPE_USB_HEADSET,
+                        MAX_VOLUME,
+                        CURRENT_VOLUME,
+                        IS_VOLUME_FIXED,
+                        null);
         assertThat(usbMediaDevice).isNotNull();
         assertThat(usbMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_usb_device_mic_name));
@@ -119,7 +142,23 @@
                         AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        IS_VOLUME_FIXED);
+                        IS_VOLUME_FIXED,
+                        PRODUCT_NAME_BT_HEADSET);
+        assertThat(btMediaDevice).isNotNull();
+        assertThat(btMediaDevice.getName()).isEqualTo(PRODUCT_NAME_BT_HEADSET);
+    }
+
+    @Test
+    public void getName_returnCorrectName_btHeadset_nullProductName() {
+        InputMediaDevice btMediaDevice =
+                InputMediaDevice.create(
+                        mContext,
+                        String.valueOf(BT_HEADSET_ID),
+                        AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+                        MAX_VOLUME,
+                        CURRENT_VOLUME,
+                        IS_VOLUME_FIXED,
+                        null);
         assertThat(btMediaDevice).isNotNull();
         assertThat(btMediaDevice.getName())
                 .isEqualTo(mContext.getString(R.string.media_transfer_bt_device_mic_name));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index 16c0c1c..782cee2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -21,6 +21,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -55,13 +56,95 @@
     private static final int INPUT_USB_DEVICE_ID = 3;
     private static final int INPUT_USB_HEADSET_ID = 4;
     private static final int INPUT_USB_ACCESSORY_ID = 5;
+    private static final int HDMI_ID = 6;
     private static final int MAX_VOLUME = 1;
     private static final int CURRENT_VOLUME = 0;
     private static final boolean VOLUME_FIXED_TRUE = true;
+    private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic";
+    private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
+    private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset";
+    private static final String PRODUCT_NAME_USB_DEVICE = "My USB Device";
+    private static final String PRODUCT_NAME_USB_ACCESSORY = "My USB Accessory";
+    private static final String PRODUCT_NAME_HDMI_DEVICE = "HDMI device";
 
     private final Context mContext = spy(RuntimeEnvironment.application);
     private InputRouteManager mInputRouteManager;
 
+    private AudioDeviceInfo mockBuiltinMicInfo() {
+        final AudioDeviceInfo info = mock(AudioDeviceInfo.class);
+        when(info.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
+        when(info.getId()).thenReturn(BUILTIN_MIC_ID);
+        when(info.getAddress()).thenReturn("");
+        when(info.getProductName()).thenReturn(PRODUCT_NAME_BUILTIN_MIC);
+        return info;
+    }
+
+    private AudioDeviceInfo mockWiredHeadsetInfo() {
+        final AudioDeviceInfo info = mock(AudioDeviceInfo.class);
+        when(info.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
+        when(info.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
+        when(info.getAddress()).thenReturn("");
+        when(info.getProductName()).thenReturn(PRODUCT_NAME_WIRED_HEADSET);
+        return info;
+    }
+
+    private AudioDeviceInfo mockUsbDeviceInfo() {
+        final AudioDeviceInfo info = mock(AudioDeviceInfo.class);
+        when(info.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_DEVICE);
+        when(info.getId()).thenReturn(INPUT_USB_DEVICE_ID);
+        when(info.getAddress()).thenReturn("");
+        when(info.getProductName()).thenReturn(PRODUCT_NAME_USB_DEVICE);
+        return info;
+    }
+
+    private AudioDeviceInfo mockUsbHeadsetInfo() {
+        final AudioDeviceInfo info = mock(AudioDeviceInfo.class);
+        when(info.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_HEADSET);
+        when(info.getId()).thenReturn(INPUT_USB_HEADSET_ID);
+        when(info.getAddress()).thenReturn("");
+        when(info.getProductName()).thenReturn(PRODUCT_NAME_USB_HEADSET);
+        return info;
+    }
+
+    private AudioDeviceInfo mockUsbAccessoryInfo() {
+        final AudioDeviceInfo info = mock(AudioDeviceInfo.class);
+        when(info.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_ACCESSORY);
+        when(info.getId()).thenReturn(INPUT_USB_ACCESSORY_ID);
+        when(info.getAddress()).thenReturn("");
+        when(info.getProductName()).thenReturn(PRODUCT_NAME_USB_ACCESSORY);
+        return info;
+    }
+
+    private AudioDeviceInfo mockHdmiInfo() {
+        final AudioDeviceInfo info = mock(AudioDeviceInfo.class);
+        when(info.getType()).thenReturn(AudioDeviceInfo.TYPE_HDMI);
+        when(info.getId()).thenReturn(HDMI_ID);
+        when(info.getAddress()).thenReturn("");
+        when(info.getProductName()).thenReturn(PRODUCT_NAME_HDMI_DEVICE);
+        return info;
+    }
+
+    private AudioDeviceAttributes getBuiltinMicDeviceAttributes() {
+        return new AudioDeviceAttributes(
+                AudioDeviceAttributes.ROLE_INPUT,
+                AudioDeviceInfo.TYPE_BUILTIN_MIC,
+                /* address= */ "");
+    }
+
+    private AudioDeviceAttributes getWiredHeadsetDeviceAttributes() {
+        return new AudioDeviceAttributes(
+                AudioDeviceAttributes.ROLE_INPUT,
+                AudioDeviceInfo.TYPE_WIRED_HEADSET,
+                /* address= */ "");
+    }
+
+    private void onPreferredDevicesForCapturePresetChanged(InputRouteManager inputRouteManager) {
+        final List<AudioDeviceAttributes> audioDeviceAttributesList =
+                new ArrayList<AudioDeviceAttributes>();
+        inputRouteManager.onPreferredDevicesForCapturePresetChangedListener(
+                MediaRecorder.AudioSource.MIC, audioDeviceAttributesList);
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -72,31 +155,15 @@
 
     @Test
     public void onAudioDevicesAdded_shouldUpdateInputMediaDevice() {
-        final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
-        when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
-        when(info1.getId()).thenReturn(BUILTIN_MIC_ID);
-
-        final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
-        when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
-        when(info2.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
-
-        final AudioDeviceInfo info3 = mock(AudioDeviceInfo.class);
-        when(info3.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_DEVICE);
-        when(info3.getId()).thenReturn(INPUT_USB_DEVICE_ID);
-
-        final AudioDeviceInfo info4 = mock(AudioDeviceInfo.class);
-        when(info4.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_HEADSET);
-        when(info4.getId()).thenReturn(INPUT_USB_HEADSET_ID);
-
-        final AudioDeviceInfo info5 = mock(AudioDeviceInfo.class);
-        when(info5.getType()).thenReturn(AudioDeviceInfo.TYPE_USB_ACCESSORY);
-        when(info5.getId()).thenReturn(INPUT_USB_ACCESSORY_ID);
-
-        final AudioDeviceInfo unsupportedInfo = mock(AudioDeviceInfo.class);
-        when(unsupportedInfo.getType()).thenReturn(AudioDeviceInfo.TYPE_HDMI);
-
         final AudioManager audioManager = mock(AudioManager.class);
-        AudioDeviceInfo[] devices = {info1, info2, info3, info4, info5, unsupportedInfo};
+        AudioDeviceInfo[] devices = {
+            mockBuiltinMicInfo(),
+            mockWiredHeadsetInfo(),
+            mockUsbDeviceInfo(),
+            mockUsbHeadsetInfo(),
+            mockUsbAccessoryInfo(),
+            mockHdmiInfo()
+        };
         when(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(devices);
 
         InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
@@ -104,8 +171,9 @@
         assertThat(inputRouteManager.mInputMediaDevices).isEmpty();
 
         inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+        onPreferredDevicesForCapturePresetChanged(inputRouteManager);
 
-        // The unsupported info should be filtered out.
+        // The unsupported (hdmi) info should be filtered out.
         assertThat(inputRouteManager.mInputMediaDevices).hasSize(devices.length - 1);
         assertThat(inputRouteManager.mInputMediaDevices.get(0).getId())
                 .isEqualTo(String.valueOf(BUILTIN_MIC_ID));
@@ -130,34 +198,25 @@
         final MediaDevice device = mock(MediaDevice.class);
         inputRouteManager.mInputMediaDevices.add(device);
 
-        final AudioDeviceInfo info = mock(AudioDeviceInfo.class);
-        when(info.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
-        inputRouteManager.mAudioDeviceCallback.onAudioDevicesRemoved(new AudioDeviceInfo[] {info});
+        inputRouteManager.mAudioDeviceCallback.onAudioDevicesRemoved(
+                new AudioDeviceInfo[] {mockWiredHeadsetInfo()});
+        onPreferredDevicesForCapturePresetChanged(inputRouteManager);
 
         assertThat(inputRouteManager.mInputMediaDevices).isEmpty();
     }
 
     @Test
     public void getSelectedInputDevice_returnOneFromAudioManager() {
-        final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
-        when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
-        when(info1.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
-
-        final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
-        when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
-        when(info2.getId()).thenReturn(BUILTIN_MIC_ID);
-
         final AudioManager audioManager = mock(AudioManager.class);
-        AudioDeviceInfo[] devices = {info1, info2};
+        AudioDeviceInfo[] devices = {mockWiredHeadsetInfo(), mockBuiltinMicInfo()};
         when(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(devices);
 
         // Mock audioManager.getDevicesForAttributes returns exactly one audioDeviceAttributes.
-        AudioDeviceAttributes audioDeviceAttributes = new AudioDeviceAttributes(info1);
         when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES))
-                .thenReturn(Collections.singletonList(audioDeviceAttributes));
+                .thenReturn(Collections.singletonList(getWiredHeadsetDeviceAttributes()));
 
         InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
-        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+        onPreferredDevicesForCapturePresetChanged(inputRouteManager);
 
         // The selected input device has the same type as the one returned from AudioManager.
         InputMediaDevice selectedInputDevice =
@@ -168,29 +227,19 @@
 
     @Test
     public void getSelectedInputDevice_returnMoreThanOneFromAudioManager() {
-        final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
-        when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
-        when(info1.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
-
-        final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
-        when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
-        when(info2.getId()).thenReturn(BUILTIN_MIC_ID);
-
         final AudioManager audioManager = mock(AudioManager.class);
-        AudioDeviceInfo[] devices = {info1, info2};
+        AudioDeviceInfo[] devices = {mockWiredHeadsetInfo(), mockBuiltinMicInfo()};
         when(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(devices);
 
         // Mock audioManager.getDevicesForAttributes returns more than one audioDeviceAttributes.
-        AudioDeviceAttributes audioDeviceAttributes1 = new AudioDeviceAttributes(info1);
-        AudioDeviceAttributes audioDeviceAttributes2 = new AudioDeviceAttributes(info2);
         List<AudioDeviceAttributes> attributesOfSelectedInputDevices = new ArrayList<>();
-        attributesOfSelectedInputDevices.add(audioDeviceAttributes1);
-        attributesOfSelectedInputDevices.add(audioDeviceAttributes2);
+        attributesOfSelectedInputDevices.add(getWiredHeadsetDeviceAttributes());
+        attributesOfSelectedInputDevices.add(getBuiltinMicDeviceAttributes());
         when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES))
                 .thenReturn(attributesOfSelectedInputDevices);
 
         InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
-        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+        onPreferredDevicesForCapturePresetChanged(inputRouteManager);
 
         // The selected input device has the same type as the first one returned from AudioManager.
         InputMediaDevice selectedInputDevice =
@@ -201,25 +250,17 @@
 
     @Test
     public void getSelectedInputDevice_returnEmptyFromAudioManager() {
-        final AudioDeviceInfo info1 = mock(AudioDeviceInfo.class);
-        when(info1.getType()).thenReturn(AudioDeviceInfo.TYPE_WIRED_HEADSET);
-        when(info1.getId()).thenReturn(INPUT_WIRED_HEADSET_ID);
-
-        final AudioDeviceInfo info2 = mock(AudioDeviceInfo.class);
-        when(info2.getType()).thenReturn(AudioDeviceInfo.TYPE_BUILTIN_MIC);
-        when(info2.getId()).thenReturn(BUILTIN_MIC_ID);
-
         final AudioManager audioManager = mock(AudioManager.class);
-        AudioDeviceInfo[] devices = {info1, info2};
+        AudioDeviceInfo[] devices = {mockWiredHeadsetInfo(), mockBuiltinMicInfo()};
         when(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(devices);
 
         // Mock audioManager.getDevicesForAttributes returns empty list of audioDeviceAttributes.
-        List<AudioDeviceAttributes> attributesOfSelectedInputDevices = new ArrayList<>();
+        List<AudioDeviceAttributes> emptyAttributesOfSelectedInputDevices = new ArrayList<>();
         when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES))
-                .thenReturn(attributesOfSelectedInputDevices);
+                .thenReturn(emptyAttributesOfSelectedInputDevices);
 
         InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
-        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+        onPreferredDevicesForCapturePresetChanged(inputRouteManager);
 
         // The selected input device has default type AudioDeviceInfo.TYPE_BUILTIN_MIC.
         InputMediaDevice selectedInputDevice =
@@ -232,39 +273,133 @@
     public void selectDevice() {
         final AudioManager audioManager = mock(AudioManager.class);
         InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
-        final MediaDevice inputMediaDevice =
+        final MediaDevice builtinMicDevice =
                 InputMediaDevice.create(
                         mContext,
                         String.valueOf(BUILTIN_MIC_ID),
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
-        inputRouteManager.selectDevice(inputMediaDevice);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_BUILTIN_MIC);
+        inputRouteManager.selectDevice(builtinMicDevice);
 
-        AudioDeviceAttributes deviceAttributes =
-                new AudioDeviceAttributes(
-                        AudioDeviceAttributes.ROLE_INPUT,
-                        AudioDeviceInfo.TYPE_BUILTIN_MIC,
-                        /* address= */ "");
         for (@MediaRecorder.Source int preset : PRESETS) {
             verify(audioManager, atLeastOnce())
-                    .setPreferredDeviceForCapturePreset(preset, deviceAttributes);
+                    .setPreferredDeviceForCapturePreset(preset, getBuiltinMicDeviceAttributes());
+        }
+    }
+
+    @Test
+    public void onInitiation_shouldApplyDefaultSelectedDeviceToAllPresets() {
+        final AudioManager audioManager = mock(AudioManager.class);
+        new InputRouteManager(mContext, audioManager);
+
+        verify(audioManager, atLeastOnce()).getDevicesForAttributes(INPUT_ATTRIBUTES);
+        for (@MediaRecorder.Source int preset : PRESETS) {
+            verify(audioManager, atLeastOnce())
+                    .setPreferredDeviceForCapturePreset(preset, getBuiltinMicDeviceAttributes());
+        }
+    }
+
+    @Test
+    public void onAudioDevicesAdded_shouldApplyDefaultSelectedDeviceToAllPresets() {
+        final AudioManager audioManager = mock(AudioManager.class);
+        AudioDeviceAttributes wiredHeadsetDeviceAttributes = getWiredHeadsetDeviceAttributes();
+        when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES))
+                .thenReturn(Collections.singletonList(wiredHeadsetDeviceAttributes));
+
+        InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+        AudioDeviceInfo[] devices = {mockWiredHeadsetInfo()};
+        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+        // Called twice, one after initiation, the other after onAudioDevicesAdded call.
+        verify(audioManager, atLeast(2)).getDevicesForAttributes(INPUT_ATTRIBUTES);
+        for (@MediaRecorder.Source int preset : PRESETS) {
+            verify(audioManager, atLeast(2))
+                    .setPreferredDeviceForCapturePreset(preset, wiredHeadsetDeviceAttributes);
+        }
+    }
+
+    @Test
+    public void onAudioDevicesRemoved_shouldApplyDefaultSelectedDeviceToAllPresets() {
+        final AudioManager audioManager = mock(AudioManager.class);
+        InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+        AudioDeviceInfo[] devices = {mockWiredHeadsetInfo()};
+        inputRouteManager.mAudioDeviceCallback.onAudioDevicesRemoved(devices);
+
+        // Called twice, one after initiation, the other after onAudioDevicesRemoved call.
+        verify(audioManager, atLeast(2)).getDevicesForAttributes(INPUT_ATTRIBUTES);
+        for (@MediaRecorder.Source int preset : PRESETS) {
+            verify(audioManager, atLeast(2))
+                    .setPreferredDeviceForCapturePreset(preset, getBuiltinMicDeviceAttributes());
         }
     }
 
     @Test
     public void getMaxInputGain_returnMaxInputGain() {
-        assertThat(mInputRouteManager.getMaxInputGain()).isEqualTo(15);
+        assertThat(mInputRouteManager.getMaxInputGain()).isEqualTo(100);
     }
 
     @Test
     public void getCurrentInputGain_returnCurrentInputGain() {
-        assertThat(mInputRouteManager.getCurrentInputGain()).isEqualTo(8);
+        assertThat(mInputRouteManager.getCurrentInputGain()).isEqualTo(100);
     }
 
     @Test
     public void isInputGainFixed() {
         assertThat(mInputRouteManager.isInputGainFixed()).isTrue();
     }
+
+    @Test
+    public void onAudioDevicesAdded_shouldSetProductNameCorrectly() {
+        final AudioDeviceInfo info1 = mockWiredHeadsetInfo();
+        String firstProductName = "My first headset";
+        when(info1.getProductName()).thenReturn(firstProductName);
+        InputMediaDevice inputMediaDevice1 = createInputMediaDeviceFromDeviceInfo(info1);
+
+        final AudioDeviceInfo info2 = mockWiredHeadsetInfo();
+        String secondProductName = "My second headset";
+        when(info2.getProductName()).thenReturn(secondProductName);
+        InputMediaDevice inputMediaDevice2 = createInputMediaDeviceFromDeviceInfo(info2);
+
+        final AudioDeviceInfo infoWithNullProductName = mockWiredHeadsetInfo();
+        when(infoWithNullProductName.getProductName()).thenReturn(null);
+        InputMediaDevice inputMediaDevice3 =
+                createInputMediaDeviceFromDeviceInfo(infoWithNullProductName);
+
+        final AudioDeviceInfo infoWithBlankProductName = mockWiredHeadsetInfo();
+        when(infoWithBlankProductName.getProductName()).thenReturn("");
+        InputMediaDevice inputMediaDevice4 =
+                createInputMediaDeviceFromDeviceInfo(infoWithBlankProductName);
+
+        final AudioManager audioManager = mock(AudioManager.class);
+        AudioDeviceInfo[] devices = {
+            info1, info2, infoWithNullProductName, infoWithBlankProductName
+        };
+        when(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(devices);
+
+        InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+
+        assertThat(inputRouteManager.mInputMediaDevices).isEmpty();
+
+        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+        onPreferredDevicesForCapturePresetChanged(inputRouteManager);
+
+        assertThat(inputRouteManager.mInputMediaDevices)
+                .containsExactly(
+                        inputMediaDevice1, inputMediaDevice2, inputMediaDevice3, inputMediaDevice4)
+                .inOrder();
+    }
+
+    private InputMediaDevice createInputMediaDeviceFromDeviceInfo(AudioDeviceInfo info) {
+        return InputMediaDevice.create(
+                mContext,
+                String.valueOf(info.getId()),
+                info.getType(),
+                MAX_VOLUME,
+                CURRENT_VOLUME,
+                VOLUME_FIXED_TRUE,
+                info.getProductName() == null ? null : info.getProductName().toString());
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 5293011..d8b6707 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -207,20 +207,6 @@
     }
 
     @Test
-    public void getHotspotIconResource_deviceTypeExists_shouldNotNull() {
-        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_PHONE))
-                .isNotNull();
-        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_TABLET))
-                .isNotNull();
-        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_LAPTOP))
-                .isNotNull();
-        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_WATCH))
-                .isNotNull();
-        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_AUTO))
-                .isNotNull();
-    }
-
-    @Test
     public void testInternetIconInjector_getIcon_returnsCorrectValues() {
         WifiUtils.InternetIconInjector iconInjector = new WifiUtils.InternetIconInjector(mContext);
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 4dc8424..d3ee400 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -282,5 +282,6 @@
         Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
         Settings.Secure.MANDATORY_BIOMETRICS,
         Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+        Settings.Secure.ADVANCED_PROTECTION_MODE,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 2cdd0ae..3530e0f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -106,6 +106,8 @@
                 Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
                 Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
                 Settings.System.LOCALE_PREFERENCES,
+                Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING,
+                Settings.System.MOUSE_SWAP_PRIMARY_BUTTON,
                 Settings.System.TOUCHPAD_POINTER_SPEED,
                 Settings.System.TOUCHPAD_NATURAL_SCROLLING,
                 Settings.System.TOUCHPAD_TAP_TO_CLICK,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 688676d..d34ccc5 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -442,5 +442,6 @@
         VALIDATORS.put(Secure.MANDATORY_BIOMETRICS, new InclusiveIntegerRangeValidator(0, 1));
         VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
                 new InclusiveIntegerRangeValidator(0, 1));
+        VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 2823277..509b88b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -221,6 +221,8 @@
                         POINTER_ICON_VECTOR_STYLE_STROKE_END));
         VALIDATORS.put(System.POINTER_SCALE,
                 new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE));
+        VALIDATORS.put(System.MOUSE_REVERSE_VERTICAL_SCROLLING, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.MOUSE_SWAP_PRIMARY_BUTTON, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
         VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index ec3bd90..ebeee85 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -312,8 +312,8 @@
             } else {
                 // If the ringtone/notification support the vibration, use the original value.
                 final int ringtoneType = getRingtoneType(name);
-                if ((Settings.System.RINGTONE.equals(name)
-                        || Settings.System.NOTIFICATION_SOUND.equals(name))
+                if ((ringtoneType == RingtoneManager.TYPE_RINGTONE
+                        || ringtoneType == RingtoneManager.TYPE_NOTIFICATION)
                         && hasVibrationSettings(value, ringtoneType)) {
                     return value;
                 }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index babc1a3..cea2bbc 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -57,9 +57,11 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.FixMethodOrder;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -67,6 +69,7 @@
  * Tests for the SettingsHelperTest
  */
 @RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class SettingsHelperTest {
     private static final String SETTING_KEY = "setting_key";
     private static final String SETTING_VALUE = "setting_value";
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index 2531454..3350efc 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -8,7 +8,10 @@
 }
 
 // used both for the android_app and android_library
-shell_srcs = ["src/**/*.java",":dumpstate_aidl"]
+shell_srcs = [
+    "src/**/*.java",
+    ":dumpstate_aidl",
+]
 shell_static_libs = ["androidx.legacy_legacy-support-v4"]
 
 android_app {
@@ -22,6 +25,9 @@
     libs: [
         "device_policy_aconfig_flags_lib",
     ],
+    flags_packages: [
+        "android.security.flags-aconfig",
+    ],
     platform_apis: true,
     certificate: "platform",
     privileged: true,
@@ -43,4 +49,7 @@
     static_libs: shell_static_libs,
     platform_apis: true,
     manifest: "AndroidManifest.xml",
+    flags_packages: [
+        "android.security.flags-aconfig",
+    ],
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 408ed1e..05c5e5d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -924,6 +924,7 @@
 
     <!-- Permission required for CTS test - CtsPackageManagerTestCases-->
     <uses-permission android:name="android.permission.DOMAIN_VERIFICATION_AGENT" />
+    <uses-permission android:name="android.permission.VERIFICATION_AGENT" />
 
     <!-- Permission required for Cts test - CtsInputTestCases -->
     <uses-permission
@@ -941,6 +942,11 @@
 
     <!-- Permission required for CTS test - CtsNfcTestCases -->
     <uses-permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
+    <!-- Permission required for CTS test - AdvancedProtectionManagerTest -->
+    <uses-permission android:name="android.permission.SET_ADVANCED_PROTECTION_MODE"
+        android:featureFlag="android.security.aapm_api"/>
+    <uses-permission android:name="android.permission.QUERY_ADVANCED_PROTECTION_MODE"
+        android:featureFlag="android.security.aapm_api"/>
 
     <!-- Permission required for CTS test - CtsAppTestCases -->
     <uses-permission android:name="android.permission.KILL_UID" />
diff --git a/packages/StatementService/Parser/Android.bp b/packages/StatementService/Parser/Android.bp
new file mode 100644
index 0000000..c8af134
--- /dev/null
+++ b/packages/StatementService/Parser/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "StatementServiceParser",
+    use_resource_processor: true,
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    target_sdk_version: "29",
+}
diff --git a/core/java/android/service/wallpaper/AndroidManifest.xml b/packages/StatementService/Parser/AndroidManifest.xml
similarity index 75%
rename from core/java/android/service/wallpaper/AndroidManifest.xml
rename to packages/StatementService/Parser/AndroidManifest.xml
index f1bdb76..a3a99ac 100644
--- a/core/java/android/service/wallpaper/AndroidManifest.xml
+++ b/packages/StatementService/Parser/AndroidManifest.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2024 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+     http://www.apache.org/licenses/LICENSE-2.0
 
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,7 +15,5 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.service.wallpaper">
-
-    <uses-sdk android:minSdkVersion="29" />
-</manifest>
+    package="com.android.statementservice.parser">
+</manifest>
\ No newline at end of file
diff --git a/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
new file mode 100644
index 0000000..4314f86
--- /dev/null
+++ b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DalComponentParser")
+
+package com.android.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+
+/**
+ * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type
+ * and pattern. Matching expressions support the following wildcards:
+ *
+ *  1) An asterisk (*) matches zero to as many characters as possible
+ *  2) A question mark (?) matches any single character.
+ *
+ * Matching one to many characters can be done with a question mark followed by an asterisk (?+).
+ *
+ * @param expression A matching expression string from a DAL relation extension component used for
+ *                   matching a URI part. This must be a non-empty string and all characters in the
+ *                   string should be decoded.
+ *
+ * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern.
+ */
+fun parseMatchingExpression(expression: String): Pair<Int, String> {
+    if (expression.isNullOrEmpty()) {
+        throw IllegalArgumentException("Matching expressions cannot be an empty string")
+    }
+    var count = 0
+    var isAdvanced = expression.contains("?*")
+    val pattern = buildString {
+        for (char in expression) {
+            when (char) {
+                '*' -> {
+                    if (this.endsWith('.') && !this.endsWith("\\.")) {
+                        append('+')
+                    } else {
+                        count += 1
+                        append(".*")
+                    }
+                }
+                '?' -> {
+                    count += 1
+                    append('.')
+                }
+                '.' -> {
+                    append("\\.")
+                }
+                '[', ']', '{', '}' -> {
+                    if (isAdvanced) {
+                        append('\\')
+                    }
+                    append(char)
+                }
+                else -> append(char)
+            }
+        }
+    }
+    if (count == 0) {
+        return Pair(PATTERN_LITERAL, pattern)
+    }
+    if (count == 1 && pattern.endsWith(".*")) {
+        return Pair(PATTERN_PREFIX, pattern.dropLast(2))
+    }
+    if (isAdvanced) {
+        return Pair(PATTERN_ADVANCED_GLOB, pattern)
+    }
+    return Pair(PATTERN_SIMPLE_GLOB, pattern)
+}
\ No newline at end of file
diff --git a/packages/StatementService/TEST_MAPPING b/packages/StatementService/TEST_MAPPING
new file mode 100644
index 0000000..0714c93
--- /dev/null
+++ b/packages/StatementService/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit" : [
+    {
+      "name": "StatementServiceTests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/StatementService/tests/Android.bp b/packages/StatementService/tests/Android.bp
new file mode 100644
index 0000000..ec1bd96
--- /dev/null
+++ b/packages/StatementService/tests/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_framework_android_packages",
+}
+
+android_test {
+    name: "StatementServiceTests",
+    use_resource_processor: true,
+    test_suites: ["general-tests"],
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.runner",
+        "StatementServiceParser",
+        "truth",
+    ],
+}
diff --git a/core/java/android/service/wallpaper/AndroidManifest.xml b/packages/StatementService/tests/AndroidManifest.xml
similarity index 61%
copy from core/java/android/service/wallpaper/AndroidManifest.xml
copy to packages/StatementService/tests/AndroidManifest.xml
index f1bdb76..bde0f95 100644
--- a/core/java/android/service/wallpaper/AndroidManifest.xml
+++ b/packages/StatementService/tests/AndroidManifest.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2024 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+     http://www.apache.org/licenses/LICENSE-2.0
 
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,7 +15,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.service.wallpaper">
+    package="com.android.statementservice.test">
 
-    <uses-sdk android:minSdkVersion="29" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.statementservice.test" />
 </manifest>
diff --git a/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
new file mode 100644
index 0000000..44a56ec
--- /dev/null
+++ b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DalComponentParserTest {
+
+    @Test
+    fun parseExpressions() {
+        validateParsedExpression("foobar", PATTERN_LITERAL, "foobar")
+        validateParsedExpression("foo.bar", PATTERN_LITERAL, "foo\\.bar")
+        validateParsedExpression("foo*", PATTERN_PREFIX, "foo")
+        validateParsedExpression("*bar", PATTERN_SIMPLE_GLOB, ".*bar")
+        validateParsedExpression("foo*bar", PATTERN_SIMPLE_GLOB, "foo.*bar")
+        validateParsedExpression("foo.*bar", PATTERN_SIMPLE_GLOB, "foo\\..*bar")
+        validateParsedExpression("*foo*bar", PATTERN_SIMPLE_GLOB, ".*foo.*bar")
+        validateParsedExpression("foo?bar", PATTERN_SIMPLE_GLOB, "foo.bar")
+        validateParsedExpression("foo.?bar", PATTERN_SIMPLE_GLOB, "foo\\..bar")
+        validateParsedExpression("?bar", PATTERN_SIMPLE_GLOB, ".bar")
+        validateParsedExpression("foo?", PATTERN_SIMPLE_GLOB, "foo.")
+        validateParsedExpression("fo?b*r", PATTERN_SIMPLE_GLOB, "fo.b.*r")
+        validateParsedExpression("?*bar", PATTERN_ADVANCED_GLOB, ".+bar")
+        validateParsedExpression("foo?*bar", PATTERN_ADVANCED_GLOB, "foo.+bar")
+        validateParsedExpression("foo?*bar*", PATTERN_ADVANCED_GLOB, "foo.+bar.*")
+        validateParsedExpression("foo*?bar", PATTERN_SIMPLE_GLOB, "foo.*.bar")
+
+        // set matches are not supported in DAL
+        validateParsedExpression("foo[a-z]", PATTERN_LITERAL, "foo[a-z]")
+        validateParsedExpression("foo[a-z]+", PATTERN_LITERAL, "foo[a-z]+")
+        validateParsedExpression("foo[a-z]*", PATTERN_PREFIX, "foo[a-z]")
+        validateParsedExpression("[a-z]*bar", PATTERN_SIMPLE_GLOB, "[a-z].*bar")
+        validateParsedExpression("foo[a-z]?bar", PATTERN_SIMPLE_GLOB, "foo[a-z].bar")
+        validateParsedExpression("foo[a-z]?*bar", PATTERN_ADVANCED_GLOB, "foo\\[a-z\\].+bar")
+
+        // range matches are not supported in DAL
+        validateParsedExpression("fo{2}", PATTERN_LITERAL, "fo{2}")
+        validateParsedExpression("fo{2}+", PATTERN_LITERAL, "fo{2}+")
+        validateParsedExpression("fo{2}*", PATTERN_PREFIX, "fo{2}")
+        validateParsedExpression("fo{2}*bar", PATTERN_SIMPLE_GLOB, "fo{2}.*bar")
+        validateParsedExpression("fo{2}?*", PATTERN_ADVANCED_GLOB, "fo\\{2\\}.+")
+        validateParsedExpression("foo{2}?*bar", PATTERN_ADVANCED_GLOB, "foo\\{2\\}.+bar")
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun parseEmptyExpression() {
+        parseMatchingExpression("")
+    }
+
+    private fun validateParsedExpression(given: String, expectedType: Int, expectedFilter: String) {
+        val (type, filter) = parseMatchingExpression(given)
+        assertThat(filter).isEqualTo(expectedFilter)
+        assertThat(type).isEqualTo(expectedType)
+        assertThat(filter).isEqualTo(expectedFilter)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 1f10ead..3c560fd 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -87,7 +87,6 @@
     srcs: [
         "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
         "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
-        "tests/src/**/systemui/doze/DozeMachineTest.java",
         "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
         "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
         "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
@@ -175,7 +174,6 @@
         "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt",
         "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt",
         "tests/src/**/systemui/controls/ui/DetailDialogTest.kt",
-        "tests/src/**/systemui/doze/DozeMachineTest.kt",
         "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt",
         "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
         "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
@@ -318,9 +316,6 @@
         "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
         "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
         "tests/src/**/keyguard/ClockEventControllerTest.kt",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.kt",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerTest.java",
         "tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
         "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
@@ -417,7 +412,6 @@
         "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
         "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
         "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
-        "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.java",
         "tests/src/**/keyguard/CarrierTextManagerTest.java",
         "tests/src/**/systemui/ScreenDecorationsTest.java",
         "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
@@ -425,8 +419,8 @@
         "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
         "tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
         "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
-        "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt",
-        "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt",
         "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt",
         "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt",
         "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt",
@@ -802,6 +796,7 @@
         "SystemUICustomizationTestUtils",
         "androidx.compose.runtime_runtime",
         "kosmos",
+        "testables",
         "androidx.test.rules",
     ],
     libs: [
@@ -898,9 +893,9 @@
         "androidx.compose.runtime_runtime",
     ],
     libs: [
-        "android.test.runner.stubs.system",
-        "android.test.base.stubs.system",
-        "android.test.mock.stubs.system",
+        "android.test.runner.impl",
+        "android.test.base.impl",
+        "android.test.mock.impl",
         "truth",
     ],
 
@@ -935,9 +930,9 @@
         "androidx.compose.runtime_runtime",
     ],
     libs: [
-        "android.test.runner.stubs.system",
-        "android.test.base.stubs.system",
-        "android.test.mock.stubs.system",
+        "android.test.runner.impl",
+        "android.test.base.impl",
+        "android.test.mock.impl",
         "truth",
     ],
 
@@ -973,9 +968,9 @@
         "androidx.compose.runtime_runtime",
     ],
     libs: [
-        "android.test.runner.stubs.system",
-        "android.test.base.stubs.system",
-        "android.test.mock.stubs.system",
+        "android.test.runner.impl",
+        "android.test.base.impl",
+        "android.test.mock.impl",
     ],
     auto_gen_config: true,
     plugins: [
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 07a1e63..380344a 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -148,5 +148,10 @@
     {
       "name": "SystemUIGoogleRobo2RNGTests"
     }
+  ],
+  "imports": [
+    {
+      "path": "cts/tests/tests/multiuser"
+    }
   ]
 }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a21a805..426b24d 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -298,6 +298,22 @@
 }
 
 flag {
+    name: "status_bar_ui_thread"
+    namespace: "systemui"
+    description: "Move the StatusBar window to a new UI thread, which is separate from the main "
+        "thread."
+    bug: "374159193"
+}
+
+flag {
+    name: "notification_shade_ui_thread"
+    namespace: "systemui"
+    description: "Move the NotificationShade window to a new UI thread, which is separate from "
+        "the main thread."
+    bug: "374159657"
+}
+
+flag {
     name: "new_aod_transition"
     namespace: "systemui"
     description: "New LOCKSCREEN <=> AOD transition"
@@ -395,9 +411,9 @@
 }
 
 flag {
-    name: "status_bar_ron_chips"
+    name: "status_bar_notification_chips"
     namespace: "systemui"
-    description: "Show rich ongoing notifications as chips in the status bar"
+    description: "Show promoted ongoing notifications as chips in the status bar"
     bug: "361346412"
 }
 
@@ -644,6 +660,13 @@
 }
 
 flag {
+    name: "screenshot_context_url"
+    namespace: "systemui"
+    description: "Include optional app-provided context URL when sharing a screenshot."
+    bug: "242791070"
+}
+
+flag {
    name: "run_fingerprint_detect_on_dismissible_keyguard"
    namespace: "systemui"
    description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
@@ -1481,3 +1504,56 @@
    bug: "370863642"
 }
 
+flag {
+  name: "notes_role_qs_tile"
+  namespace: "systemui"
+  description: "Enables notes role qs tile which opens default notes role app in app bubbles"
+  bug: "357863750"
+}
+
+flag {
+  name: "ignore_touches_next_to_notification_shelf"
+  namespace: "systemui"
+  description: "The shelf can vertically overlap the unlock icon. Ignore touches if so."
+  bug: "358424256"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
+   name: "media_projection_request_attribution_fix"
+   namespace: "systemui"
+   description: "Ensure MediaProjection consent requests are properly attributed"
+   bug: "373581993"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
+  name: "secondary_user_widget_host"
+  namespace: "systemui"
+  description: "Host communal widgets in the current secondary user on HSUM."
+  bug: "373874416"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+   name: "only_show_media_stream_slider_in_single_volume_mode"
+   namespace: "systemui"
+   description: "When the device is in single volume mode, only show media stream slider and hide all other stream (e.g. call, notification, alarm, etc) sliders in volume panel"
+   bug: "373729625"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
+    name: "qs_tile_detailed_view"
+    namespace: "systemui"
+    description: "Enables the tile detailed view UI."
+    bug: "374173773"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index d025275..18f40c9 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -136,6 +136,19 @@
             )
 
         /**
+         * The timings when animating a View into an app using a spring animator. These timings
+         * represent fractions of the progress between the spring's initial value and its final
+         * value.
+         */
+        val SPRING_TIMINGS =
+            TransitionAnimator.SpringTimings(
+                contentBeforeFadeOutDelay = 0f,
+                contentBeforeFadeOutDuration = 0.8f,
+                contentAfterFadeInDelay = 0.85f,
+                contentAfterFadeInDuration = 0.135f,
+            )
+
+        /**
          * The timings when animating a Dialog into an app. We need to wait at least 200ms before
          * showing the app (which is under the dialog window) so that the dialog window dim is fully
          * faded out, to avoid flicker.
@@ -152,6 +165,13 @@
                 contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f),
             )
 
+        /** The interpolators when animating a View into an app using a spring animator. */
+        val SPRING_INTERPOLATORS =
+            INTERPOLATORS.copy(
+                contentBeforeFadeOutInterpolator = Interpolators.DECELERATE_1_5,
+                contentAfterFadeInInterpolator = Interpolators.SLOW_OUT_LINEAR_IN,
+            )
+
         // TODO(b/288507023): Remove this flag.
         @JvmField val DEBUG_TRANSITION_ANIMATION = Build.IS_DEBUGGABLE
 
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index fc4cf1d..4cf2642 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -23,16 +23,24 @@
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffXfermode
 import android.graphics.drawable.GradientDrawable
+import android.util.FloatProperty
 import android.util.Log
 import android.util.MathUtils
 import android.view.View
 import android.view.ViewGroup
+import android.view.ViewGroupOverlay
+import android.view.ViewOverlay
 import android.view.animation.Interpolator
 import android.window.WindowAnimationState
-import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators.LINEAR
+import com.android.app.animation.MathUtils.max
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.dynamicanimation.animation.SpringAnimation
+import com.android.internal.dynamicanimation.animation.SpringForce
 import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
 import java.util.concurrent.Executor
+import kotlin.math.abs
+import kotlin.math.min
 import kotlin.math.roundToInt
 
 private const val TAG = "TransitionAnimator"
@@ -42,11 +50,27 @@
     private val mainExecutor: Executor,
     private val timings: Timings,
     private val interpolators: Interpolators,
+
+    /** [springTimings] and [springInterpolators] must either both be null or both not null. */
+    private val springTimings: SpringTimings? = null,
+    private val springInterpolators: Interpolators? = null,
+    private val springParams: SpringParams = DEFAULT_SPRING_PARAMS,
 ) {
     companion object {
         internal const val DEBUG = false
         private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
 
+        /** Default parameters for the multi-spring animator. */
+        private val DEFAULT_SPRING_PARAMS =
+            SpringParams(
+                centerXStiffness = 450f,
+                centerXDampingRatio = 0.965f,
+                centerYStiffness = 400f,
+                centerYDampingRatio = 0.95f,
+                scaleStiffness = 500f,
+                scaleDampingRatio = 0.99f,
+            )
+
         /**
          * Given the [linearProgress] of a transition animation, return the linear progress of the
          * sub-animation starting [delay] ms after the transition animation and that lasts
@@ -59,8 +83,22 @@
             delay: Long,
             duration: Long,
         ): Float {
+            return getProgressInternal(
+                timings.totalDuration.toFloat(),
+                linearProgress,
+                delay.toFloat(),
+                duration.toFloat(),
+            )
+        }
+
+        private fun getProgressInternal(
+            totalDuration: Float,
+            linearProgress: Float,
+            delay: Float,
+            duration: Float,
+        ): Float {
             return MathUtils.constrain(
-                (linearProgress * timings.totalDuration - delay) / duration,
+                (linearProgress * totalDuration - delay) / duration,
                 0.0f,
                 1.0f,
             )
@@ -84,11 +122,32 @@
                 it.bottomCornerRadius = (bottomLeftRadius + bottomRightRadius) / 2
                 it.topCornerRadius = (topLeftRadius + topRightRadius) / 2
             }
+
+        /** Builds a [FloatProperty] for updating the defined [property] using a spring. */
+        private fun buildProperty(
+            property: SpringProperty,
+            updateProgress: (SpringState) -> Unit,
+        ): FloatProperty<SpringState> {
+            return object : FloatProperty<SpringState>(property.name) {
+                override fun get(state: SpringState): Float {
+                    return property.get(state)
+                }
+
+                override fun setValue(state: SpringState, value: Float) {
+                    property.setValue(state, value)
+                    updateProgress(state)
+                }
+            }
+        }
     }
 
     private val transitionContainerLocation = IntArray(2)
     private val cornerRadii = FloatArray(8)
 
+    init {
+        check((springTimings == null) == (springInterpolators == null))
+    }
+
     /**
      * A controller that takes care of applying the animation to an expanding view.
      *
@@ -196,11 +255,111 @@
         var visible: Boolean = true
     }
 
+    /** Encapsulated the state of a multi-spring animation. */
+    internal class SpringState(
+        // Animated values.
+        var centerX: Float,
+        var centerY: Float,
+        var scale: Float = 0f,
+
+        // Cached values.
+        var previousCenterX: Float = -1f,
+        var previousCenterY: Float = -1f,
+        var previousScale: Float = -1f,
+
+        // Completion flags.
+        var isCenterXDone: Boolean = false,
+        var isCenterYDone: Boolean = false,
+        var isScaleDone: Boolean = false,
+    ) {
+        /** Whether all springs composing the animation have settled in the final position. */
+        val isDone
+            get() = isCenterXDone && isCenterYDone && isScaleDone
+    }
+
+    /** Supported [SpringState] properties with getters and setters to update them. */
+    private enum class SpringProperty {
+        CENTER_X {
+            override fun get(state: SpringState): Float {
+                return state.centerX
+            }
+
+            override fun setValue(state: SpringState, value: Float) {
+                state.centerX = value
+            }
+        },
+        CENTER_Y {
+            override fun get(state: SpringState): Float {
+                return state.centerY
+            }
+
+            override fun setValue(state: SpringState, value: Float) {
+                state.centerY = value
+            }
+        },
+        SCALE {
+            override fun get(state: SpringState): Float {
+                return state.scale
+            }
+
+            override fun setValue(state: SpringState, value: Float) {
+                state.scale = value
+            }
+        };
+
+        /** Extracts the current value of the underlying property from [state]. */
+        abstract fun get(state: SpringState): Float
+
+        /** Update's the [value] of the underlying property inside [state]. */
+        abstract fun setValue(state: SpringState, value: Float)
+    }
+
     interface Animation {
+        /** Start the animation. */
+        fun start()
+
         /** Cancel the animation. */
         fun cancel()
     }
 
+    @VisibleForTesting
+    class InterpolatedAnimation(@get:VisibleForTesting val animator: Animator) : Animation {
+        override fun start() {
+            animator.start()
+        }
+
+        override fun cancel() {
+            animator.cancel()
+        }
+    }
+
+    @VisibleForTesting
+    class MultiSpringAnimation
+    internal constructor(
+        @get:VisibleForTesting val springX: SpringAnimation,
+        @get:VisibleForTesting val springY: SpringAnimation,
+        @get:VisibleForTesting val springScale: SpringAnimation,
+        private val springState: SpringState,
+        private val onAnimationStart: Runnable,
+    ) : Animation {
+        @get:VisibleForTesting
+        val isDone
+            get() = springState.isDone
+
+        override fun start() {
+            onAnimationStart.run()
+            springX.start()
+            springY.start()
+            springScale.start()
+        }
+
+        override fun cancel() {
+            springX.cancel()
+            springY.cancel()
+            springScale.cancel()
+        }
+    }
+
     /** The timings (durations and delays) used by this animator. */
     data class Timings(
         /** The total duration of the animation. */
@@ -222,6 +381,25 @@
         val contentAfterFadeInDuration: Long,
     )
 
+    /**
+     * The timings (durations and delays) used by the multi-spring animator. These are expressed as
+     * fractions of 1, similar to how the progress of an animator can be expressed as a float value
+     * between 0 and 1.
+     */
+    class SpringTimings(
+        /** The portion of animation to wait before fading out the expanding content. */
+        val contentBeforeFadeOutDelay: Float,
+
+        /** The portion of animation during which the expanding content fades out. */
+        val contentBeforeFadeOutDuration: Float,
+
+        /** The portion of animation to wait before fading in the expanded content. */
+        val contentAfterFadeInDelay: Float,
+
+        /** The portion of animation during which the expanded content fades in. */
+        val contentAfterFadeInDuration: Float,
+    )
+
     /** The interpolators used by this animator. */
     data class Interpolators(
         /** The interpolator used for the Y position, width, height and corner radius. */
@@ -240,6 +418,21 @@
         val contentAfterFadeInInterpolator: Interpolator,
     )
 
+    /** The parameters (stiffnesses and damping ratios) used by the multi-spring animator. */
+    data class SpringParams(
+        // Parameters for the X position spring.
+        val centerXStiffness: Float,
+        val centerXDampingRatio: Float,
+
+        // Parameters for the Y position spring.
+        val centerYStiffness: Float,
+        val centerYDampingRatio: Float,
+
+        // Parameters for the scale spring.
+        val scaleStiffness: Float,
+        val scaleDampingRatio: Float,
+    )
+
     /**
      * Start a transition animation controlled by [controller] towards [endState]. An intermediary
      * layer with [windowBackgroundColor] will fade in then (optionally) fade out above the
@@ -250,6 +443,9 @@
      * the animation (if ![Controller.isLaunching]), and will have SRC blending mode (ultimately
      * punching a hole in the [transition container][Controller.transitionContainer]) iff [drawHole]
      * is true.
+     *
+     * If [useSpring] is true, a multi-spring animation will be used instead of the default
+     * interpolators.
      */
     fun startAnimation(
         controller: Controller,
@@ -257,8 +453,9 @@
         windowBackgroundColor: Int,
         fadeWindowBackgroundLayer: Boolean = true,
         drawHole: Boolean = false,
+        useSpring: Boolean = false,
     ): Animation {
-        if (!controller.isLaunching) checkReturnAnimationFrameworkFlag()
+        if (!controller.isLaunching || useSpring) checkReturnAnimationFrameworkFlag()
 
         // We add an extra layer with the same color as the dialog/app splash screen background
         // color, which is usually the same color of the app background. We first fade in this layer
@@ -270,33 +467,91 @@
                 alpha = 0
             }
 
-        val animator =
-            createAnimator(
+        return createAnimation(
                 controller,
+                controller.createAnimatorState(),
                 endState,
                 windowBackgroundLayer,
                 fadeWindowBackgroundLayer,
                 drawHole,
+                useSpring,
             )
-        animator.start()
-
-        return object : Animation {
-            override fun cancel() {
-                animator.cancel()
-            }
-        }
+            .apply { start() }
     }
 
     @VisibleForTesting
-    fun createAnimator(
+    fun createAnimation(
         controller: Controller,
+        startState: State,
         endState: State,
         windowBackgroundLayer: GradientDrawable,
         fadeWindowBackgroundLayer: Boolean = true,
         drawHole: Boolean = false,
-    ): ValueAnimator {
-        val state = controller.createAnimatorState()
+        useSpring: Boolean = false,
+    ): Animation {
+        val transitionContainer = controller.transitionContainer
+        val transitionContainerOverlay = transitionContainer.overlay
+        val openingWindowSyncView = controller.openingWindowSyncView
+        val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay
 
+        // Whether we should move the [windowBackgroundLayer] into the overlay of
+        // [Controller.openingWindowSyncView] once the opening app window starts to be visible, or
+        // from it once the closing app window stops being visible.
+        // This is necessary as a one-off sync so we can avoid syncing at every frame, especially
+        // in complex interactions like launching an activity from a dialog. See
+        // b/214961273#comment2 for more details.
+        val moveBackgroundLayerWhenAppVisibilityChanges =
+            openingWindowSyncView != null &&
+                openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl
+
+        return if (useSpring && springTimings != null && springInterpolators != null) {
+            createSpringAnimation(
+                controller,
+                startState,
+                endState,
+                windowBackgroundLayer,
+                transitionContainer,
+                transitionContainerOverlay,
+                openingWindowSyncView,
+                openingWindowSyncViewOverlay,
+                fadeWindowBackgroundLayer,
+                drawHole,
+                moveBackgroundLayerWhenAppVisibilityChanges,
+            )
+        } else {
+            createInterpolatedAnimation(
+                controller,
+                startState,
+                endState,
+                windowBackgroundLayer,
+                transitionContainer,
+                transitionContainerOverlay,
+                openingWindowSyncView,
+                openingWindowSyncViewOverlay,
+                fadeWindowBackgroundLayer,
+                drawHole,
+                moveBackgroundLayerWhenAppVisibilityChanges,
+            )
+        }
+    }
+
+    /**
+     * Creates an interpolator-based animator that uses [timings] and [interpolators] to calculate
+     * the new bounds and corner radiuses at each frame.
+     */
+    private fun createInterpolatedAnimation(
+        controller: Controller,
+        state: State,
+        endState: State,
+        windowBackgroundLayer: GradientDrawable,
+        transitionContainer: View,
+        transitionContainerOverlay: ViewGroupOverlay,
+        openingWindowSyncView: View? = null,
+        openingWindowSyncViewOverlay: ViewOverlay? = null,
+        fadeWindowBackgroundLayer: Boolean = true,
+        drawHole: Boolean = false,
+        moveBackgroundLayerWhenAppVisibilityChanges: Boolean = false,
+    ): Animation {
         // Start state.
         val startTop = state.top
         val startBottom = state.bottom
@@ -333,60 +588,35 @@
             }
         }
 
-        val transitionContainer = controller.transitionContainer
         val isExpandingFullyAbove = isExpandingFullyAbove(transitionContainer, endState)
+        var movedBackgroundLayer = false
 
         // Update state.
         val animator = ValueAnimator.ofFloat(0f, 1f)
         animator.duration = timings.totalDuration
         animator.interpolator = LINEAR
 
-        // Whether we should move the [windowBackgroundLayer] into the overlay of
-        // [Controller.openingWindowSyncView] once the opening app window starts to be visible, or
-        // from it once the closing app window stops being visible.
-        // This is necessary as a one-off sync so we can avoid syncing at every frame, especially
-        // in complex interactions like launching an activity from a dialog. See
-        // b/214961273#comment2 for more details.
-        val openingWindowSyncView = controller.openingWindowSyncView
-        val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay
-        val moveBackgroundLayerWhenAppVisibilityChanges =
-            openingWindowSyncView != null &&
-                openingWindowSyncView.viewRootImpl != controller.transitionContainer.viewRootImpl
-
-        val transitionContainerOverlay = transitionContainer.overlay
-        var movedBackgroundLayer = false
-
         animator.addListener(
             object : AnimatorListenerAdapter() {
                 override fun onAnimationStart(animation: Animator, isReverse: Boolean) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Animation started")
-                    }
-                    controller.onTransitionAnimationStart(isExpandingFullyAbove)
-
-                    // Add the drawable to the transition container overlay. Overlays always draw
-                    // drawables after views, so we know that it will be drawn above any view added
-                    // by the controller.
-                    if (controller.isLaunching || openingWindowSyncViewOverlay == null) {
-                        transitionContainerOverlay.add(windowBackgroundLayer)
-                    } else {
-                        openingWindowSyncViewOverlay.add(windowBackgroundLayer)
-                    }
+                    onAnimationStart(
+                        controller,
+                        isExpandingFullyAbove,
+                        windowBackgroundLayer,
+                        transitionContainerOverlay,
+                        openingWindowSyncViewOverlay,
+                    )
                 }
 
                 override fun onAnimationEnd(animation: Animator) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Animation ended")
-                    }
-
-                    // TODO(b/330672236): Post this to the main thread instead so that it does not
-                    // flicker with Flexiglass enabled.
-                    controller.onTransitionAnimationEnd(isExpandingFullyAbove)
-                    transitionContainerOverlay.remove(windowBackgroundLayer)
-
-                    if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
-                        openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
-                    }
+                    onAnimationEnd(
+                        controller,
+                        isExpandingFullyAbove,
+                        windowBackgroundLayer,
+                        transitionContainerOverlay,
+                        openingWindowSyncViewOverlay,
+                        moveBackgroundLayerWhenAppVisibilityChanges,
+                    )
                 }
             }
         )
@@ -413,63 +643,20 @@
             state.bottomCornerRadius =
                 MathUtils.lerp(startBottomCornerRadius, endBottomCornerRadius, progress)
 
-            state.visible =
-                if (controller.isLaunching) {
-                    // The expanding view can/should be hidden once it is completely covered by the
-                    // opening window.
-                    getProgress(
-                        timings,
-                        linearProgress,
-                        timings.contentBeforeFadeOutDelay,
-                        timings.contentBeforeFadeOutDuration,
-                    ) < 1
-                } else {
-                    getProgress(
-                        timings,
-                        linearProgress,
-                        timings.contentAfterFadeInDelay,
-                        timings.contentAfterFadeInDuration,
-                    ) > 0
-                }
+            state.visible = checkVisibility(timings, linearProgress, controller.isLaunching)
 
-            if (
-                controller.isLaunching &&
-                    moveBackgroundLayerWhenAppVisibilityChanges &&
-                    !state.visible &&
-                    !movedBackgroundLayer
-            ) {
-                // The expanding view is not visible, so the opening app is visible. If this is
-                // the first frame when it happens, trigger a one-off sync and move the
-                // background layer in its new container.
-                movedBackgroundLayer = true
-
-                transitionContainerOverlay.remove(windowBackgroundLayer)
-                openingWindowSyncViewOverlay!!.add(windowBackgroundLayer)
-
-                ViewRootSync.synchronizeNextDraw(
-                    transitionContainer,
-                    openingWindowSyncView,
-                    then = {},
-                )
-            } else if (
-                !controller.isLaunching &&
-                    moveBackgroundLayerWhenAppVisibilityChanges &&
-                    state.visible &&
-                    !movedBackgroundLayer
-            ) {
-                // The contracting view is now visible, so the closing app is not. If this is
-                // the first frame when it happens, trigger a one-off sync and move the
-                // background layer in its new container.
-                movedBackgroundLayer = true
-
-                openingWindowSyncViewOverlay!!.remove(windowBackgroundLayer)
-                transitionContainerOverlay.add(windowBackgroundLayer)
-
-                ViewRootSync.synchronizeNextDraw(
-                    openingWindowSyncView,
-                    transitionContainer,
-                    then = {},
-                )
+            if (!movedBackgroundLayer) {
+                movedBackgroundLayer =
+                    maybeMoveBackgroundLayer(
+                        controller,
+                        state,
+                        windowBackgroundLayer,
+                        transitionContainer,
+                        transitionContainerOverlay,
+                        openingWindowSyncView,
+                        openingWindowSyncViewOverlay,
+                        moveBackgroundLayerWhenAppVisibilityChanges,
+                    )
             }
 
             val container =
@@ -478,7 +665,6 @@
                 } else {
                     controller.transitionContainer
                 }
-
             applyStateToWindowBackgroundLayer(
                 windowBackgroundLayer,
                 state,
@@ -487,11 +673,342 @@
                 fadeWindowBackgroundLayer,
                 drawHole,
                 controller.isLaunching,
+                useSpring = false,
             )
+
             controller.onTransitionAnimationProgress(state, progress, linearProgress)
         }
 
-        return animator
+        return InterpolatedAnimation(animator)
+    }
+
+    /**
+     * Creates a compound animator made up of three springs: one for the center x position, one for
+     * the center-y position, and one for the overall scale.
+     *
+     * This animator uses [springTimings] and [springInterpolators] for opacity, based on the scale
+     * progress.
+     */
+    private fun createSpringAnimation(
+        controller: Controller,
+        startState: State,
+        endState: State,
+        windowBackgroundLayer: GradientDrawable,
+        transitionContainer: View,
+        transitionContainerOverlay: ViewGroupOverlay,
+        openingWindowSyncView: View?,
+        openingWindowSyncViewOverlay: ViewOverlay?,
+        fadeWindowBackgroundLayer: Boolean = true,
+        drawHole: Boolean = false,
+        moveBackgroundLayerWhenAppVisibilityChanges: Boolean = false,
+    ): Animation {
+        var springX: SpringAnimation? = null
+        var springY: SpringAnimation? = null
+        var targetX = endState.centerX
+        var targetY = endState.centerY
+
+        var movedBackgroundLayer = false
+
+        fun maybeUpdateEndState() {
+            if (endState.centerX != targetX && endState.centerY != targetY) {
+                targetX = endState.centerX
+                targetY = endState.centerY
+
+                springX?.animateToFinalPosition(targetX)
+                springY?.animateToFinalPosition(targetY)
+            }
+        }
+
+        fun updateProgress(state: SpringState) {
+            if (
+                (!state.isCenterXDone && state.centerX == state.previousCenterX) ||
+                    (!state.isCenterYDone && state.centerY == state.previousCenterY) ||
+                    (!state.isScaleDone && state.scale == state.previousScale)
+            ) {
+                // Because all three springs use the same update method, we only actually update
+                // when all values have changed, avoiding two redundant calls per frame.
+                return
+            }
+
+            // Update the latest values for the check above.
+            state.previousCenterX = state.centerX
+            state.previousCenterY = state.centerY
+            state.previousScale = state.scale
+
+            // Current scale-based values, that will be used to find the new animation bounds.
+            val width =
+                MathUtils.lerp(startState.width.toFloat(), endState.width.toFloat(), state.scale)
+            val height =
+                MathUtils.lerp(startState.height.toFloat(), endState.height.toFloat(), state.scale)
+
+            val newState =
+                State(
+                        left = (state.centerX - width / 2).toInt(),
+                        top = (state.centerY - height / 2).toInt(),
+                        right = (state.centerX + width / 2).toInt(),
+                        bottom = (state.centerY + height / 2).toInt(),
+                        topCornerRadius =
+                            MathUtils.lerp(
+                                startState.topCornerRadius,
+                                endState.topCornerRadius,
+                                state.scale,
+                            ),
+                        bottomCornerRadius =
+                            MathUtils.lerp(
+                                startState.bottomCornerRadius,
+                                endState.bottomCornerRadius,
+                                state.scale,
+                            ),
+                    )
+                    .apply {
+                        visible = checkVisibility(timings, state.scale, controller.isLaunching)
+                    }
+
+            if (!movedBackgroundLayer) {
+                movedBackgroundLayer =
+                    maybeMoveBackgroundLayer(
+                        controller,
+                        newState,
+                        windowBackgroundLayer,
+                        transitionContainer,
+                        transitionContainerOverlay,
+                        openingWindowSyncView,
+                        openingWindowSyncViewOverlay,
+                        moveBackgroundLayerWhenAppVisibilityChanges,
+                    )
+            }
+
+            val container =
+                if (movedBackgroundLayer) {
+                    openingWindowSyncView!!
+                } else {
+                    controller.transitionContainer
+                }
+            applyStateToWindowBackgroundLayer(
+                windowBackgroundLayer,
+                newState,
+                state.scale,
+                container,
+                fadeWindowBackgroundLayer,
+                drawHole,
+                isLaunching = false,
+                useSpring = true,
+            )
+
+            controller.onTransitionAnimationProgress(newState, state.scale, state.scale)
+
+            maybeUpdateEndState()
+        }
+
+        val springState = SpringState(centerX = startState.centerX, centerY = startState.centerY)
+        val isExpandingFullyAbove = isExpandingFullyAbove(transitionContainer, endState)
+
+        /** End listener for each spring, which only does the end work if all springs are done. */
+        fun onAnimationEnd() {
+            if (!springState.isDone) return
+            onAnimationEnd(
+                controller,
+                isExpandingFullyAbove,
+                windowBackgroundLayer,
+                transitionContainerOverlay,
+                openingWindowSyncViewOverlay,
+                moveBackgroundLayerWhenAppVisibilityChanges,
+            )
+        }
+
+        springX =
+            SpringAnimation(
+                    springState,
+                    buildProperty(SpringProperty.CENTER_X) { state -> updateProgress(state) },
+                )
+                .apply {
+                    spring =
+                        SpringForce(endState.centerX).apply {
+                            stiffness = springParams.centerXStiffness
+                            dampingRatio = springParams.centerXDampingRatio
+                        }
+
+                    setStartValue(startState.centerX)
+                    setMinValue(min(startState.centerX, endState.centerX))
+                    setMaxValue(max(startState.centerX, endState.centerX))
+
+                    addEndListener { _, _, _, _ ->
+                        springState.isCenterXDone = true
+                        onAnimationEnd()
+                    }
+                }
+        springY =
+            SpringAnimation(
+                    springState,
+                    buildProperty(SpringProperty.CENTER_Y) { state -> updateProgress(state) },
+                )
+                .apply {
+                    spring =
+                        SpringForce(endState.centerY).apply {
+                            stiffness = springParams.centerYStiffness
+                            dampingRatio = springParams.centerYDampingRatio
+                        }
+
+                    setStartValue(startState.centerY)
+                    setMinValue(min(startState.centerY, endState.centerY))
+                    setMaxValue(max(startState.centerY, endState.centerY))
+
+                    addEndListener { _, _, _, _ ->
+                        springState.isCenterYDone = true
+                        onAnimationEnd()
+                    }
+                }
+        val springScale =
+            SpringAnimation(
+                    springState,
+                    buildProperty(SpringProperty.SCALE) { state -> updateProgress(state) },
+                )
+                .apply {
+                    spring =
+                        SpringForce(1f).apply {
+                            stiffness = springParams.scaleStiffness
+                            dampingRatio = springParams.scaleDampingRatio
+                        }
+
+                    setStartValue(0f)
+                    setMaxValue(1f)
+                    setMinimumVisibleChange(abs(1f / startState.height))
+
+                    addEndListener { _, _, _, _ ->
+                        springState.isScaleDone = true
+                        onAnimationEnd()
+                    }
+                }
+
+        return MultiSpringAnimation(springX, springY, springScale, springState) {
+            onAnimationStart(
+                controller,
+                isExpandingFullyAbove,
+                windowBackgroundLayer,
+                transitionContainerOverlay,
+                openingWindowSyncViewOverlay,
+            )
+        }
+    }
+
+    private fun onAnimationStart(
+        controller: Controller,
+        isExpandingFullyAbove: Boolean,
+        windowBackgroundLayer: GradientDrawable,
+        transitionContainerOverlay: ViewGroupOverlay,
+        openingWindowSyncViewOverlay: ViewOverlay?,
+    ) {
+        if (DEBUG) {
+            Log.d(TAG, "Animation started")
+        }
+        controller.onTransitionAnimationStart(isExpandingFullyAbove)
+
+        // Add the drawable to the transition container overlay. Overlays always draw
+        // drawables after views, so we know that it will be drawn above any view added
+        // by the controller.
+        if (controller.isLaunching || openingWindowSyncViewOverlay == null) {
+            transitionContainerOverlay.add(windowBackgroundLayer)
+        } else {
+            openingWindowSyncViewOverlay.add(windowBackgroundLayer)
+        }
+    }
+
+    private fun onAnimationEnd(
+        controller: Controller,
+        isExpandingFullyAbove: Boolean,
+        windowBackgroundLayer: GradientDrawable,
+        transitionContainerOverlay: ViewGroupOverlay,
+        openingWindowSyncViewOverlay: ViewOverlay?,
+        moveBackgroundLayerWhenAppVisibilityChanges: Boolean,
+    ) {
+        if (DEBUG) {
+            Log.d(TAG, "Animation ended")
+        }
+
+        // TODO(b/330672236): Post this to the main thread instead so that it does not
+        // flicker with Flexiglass enabled.
+        controller.onTransitionAnimationEnd(isExpandingFullyAbove)
+        transitionContainerOverlay.remove(windowBackgroundLayer)
+
+        if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
+            openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+        }
+    }
+
+    /** Returns whether is the controller's view should be visible with the given [timings]. */
+    private fun checkVisibility(timings: Timings, progress: Float, isLaunching: Boolean): Boolean {
+        return if (isLaunching) {
+            // The expanding view can/should be hidden once it is completely covered by the opening
+            // window.
+            getProgress(
+                timings,
+                progress,
+                timings.contentBeforeFadeOutDelay,
+                timings.contentBeforeFadeOutDuration,
+            ) < 1
+        } else {
+            // The shrinking view can/should be hidden while it is completely covered by the closing
+            // window.
+            getProgress(
+                timings,
+                progress,
+                timings.contentAfterFadeInDelay,
+                timings.contentAfterFadeInDuration,
+            ) > 0
+        }
+    }
+
+    /**
+     * If necessary, moves the background layer from the view container's overlay to the window sync
+     * view overlay, or vice versa.
+     *
+     * @return true if the background layer vwas moved, false otherwise.
+     */
+    private fun maybeMoveBackgroundLayer(
+        controller: Controller,
+        state: State,
+        windowBackgroundLayer: GradientDrawable,
+        transitionContainer: View,
+        transitionContainerOverlay: ViewGroupOverlay,
+        openingWindowSyncView: View?,
+        openingWindowSyncViewOverlay: ViewOverlay?,
+        moveBackgroundLayerWhenAppVisibilityChanges: Boolean,
+    ): Boolean {
+        if (
+            controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && !state.visible
+        ) {
+            // The expanding view is not visible, so the opening app is visible. If this is the
+            // first frame when it happens, trigger a one-off sync and move the background layer
+            // in its new container.
+            transitionContainerOverlay.remove(windowBackgroundLayer)
+            openingWindowSyncViewOverlay!!.add(windowBackgroundLayer)
+
+            ViewRootSync.synchronizeNextDraw(
+                transitionContainer,
+                openingWindowSyncView!!,
+                then = {},
+            )
+
+            return true
+        } else if (
+            !controller.isLaunching && moveBackgroundLayerWhenAppVisibilityChanges && state.visible
+        ) {
+            // The contracting view is now visible, so the closing app is not. If this is the first
+            // frame when it happens, trigger a one-off sync and move the background layer in its
+            // new container.
+            openingWindowSyncViewOverlay!!.remove(windowBackgroundLayer)
+            transitionContainerOverlay.add(windowBackgroundLayer)
+
+            ViewRootSync.synchronizeNextDraw(
+                openingWindowSyncView!!,
+                transitionContainer,
+                then = {},
+            )
+
+            return true
+        }
+
+        return false
     }
 
     /** Return whether we are expanding fully above the [transitionContainer]. */
@@ -511,6 +1028,7 @@
         fadeWindowBackgroundLayer: Boolean,
         drawHole: Boolean,
         isLaunching: Boolean,
+        useSpring: Boolean,
     ) {
         // Update position.
         transitionContainer.getLocationOnScreen(transitionContainerLocation)
@@ -532,29 +1050,53 @@
         cornerRadii[7] = state.bottomCornerRadius
         drawable.cornerRadii = cornerRadii
 
-        // We first fade in the background layer to hide the expanding view, then fade it out
-        // with SRC mode to draw a hole punch in the status bar and reveal the opening window.
-        val fadeInProgress =
-            getProgress(
-                timings,
-                linearProgress,
-                timings.contentBeforeFadeOutDelay,
-                timings.contentBeforeFadeOutDuration,
-            )
+        val interpolators: Interpolators
+        val fadeInProgress: Float
+        val fadeOutProgress: Float
+        if (useSpring) {
+            interpolators = springInterpolators!!
+            val timings = springTimings!!
+            fadeInProgress =
+                getProgressInternal(
+                    totalDuration = 1f,
+                    linearProgress,
+                    timings.contentBeforeFadeOutDelay,
+                    timings.contentBeforeFadeOutDuration,
+                )
+            fadeOutProgress =
+                getProgressInternal(
+                    totalDuration = 1f,
+                    linearProgress,
+                    timings.contentAfterFadeInDelay,
+                    timings.contentAfterFadeInDuration,
+                )
+        } else {
+            interpolators = this.interpolators
+            fadeInProgress =
+                getProgress(
+                    timings,
+                    linearProgress,
+                    timings.contentBeforeFadeOutDelay,
+                    timings.contentBeforeFadeOutDuration,
+                )
+            fadeOutProgress =
+                getProgress(
+                    timings,
+                    linearProgress,
+                    timings.contentAfterFadeInDelay,
+                    timings.contentAfterFadeInDuration,
+                )
+        }
 
+        // We first fade in the background layer to hide the expanding view, then fade it out with
+        // SRC mode to draw a hole punch in the status bar and reveal the opening window (if
+        // needed). If !isLaunching, the reverse happens.
         if (isLaunching) {
             if (fadeInProgress < 1) {
                 val alpha =
                     interpolators.contentBeforeFadeOutInterpolator.getInterpolation(fadeInProgress)
                 drawable.alpha = (alpha * 0xFF).roundToInt()
             } else if (fadeWindowBackgroundLayer) {
-                val fadeOutProgress =
-                    getProgress(
-                        timings,
-                        linearProgress,
-                        timings.contentAfterFadeInDelay,
-                        timings.contentAfterFadeInDuration,
-                    )
                 val alpha =
                     1 -
                         interpolators.contentAfterFadeInInterpolator.getInterpolation(
@@ -578,13 +1120,6 @@
                     drawable.setXfermode(SRC_MODE)
                 }
             } else {
-                val fadeOutProgress =
-                    getProgress(
-                        timings,
-                        linearProgress,
-                        timings.contentAfterFadeInDelay,
-                        timings.contentAfterFadeInDuration,
-                    )
                 val alpha =
                     1 -
                         interpolators.contentAfterFadeInInterpolator.getInterpolation(
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
index 4fe9f89..dc6e0ce 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
@@ -16,6 +16,7 @@
 
 package com.android.compose.animation
 
+import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.Easing
 import androidx.core.animation.Interpolator
 import com.android.app.animation.InterpolatorsAndroidX
@@ -59,6 +60,17 @@
     /** The linear interpolator. */
     val Linear = fromInterpolator(InterpolatorsAndroidX.LINEAR)
 
+    /**
+     * Use this easing for animating progress values coming from the back callback to get the
+     * predictive-back-typical decelerate motion.
+     *
+     * This easing is similar to [StandardDecelerate] but has a slight acceleration phase at the
+     * start.
+     *
+     * See also [InterpolatorsAndroidX.BACK_GESTURE].
+     */
+    val PredictiveBack = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+
     /** The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN. */
     val Legacy = fromInterpolator(InterpolatorsAndroidX.LEGACY)
 
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
index 37c37b0..1256641 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
@@ -16,8 +16,9 @@
 
 package com.android.compose.theme
 
-import android.annotation.ColorInt
 import android.content.Context
+import androidx.annotation.ColorRes
+import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.graphics.Color
 import com.android.internal.R
@@ -34,62 +35,45 @@
 /**
  * The Android color scheme.
  *
- * Important: Use M3 colors from MaterialTheme.colorScheme whenever possible instead. In the future,
- * most of the colors in this class will be removed in favor of their M3 counterpart.
+ * This scheme contains the Material3 colors that are not available on
+ * [androidx.compose.material3.MaterialTheme]. For other colors (e.g. primary), use
+ * `MaterialTheme.colorScheme` instead.
  */
-class AndroidColorScheme(context: Context) {
-    val onSecondaryFixedVariant = getColor(context, R.attr.materialColorOnSecondaryFixedVariant)
-    val onTertiaryFixedVariant = getColor(context, R.attr.materialColorOnTertiaryFixedVariant)
-    val surfaceContainerLowest = getColor(context, R.attr.materialColorSurfaceContainerLowest)
-    val onPrimaryFixedVariant = getColor(context, R.attr.materialColorOnPrimaryFixedVariant)
-    val onSecondaryContainer = getColor(context, R.attr.materialColorOnSecondaryContainer)
-    val onTertiaryContainer = getColor(context, R.attr.materialColorOnTertiaryContainer)
-    val surfaceContainerLow = getColor(context, R.attr.materialColorSurfaceContainerLow)
-    val onPrimaryContainer = getColor(context, R.attr.materialColorOnPrimaryContainer)
-    val secondaryFixedDim = getColor(context, R.attr.materialColorSecondaryFixedDim)
-    val onErrorContainer = getColor(context, R.attr.materialColorOnErrorContainer)
-    val onSecondaryFixed = getColor(context, R.attr.materialColorOnSecondaryFixed)
-    val onSurfaceInverse = getColor(context, R.attr.materialColorOnSurfaceInverse)
-    val tertiaryFixedDim = getColor(context, R.attr.materialColorTertiaryFixedDim)
-    val onTertiaryFixed = getColor(context, R.attr.materialColorOnTertiaryFixed)
-    val primaryFixedDim = getColor(context, R.attr.materialColorPrimaryFixedDim)
-    val secondaryContainer = getColor(context, R.attr.materialColorSecondaryContainer)
-    val errorContainer = getColor(context, R.attr.materialColorErrorContainer)
-    val onPrimaryFixed = getColor(context, R.attr.materialColorOnPrimaryFixed)
-    val primaryInverse = getColor(context, R.attr.materialColorPrimaryInverse)
-    val secondaryFixed = getColor(context, R.attr.materialColorSecondaryFixed)
-    val surfaceInverse = getColor(context, R.attr.materialColorSurfaceInverse)
-    val surfaceVariant = getColor(context, R.attr.materialColorSurfaceVariant)
-    val tertiaryContainer = getColor(context, R.attr.materialColorTertiaryContainer)
-    val tertiaryFixed = getColor(context, R.attr.materialColorTertiaryFixed)
-    val primaryContainer = getColor(context, R.attr.materialColorPrimaryContainer)
-    val onBackground = getColor(context, R.attr.materialColorOnBackground)
-    val primaryFixed = getColor(context, R.attr.materialColorPrimaryFixed)
-    val onSecondary = getColor(context, R.attr.materialColorOnSecondary)
-    val onTertiary = getColor(context, R.attr.materialColorOnTertiary)
-    val surfaceDim = getColor(context, R.attr.materialColorSurfaceDim)
-    val surfaceBright = getColor(context, R.attr.materialColorSurfaceBright)
-    val error = getColor(context, R.attr.materialColorError)
-    val onError = getColor(context, R.attr.materialColorOnError)
-    val surface = getColor(context, R.attr.materialColorSurface)
-    val surfaceContainerHigh = getColor(context, R.attr.materialColorSurfaceContainerHigh)
-    val surfaceContainerHighest = getColor(context, R.attr.materialColorSurfaceContainerHighest)
-    val onSurfaceVariant = getColor(context, R.attr.materialColorOnSurfaceVariant)
-    val outline = getColor(context, R.attr.materialColorOutline)
-    val outlineVariant = getColor(context, R.attr.materialColorOutlineVariant)
-    val onPrimary = getColor(context, R.attr.materialColorOnPrimary)
-    val onSurface = getColor(context, R.attr.materialColorOnSurface)
-    val surfaceContainer = getColor(context, R.attr.materialColorSurfaceContainer)
-    val primary = getColor(context, R.attr.materialColorPrimary)
-    val secondary = getColor(context, R.attr.materialColorSecondary)
-    val tertiary = getColor(context, R.attr.materialColorTertiary)
-
+@Immutable
+class AndroidColorScheme(
+    val primaryFixed: Color,
+    val primaryFixedDim: Color,
+    val onPrimaryFixed: Color,
+    val onPrimaryFixedVariant: Color,
+    val secondaryFixed: Color,
+    val secondaryFixedDim: Color,
+    val onSecondaryFixed: Color,
+    val onSecondaryFixedVariant: Color,
+    val tertiaryFixed: Color,
+    val tertiaryFixedDim: Color,
+    val onTertiaryFixed: Color,
+    val onTertiaryFixedVariant: Color,
+) {
     companion object {
-        internal fun getColor(context: Context, attr: Int): Color {
-            val ta = context.obtainStyledAttributes(intArrayOf(attr))
-            @ColorInt val color = ta.getColor(0, 0)
-            ta.recycle()
-            return Color(color)
+        internal fun color(context: Context, @ColorRes id: Int): Color {
+            return Color(context.resources.getColor(id, context.theme))
+        }
+
+        operator fun invoke(context: Context): AndroidColorScheme {
+            return AndroidColorScheme(
+                primaryFixed = color(context, R.color.system_primary_fixed),
+                primaryFixedDim = color(context, R.color.system_primary_fixed_dim),
+                onPrimaryFixed = color(context, R.color.system_on_primary_fixed),
+                onPrimaryFixedVariant = color(context, R.color.system_on_primary_fixed_variant),
+                secondaryFixed = color(context, R.color.system_secondary_fixed),
+                secondaryFixedDim = color(context, R.color.system_secondary_fixed_dim),
+                onSecondaryFixed = color(context, R.color.system_on_secondary_fixed),
+                onSecondaryFixedVariant = color(context, R.color.system_on_secondary_fixed_variant),
+                tertiaryFixed = color(context, R.color.system_tertiary_fixed),
+                tertiaryFixedDim = color(context, R.color.system_tertiary_fixed_dim),
+                onTertiaryFixed = color(context, R.color.system_on_tertiary_fixed),
+                onTertiaryFixedVariant = color(context, R.color.system_on_tertiary_fixed_variant),
+            )
         }
     }
 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
index 5dbaff6..a499447 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
@@ -17,6 +17,8 @@
 package com.android.compose.theme
 
 import android.annotation.AttrRes
+import android.annotation.ColorInt
+import android.content.Context
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.ui.graphics.Color
@@ -26,5 +28,13 @@
 @Composable
 @ReadOnlyComposable
 fun colorAttr(@AttrRes attribute: Int): Color {
-    return AndroidColorScheme.getColor(LocalContext.current, attribute)
+    return colorAttr(LocalContext.current, attribute)
+}
+
+/** Return the [Color] from the given [attribute]. */
+fun colorAttr(context: Context, @AttrRes attr: Int): Color {
+    val ta = context.obtainStyledAttributes(intArrayOf(attr))
+    @ColorInt val color = ta.getColor(0, 0)
+    ta.recycle()
+    return Color(color)
 }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
index 0661870..d31d7aa 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
@@ -16,7 +16,9 @@
 
 package com.android.compose.theme
 
+import android.content.Context
 import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.ColorScheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.dynamicDarkColorScheme
 import androidx.compose.material3.dynamicLightColorScheme
@@ -24,6 +26,7 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
+import com.android.compose.theme.AndroidColorScheme.Companion.color
 import com.android.compose.theme.typography.TypeScaleTokens
 import com.android.compose.theme.typography.TypefaceNames
 import com.android.compose.theme.typography.TypefaceTokens
@@ -31,23 +34,15 @@
 import com.android.compose.theme.typography.platformTypography
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.compose.windowsizeclass.calculateWindowSizeClass
+import com.android.internal.R
 
 /** The Material 3 theme that should wrap all Platform Composables. */
 @Composable
-fun PlatformTheme(
-    isDarkTheme: Boolean = isSystemInDarkTheme(),
-    content: @Composable () -> Unit,
-) {
+fun PlatformTheme(isDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
     val context = LocalContext.current
 
-    // TODO(b/230605885): Define our color scheme.
-    val colorScheme =
-        if (isDarkTheme) {
-            dynamicDarkColorScheme(context)
-        } else {
-            dynamicLightColorScheme(context)
-        }
-    val androidColorScheme = AndroidColorScheme(context)
+    val colorScheme = remember(context, isDarkTheme) { platformColorScheme(isDarkTheme, context) }
+    val androidColorScheme = remember(context) { AndroidColorScheme(context) }
     val typefaceNames = remember(context) { TypefaceNames.get(context) }
     val typography =
         remember(typefaceNames) {
@@ -55,12 +50,31 @@
         }
     val windowSizeClass = calculateWindowSizeClass()
 
-    MaterialTheme(colorScheme, typography = typography) {
+    MaterialTheme(colorScheme = colorScheme, typography = typography) {
         CompositionLocalProvider(
             LocalAndroidColorScheme provides androidColorScheme,
             LocalWindowSizeClass provides windowSizeClass,
-        ) {
-            content()
-        }
+            content = content,
+        )
+    }
+}
+
+private fun platformColorScheme(isDarkTheme: Boolean, context: Context): ColorScheme {
+    return if (isDarkTheme) {
+        dynamicDarkColorScheme(context)
+            .copy(
+                error = color(context, R.color.system_error_dark),
+                onError = color(context, R.color.system_on_error_dark),
+                errorContainer = color(context, R.color.system_error_container_dark),
+                onErrorContainer = color(context, R.color.system_on_error_container_dark),
+            )
+    } else {
+        dynamicLightColorScheme(context)
+            .copy(
+                error = color(context, R.color.system_error_light),
+                onError = color(context, R.color.system_on_error_light),
+                errorContainer = color(context, R.color.system_error_container_light),
+                onErrorContainer = color(context, R.color.system_on_error_container_light),
+            )
     }
 }
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 6e7a142..6a824d8 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -27,7 +27,6 @@
     name: "PlatformComposeCoreTests",
     manifest: "AndroidManifest.xml",
     test_suites: ["device-tests"],
-    sdk_version: "current",
     certificate: "platform",
 
     srcs: [
diff --git a/packages/SystemUI/compose/core/tests/AndroidManifest.xml b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
index 1016340..28f80d4 100644
--- a/packages/SystemUI/compose/core/tests/AndroidManifest.xml
+++ b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
@@ -19,6 +19,11 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
+
+        <activity
+            android:name="androidx.activity.ComponentActivity"
+            android:theme="@android:style/Theme.DeviceDefault.DayNight"
+            android:exported="true" />
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
index 23538e3..de021a0 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/PlatformThemeTest.kt
@@ -16,11 +16,21 @@
 
 package com.android.compose.theme
 
+import android.content.Context
+import androidx.annotation.AttrRes
+import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.R
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Assert.assertThrows
 import org.junit.Rule
 import org.junit.Test
@@ -54,4 +64,145 @@
             }
         }
     }
+
+    @Test
+    fun testMaterialColorsMatchAttributeValue() {
+        val colorValues = mutableListOf<ColorValue>()
+
+        fun onLaunch(colorScheme: ColorScheme, context: Context) {
+            fun addValue(name: String, materialValue: Color, @AttrRes attr: Int) {
+                colorValues.add(ColorValue(name, materialValue, colorAttr(context, attr)))
+            }
+
+            addValue("primary", colorScheme.primary, R.attr.materialColorPrimary)
+            addValue("onPrimary", colorScheme.onPrimary, R.attr.materialColorOnPrimary)
+            addValue(
+                "primaryContainer",
+                colorScheme.primaryContainer,
+                R.attr.materialColorPrimaryContainer,
+            )
+            addValue(
+                "onPrimaryContainer",
+                colorScheme.onPrimaryContainer,
+                R.attr.materialColorOnPrimaryContainer,
+            )
+            addValue(
+                "inversePrimary",
+                colorScheme.inversePrimary,
+                R.attr.materialColorPrimaryInverse,
+            )
+            addValue("secondary", colorScheme.secondary, R.attr.materialColorSecondary)
+            addValue("onSecondary", colorScheme.onSecondary, R.attr.materialColorOnSecondary)
+            addValue(
+                "secondaryContainer",
+                colorScheme.secondaryContainer,
+                R.attr.materialColorSecondaryContainer,
+            )
+            addValue(
+                "onSecondaryContainer",
+                colorScheme.onSecondaryContainer,
+                R.attr.materialColorOnSecondaryContainer,
+            )
+            addValue("tertiary", colorScheme.tertiary, R.attr.materialColorTertiary)
+            addValue("onTertiary", colorScheme.onTertiary, R.attr.materialColorOnTertiary)
+            addValue(
+                "tertiaryContainer",
+                colorScheme.tertiaryContainer,
+                R.attr.materialColorTertiaryContainer,
+            )
+            addValue(
+                "onTertiaryContainer",
+                colorScheme.onTertiaryContainer,
+                R.attr.materialColorOnTertiaryContainer,
+            )
+            addValue("onBackground", colorScheme.onBackground, R.attr.materialColorOnBackground)
+            addValue("surface", colorScheme.surface, R.attr.materialColorSurface)
+            addValue("onSurface", colorScheme.onSurface, R.attr.materialColorOnSurface)
+            addValue(
+                "surfaceVariant",
+                colorScheme.surfaceVariant,
+                R.attr.materialColorSurfaceVariant,
+            )
+            addValue(
+                "onSurfaceVariant",
+                colorScheme.onSurfaceVariant,
+                R.attr.materialColorOnSurfaceVariant,
+            )
+            addValue(
+                "inverseSurface",
+                colorScheme.inverseSurface,
+                R.attr.materialColorSurfaceInverse,
+            )
+            addValue(
+                "inverseOnSurface",
+                colorScheme.inverseOnSurface,
+                R.attr.materialColorOnSurfaceInverse,
+            )
+            addValue("error", colorScheme.error, R.attr.materialColorError)
+            addValue("onError", colorScheme.onError, R.attr.materialColorOnError)
+            addValue(
+                "errorContainer",
+                colorScheme.errorContainer,
+                R.attr.materialColorErrorContainer,
+            )
+            addValue(
+                "onErrorContainer",
+                colorScheme.onErrorContainer,
+                R.attr.materialColorOnErrorContainer,
+            )
+            addValue("outline", colorScheme.outline, R.attr.materialColorOutline)
+            addValue(
+                "outlineVariant",
+                colorScheme.outlineVariant,
+                R.attr.materialColorOutlineVariant,
+            )
+            addValue("surfaceBright", colorScheme.surfaceBright, R.attr.materialColorSurfaceBright)
+            addValue("surfaceDim", colorScheme.surfaceDim, R.attr.materialColorSurfaceDim)
+            addValue(
+                "surfaceContainer",
+                colorScheme.surfaceContainer,
+                R.attr.materialColorSurfaceContainer,
+            )
+            addValue(
+                "surfaceContainerHigh",
+                colorScheme.surfaceContainerHigh,
+                R.attr.materialColorSurfaceContainerHigh,
+            )
+            addValue(
+                "surfaceContainerHighest",
+                colorScheme.surfaceContainerHighest,
+                R.attr.materialColorSurfaceContainerHighest,
+            )
+            addValue(
+                "surfaceContainerLow",
+                colorScheme.surfaceContainerLow,
+                R.attr.materialColorSurfaceContainerLow,
+            )
+            addValue(
+                "surfaceContainerLowest",
+                colorScheme.surfaceContainerLowest,
+                R.attr.materialColorSurfaceContainerLowest,
+            )
+        }
+
+        composeRule.setContent {
+            PlatformTheme {
+                val colorScheme = MaterialTheme.colorScheme
+                val context = LocalContext.current
+
+                LaunchedEffect(Unit) { onLaunch(colorScheme, context) }
+            }
+        }
+
+        assertThat(colorValues).hasSize(33)
+        colorValues.forEach { colorValue ->
+            assertWithMessage(
+                    "MaterialTheme.colorScheme.${colorValue.name} matches attribute color"
+                )
+                .that(colorValue.materialValue)
+                .isEqualTo(colorValue.attrValue)
+        }
+    }
+
+    private data class ColorValue(val name: String, val materialValue: Color, val attrValue: Color)
 }
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
index 7dc2901..c1c3b1f 100644
--- 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
@@ -28,6 +28,7 @@
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
 import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.layout.Arrangement
@@ -46,7 +47,6 @@
 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
@@ -64,6 +64,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.key.onKeyEvent
@@ -72,6 +73,9 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpOffset
@@ -88,7 +92,6 @@
 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.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
@@ -131,7 +134,7 @@
     layout: BouncerSceneLayout,
     viewModel: BouncerSceneContentViewModel,
     dialogFactory: BouncerDialogFactory,
-    modifier: Modifier
+    modifier: Modifier,
 ) {
     Box(
         // Allows the content within each of the layouts to react to the appearance and
@@ -140,31 +143,17 @@
         // Despite the keyboard only being part of the password bouncer, adding it at this level is
         // both necessary to properly handle the keyboard in all layouts and harmless in cases when
         // the keyboard isn't used (like the PIN or pattern auth methods).
-        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent),
+        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent)
     ) {
         when (layout) {
-            BouncerSceneLayout.STANDARD_BOUNCER ->
-                StandardLayout(
-                    viewModel = viewModel,
-                )
+            BouncerSceneLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel)
             BouncerSceneLayout.BESIDE_USER_SWITCHER ->
-                BesideUserSwitcherLayout(
-                    viewModel = viewModel,
-                )
-            BouncerSceneLayout.BELOW_USER_SWITCHER ->
-                BelowUserSwitcherLayout(
-                    viewModel = viewModel,
-                )
-            BouncerSceneLayout.SPLIT_BOUNCER ->
-                SplitLayout(
-                    viewModel = viewModel,
-                )
+                BesideUserSwitcherLayout(viewModel = viewModel)
+            BouncerSceneLayout.BELOW_USER_SWITCHER -> BelowUserSwitcherLayout(viewModel = viewModel)
+            BouncerSceneLayout.SPLIT_BOUNCER -> SplitLayout(viewModel = viewModel)
         }
 
-        Dialog(
-            bouncerViewModel = viewModel,
-            dialogFactory = dialogFactory,
-        )
+        Dialog(bouncerViewModel = viewModel, dialogFactory = dialogFactory)
     }
 }
 
@@ -173,31 +162,19 @@
  * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
  */
 @Composable
-private fun StandardLayout(
-    viewModel: BouncerSceneContentViewModel,
-    modifier: Modifier = Modifier,
-) {
+private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
     val isHeightExpanded =
         LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
 
     FoldAware(
-        modifier =
-            modifier.padding(
-                start = 32.dp,
-                top = 92.dp,
-                end = 32.dp,
-                bottom = 48.dp,
-            ),
+        modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 48.dp),
         viewModel = viewModel,
         aboveFold = {
             Column(
                 horizontalAlignment = Alignment.CenterHorizontally,
                 modifier = Modifier.fillMaxWidth(),
             ) {
-                StatusMessage(
-                    viewModel = viewModel.message,
-                    modifier = Modifier,
-                )
+                StatusMessage(viewModel = viewModel.message, modifier = Modifier)
 
                 OutputArea(
                     viewModel = viewModel,
@@ -210,9 +187,7 @@
                 horizontalAlignment = Alignment.CenterHorizontally,
                 modifier = Modifier.fillMaxWidth(),
             ) {
-                Box(
-                    modifier = Modifier.weight(1f),
-                ) {
+                Box(modifier = Modifier.weight(1f)) {
                     InputArea(
                         viewModel = viewModel,
                         pinButtonRowVerticalSpacing = 12.dp,
@@ -221,10 +196,7 @@
                     )
                 }
 
-                ActionArea(
-                    viewModel = viewModel,
-                    modifier = Modifier.padding(top = 48.dp),
-                )
+                ActionArea(viewModel = viewModel, modifier = Modifier.padding(top = 48.dp))
             }
         },
     )
@@ -235,10 +207,7 @@
  * by double-tapping on the side.
  */
 @Composable
-private fun SplitLayout(
-    viewModel: BouncerSceneContentViewModel,
-    modifier: Modifier = Modifier,
-) {
+private fun SplitLayout(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
     val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
 
     Row(
@@ -248,12 +217,10 @@
                 .padding(
                     horizontal = 24.dp,
                     vertical = if (authMethod is PasswordBouncerViewModel) 24.dp else 48.dp,
-                ),
+                )
     ) {
         // Left side (in left-to-right locales).
-        Box(
-            modifier = Modifier.fillMaxHeight().weight(1f),
-        ) {
+        Box(modifier = Modifier.fillMaxHeight().weight(1f)) {
             when (authMethod) {
                 is PinBouncerViewModel -> {
                     StatusMessage(
@@ -263,7 +230,7 @@
                     OutputArea(
                         viewModel = viewModel,
                         modifier =
-                            Modifier.align(Alignment.Center).sysuiResTag("bouncer_text_entry")
+                            Modifier.align(Alignment.Center).sysuiResTag("bouncer_text_entry"),
                     )
 
                     ActionArea(
@@ -293,9 +260,7 @@
         }
 
         // Right side (in right-to-left locales).
-        Box(
-            modifier = Modifier.fillMaxHeight().weight(1f),
-        ) {
+        Box(modifier = Modifier.fillMaxHeight().weight(1f)) {
             when (authMethod) {
                 is PinBouncerViewModel,
                 is PatternBouncerViewModel -> {
@@ -311,13 +276,11 @@
                         horizontalAlignment = Alignment.CenterHorizontally,
                         modifier = Modifier.fillMaxWidth().align(Alignment.Center),
                     ) {
-                        StatusMessage(
-                            viewModel = viewModel.message,
-                        )
+                        StatusMessage(viewModel = viewModel.message)
                         OutputArea(
                             viewModel = viewModel,
                             modifier =
-                                Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry")
+                                Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry"),
                         )
                     }
                 }
@@ -365,7 +328,7 @@
                 .padding(
                     top = if (isHeightExpanded) 128.dp else 96.dp,
                     bottom = if (isHeightExpanded) 128.dp else 48.dp,
-                ),
+                )
     ) {
         LaunchedEffect(isSwapped) { swapAnimationEnd = false }
         val animatedOffset by
@@ -419,14 +382,12 @@
             aboveFold = {
                 Column(
                     horizontalAlignment = Alignment.CenterHorizontally,
-                    modifier = Modifier.fillMaxWidth()
+                    modifier = Modifier.fillMaxWidth(),
                 ) {
-                    StatusMessage(
-                        viewModel = viewModel.message,
-                    )
+                    StatusMessage(viewModel = viewModel.message)
                     OutputArea(
                         viewModel = viewModel,
-                        modifier = Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry")
+                        modifier = Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry"),
                     )
                 }
             },
@@ -444,7 +405,7 @@
                     Box(
                         modifier =
                             Modifier.weight(1f)
-                                .padding(top = (if (addSpacingBetweenOutputAndInput) 24 else 0).dp),
+                                .padding(top = (if (addSpacingBetweenOutputAndInput) 24 else 0).dp)
                     ) {
                         InputArea(
                             viewModel = viewModel,
@@ -470,16 +431,8 @@
     viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
-    Column(
-        modifier =
-            modifier.padding(
-                vertical = 128.dp,
-            )
-    ) {
-        UserSwitcher(
-            viewModel = viewModel,
-            modifier = Modifier.fillMaxWidth(),
-        )
+    Column(modifier = modifier.padding(vertical = 128.dp)) {
+        UserSwitcher(viewModel = viewModel, modifier = Modifier.fillMaxWidth())
 
         Spacer(Modifier.weight(1f))
 
@@ -488,9 +441,7 @@
                 horizontalAlignment = Alignment.CenterHorizontally,
                 modifier = Modifier.fillMaxWidth(),
             ) {
-                StatusMessage(
-                    viewModel = viewModel.message,
-                )
+                StatusMessage(viewModel = viewModel.message)
                 OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
 
                 InputArea(
@@ -500,10 +451,7 @@
                     modifier = Modifier.padding(top = 128.dp),
                 )
 
-                ActionArea(
-                    viewModel = viewModel,
-                    modifier = Modifier.padding(top = 48.dp),
-                )
+                ActionArea(viewModel = viewModel, modifier = Modifier.padding(top = 48.dp))
             }
         }
     }
@@ -533,19 +481,11 @@
 
     SceneTransitionLayout(state, modifier = modifier) {
         scene(SceneKeys.ContiguousSceneKey) {
-            FoldableScene(
-                aboveFold = aboveFold,
-                belowFold = belowFold,
-                isSplit = false,
-            )
+            FoldableScene(aboveFold = aboveFold, belowFold = belowFold, isSplit = false)
         }
 
         scene(SceneKeys.SplitSceneKey) {
-            FoldableScene(
-                aboveFold = aboveFold,
-                belowFold = belowFold,
-                isSplit = true,
-            )
+            FoldableScene(aboveFold = aboveFold, belowFold = belowFold, isSplit = true)
         }
     }
 }
@@ -562,9 +502,7 @@
             R.dimen.motion_layout_half_fold_bouncer_height_ratio
         )
 
-    Column(
-        modifier = modifier.fillMaxHeight(),
-    ) {
+    Column(modifier = modifier.fillMaxHeight()) {
         // Content above the fold, when split on a foldable device in a "table top" posture:
         Box(
             modifier =
@@ -575,7 +513,7 @@
                         } else {
                             Modifier
                         }
-                    ),
+                    )
         ) {
             aboveFold()
         }
@@ -590,7 +528,7 @@
                         } else {
                             1f
                         }
-                    ),
+                    )
         ) {
             belowFold()
         }
@@ -598,10 +536,7 @@
 }
 
 @Composable
-private fun StatusMessage(
-    viewModel: BouncerMessageViewModel,
-    modifier: Modifier = Modifier,
-) {
+private fun StatusMessage(viewModel: BouncerMessageViewModel, modifier: Modifier = Modifier) {
     val message: MessageViewModel? by viewModel.message.collectAsStateWithLifecycle()
 
     DisposableEffect(Unit) {
@@ -634,7 +569,7 @@
                     fontSize = 14.sp,
                     lineHeight = 20.sp,
                     overflow = TextOverflow.Ellipsis,
-                    maxLines = 2
+                    maxLines = 2,
                 )
             }
         }
@@ -647,22 +582,19 @@
  * For example, this can be the PIN shapes or password text field.
  */
 @Composable
-private fun OutputArea(
-    viewModel: BouncerSceneContentViewModel,
-    modifier: Modifier = Modifier,
-) {
+private fun OutputArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
         viewModel.authMethodViewModel.collectAsStateWithLifecycle()
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel ->
             PinInputDisplay(
                 viewModel = nonNullViewModel,
-                modifier = modifier.sysuiResTag("bouncer_text_entry")
+                modifier = modifier.sysuiResTag("bouncer_text_entry"),
             )
         is PasswordBouncerViewModel ->
             PasswordBouncer(
                 viewModel = nonNullViewModel,
-                modifier = modifier.sysuiResTag("bouncer_text_entry")
+                modifier = modifier.sysuiResTag("bouncer_text_entry"),
             )
         else -> Unit
     }
@@ -703,10 +635,7 @@
 }
 
 @Composable
-private fun ActionArea(
-    viewModel: BouncerSceneContentViewModel,
-    modifier: Modifier = Modifier,
-) {
+private fun ActionArea(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
     val actionButton: BouncerActionButtonModel? by
         viewModel.actionButton.collectAsStateWithLifecycle()
     val appearFadeInAnimatable = remember { Animatable(0f) }
@@ -722,7 +651,7 @@
                         durationMillis = 450,
                         delayMillis = 133,
                         easing = Easings.LegacyDecelerate,
-                    )
+                    ),
             )
         }
         LaunchedEffect(Unit) {
@@ -733,39 +662,35 @@
                         durationMillis = 450,
                         delayMillis = 133,
                         easing = Easings.StandardDecelerate,
-                    )
+                    ),
             )
         }
 
         Box(
             modifier =
-                modifier.graphicsLayer {
-                    // Translate the button up from an initially pushed-down position:
-                    translationY = (1 - appearMoveAnimatable.value) * appearAnimationInitialOffset
-                    // Fade the button in:
-                    alpha = appearFadeInAnimatable.value
-                },
+                modifier
+                    .graphicsLayer {
+                        // Translate the button up from an initially pushed-down position:
+                        translationY =
+                            (1 - appearMoveAnimatable.value) * appearAnimationInitialOffset
+                        // Fade the button in:
+                        alpha = appearFadeInAnimatable.value
+                    }
+                    .height(56.dp)
+                    .clip(ButtonDefaults.shape)
+                    .background(color = MaterialTheme.colorScheme.tertiaryContainer)
+                    .semantics { role = Role.Button }
+                    .combinedClickable(
+                        onClick = { actionButtonViewModel.onClick() },
+                        onLongClick = actionButtonViewModel.onLongClick?.let { { it.invoke() } },
+                    )
         ) {
-            Button(
-                onClick = actionButtonViewModel.onClick,
-                modifier =
-                    Modifier.height(56.dp).thenIf(actionButtonViewModel.onLongClick != null) {
-                        Modifier.combinedClickable(
-                            onClick = actionButtonViewModel.onClick,
-                            onLongClick = actionButtonViewModel.onLongClick,
-                        )
-                    },
-                colors =
-                    ButtonDefaults.buttonColors(
-                        containerColor = MaterialTheme.colorScheme.tertiaryContainer,
-                        contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
-                    ),
-            ) {
-                Text(
-                    text = actionButtonViewModel.label,
-                    style = MaterialTheme.typography.bodyMedium,
-                )
-            }
+            Text(
+                text = actionButtonViewModel.label,
+                style = MaterialTheme.typography.bodyMedium,
+                color = MaterialTheme.colorScheme.onTertiaryContainer,
+                modifier = Modifier.align(Alignment.Center).padding(ButtonDefaults.ContentPadding),
+            )
         }
     }
 }
@@ -800,15 +725,10 @@
 
 /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
 @Composable
-private fun UserSwitcher(
-    viewModel: BouncerSceneContentViewModel,
-    modifier: Modifier = Modifier,
-) {
+private fun UserSwitcher(viewModel: BouncerSceneContentViewModel, modifier: Modifier = Modifier) {
     if (!viewModel.isUserSwitcherVisible) {
         // Take up the same space as the user switcher normally would, but with nothing inside it.
-        Box(
-            modifier = modifier,
-        )
+        Box(modifier = modifier)
         return
     }
 
@@ -891,7 +811,7 @@
     MaterialTheme(
         colorScheme =
             MaterialTheme.colorScheme.copy(
-                surface = MaterialTheme.colorScheme.surfaceContainerHighest,
+                surface = MaterialTheme.colorScheme.surfaceContainerHighest
             ),
         shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(28.dp)),
     ) {
@@ -932,9 +852,7 @@
  * 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 {
+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.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 571b366..6d30398 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -13,6 +13,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
@@ -44,8 +45,6 @@
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.compose.animation.scene.transitions
-import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.internal.R.attr.focusable
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -270,7 +269,7 @@
 /** Experimental hub background, static linear gradient */
 @Composable
 private fun BoxScope.StaticLinearGradient() {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     Box(
         Modifier.matchParentSize()
             .background(
@@ -283,7 +282,7 @@
 /** Experimental hub background, animated linear gradient */
 @Composable
 private fun BoxScope.AnimatedLinearGradient() {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     Box(
         Modifier.matchParentSize()
             .background(colors.primary)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 6fca178..9392b1a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -19,12 +19,12 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
 import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
 import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
@@ -71,7 +71,7 @@
                     }
                     with(lockSection) {
                         LockIcon(
-                            overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
+                            overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
                             modifier = Modifier.element(Communal.Elements.LockIcon)
                         )
                     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 7fb4c53..8b0daf6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.ui.compose
 
+import android.appwidget.AppWidgetProviderInfo
 import android.content.Context
 import android.content.res.Configuration
 import android.graphics.drawable.Icon
@@ -24,7 +25,6 @@
 import android.view.MotionEvent
 import android.widget.FrameLayout
 import android.widget.RemoteViews
-import androidx.annotation.VisibleForTesting
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.Spring
@@ -149,9 +149,11 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.times
 import androidx.compose.ui.util.fastAll
@@ -161,7 +163,6 @@
 import androidx.window.layout.WindowMetricsCalculator
 import com.android.compose.animation.Easings.Emphasized
 import com.android.compose.modifiers.thenIf
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
 import com.android.internal.R.dimen.system_app_widget_background_radius
 import com.android.systemui.Flags
@@ -184,6 +185,9 @@
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.roundToInt
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 
@@ -387,13 +391,19 @@
                             contentOffset = contentOffset,
                             screenWidth = screenWidth,
                             setGridCoordinates = { gridCoordinates = it },
-                            updateDragPositionForRemove = { offset ->
-                                isPointerWithinEnabledRemoveButton(
-                                    removeEnabled = removeButtonEnabled,
-                                    offset =
-                                        gridCoordinates?.let { it.positionInWindow() + offset },
-                                    containerToCheck = removeButtonCoordinates,
-                                )
+                            updateDragPositionForRemove = { boundingBox ->
+                                val gridOffset = gridCoordinates?.positionInWindow()
+                                val removeButtonCenter =
+                                    removeButtonCoordinates?.boundsInWindow()?.center
+                                removeButtonEnabled &&
+                                    gridOffset != null &&
+                                    removeButtonCenter != null &&
+                                    boundingBox
+                                        // The bounding box is relative to the grid, so we need to
+                                        // normalize it by adding the grid offset and the content
+                                        // offset.
+                                        .translate((gridOffset + contentOffset).round())
+                                        .contains(removeButtonCenter.round())
                             },
                             gridState = gridState,
                             contentListState = contentListState,
@@ -473,7 +483,7 @@
             if (showBottomSheet) {
                 val scope = rememberCoroutineScope()
                 val sheetState = rememberModalBottomSheetState()
-                val colors = LocalAndroidColorScheme.current
+                val colors = MaterialTheme.colorScheme
 
                 ModalBottomSheet(
                     onDismissRequest = viewModel::onDisclaimerDismissed,
@@ -501,7 +511,7 @@
 
 @Composable
 private fun DisclaimerBottomSheetContent(onButtonClicked: () -> Unit) {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
 
     Column(
         modifier = Modifier.fillMaxWidth().padding(horizontal = 32.dp, vertical = 24.dp),
@@ -645,11 +655,13 @@
 @Composable
 private fun ResizableItemFrameWrapper(
     key: String,
+    currentSpan: GridItemSpan,
     gridState: LazyGridState,
-    minItemSpan: Int,
     gridContentPadding: PaddingValues,
     verticalArrangement: Arrangement.Vertical,
     enabled: Boolean,
+    minHeightPx: Int,
+    maxHeightPx: Int,
     modifier: Modifier = Modifier,
     alpha: () -> Float = { 1f },
     onResize: (info: ResizeInfo) -> Unit = {},
@@ -660,20 +672,48 @@
     } else {
         ResizableItemFrame(
             key = key,
+            currentSpan = currentSpan,
             gridState = gridState,
-            minItemSpan = minItemSpan,
             gridContentPadding = gridContentPadding,
             verticalArrangement = verticalArrangement,
             enabled = enabled,
             alpha = alpha,
             modifier = modifier,
             onResize = onResize,
+            minHeightPx = minHeightPx,
+            maxHeightPx = maxHeightPx,
+            resizeMultiple = CommunalContentSize.HALF.span,
         ) {
             content(Modifier)
         }
     }
 }
 
+@Composable
+fun calculateWidgetSize(item: CommunalContentModel, isResizable: Boolean): WidgetSizeInfo {
+    val density = LocalDensity.current
+
+    return if (isResizable && item is CommunalContentModel.WidgetContent.Widget) {
+        with(density) {
+            val minHeightPx =
+                (min(item.providerInfo.minResizeHeight, item.providerInfo.minHeight)
+                    .coerceAtLeast(CommunalContentSize.HALF.dp().toPx().roundToInt()))
+
+            val maxHeightPx =
+                (if (item.providerInfo.maxResizeHeight > 0) {
+                        max(item.providerInfo.maxResizeHeight, item.providerInfo.minHeight)
+                    } else {
+                        Int.MAX_VALUE
+                    })
+                    .coerceIn(minHeightPx, CommunalContentSize.FULL.dp().toPx().roundToInt())
+
+            WidgetSizeInfo(minHeightPx, maxHeightPx)
+        }
+    } else {
+        WidgetSizeInfo(0, Int.MAX_VALUE)
+    }
+}
+
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 private fun BoxScope.CommunalHubLazyGrid(
@@ -686,7 +726,7 @@
     gridState: LazyGridState,
     contentListState: ContentListState,
     setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
-    updateDragPositionForRemove: (offset: Offset) -> Boolean,
+    updateDragPositionForRemove: (boundingBox: IntRect) -> Boolean,
     widgetConfigurator: WidgetConfigurator?,
     interactionHandler: RemoteViews.InteractionHandler?,
     widgetSection: CommunalAppWidgetSection,
@@ -748,6 +788,12 @@
             val size = SizeF(Dimensions.CardWidth.value, item.size.dp().value)
             val selected = item.key == selectedKey.value
             val dpSize = DpSize(size.width.dp, size.height.dp)
+            val isResizable =
+                if (item is CommunalContentModel.WidgetContent.Widget) {
+                    item.providerInfo.resizeMode and AppWidgetProviderInfo.RESIZE_VERTICAL != 0
+                } else {
+                    false
+                }
 
             if (viewModel.isEditMode && dragDropState != null) {
                 val outlineAlpha by
@@ -756,33 +802,36 @@
                         animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
                         label = "Widget resizing outline alpha",
                     )
+                val widgetSizeInfo = calculateWidgetSize(item, isResizable)
                 ResizableItemFrameWrapper(
                     key = item.key,
+                    currentSpan = GridItemSpan(item.size.span),
                     gridState = gridState,
-                    minItemSpan = CommunalContentSize.HALF.span,
                     gridContentPadding = contentPadding,
                     verticalArrangement = itemArrangement,
                     enabled = selected,
                     alpha = { outlineAlpha },
                     modifier =
                         Modifier.requiredSize(dpSize).thenIf(
-                            dragDropState.draggingItemIndex != index
+                            dragDropState.draggingItemKey != item.key
                         ) {
                             Modifier.animateItem(
                                 placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
                             )
                         },
                     onResize = { resizeInfo -> contentListState.resize(index, resizeInfo) },
+                    minHeightPx = widgetSizeInfo.minHeightPx,
+                    maxHeightPx = widgetSizeInfo.maxHeightPx,
                 ) { modifier ->
                     DraggableItem(
                         modifier = modifier,
                         dragDropState = dragDropState,
                         selected = selected,
                         enabled = item.isWidgetContent(),
-                        index = index,
+                        key = item.key,
                     ) { isDragging ->
                         CommunalContent(
-                            modifier = Modifier.fillMaxSize(),
+                            modifier = Modifier.requiredSize(dpSize),
                             model = item,
                             viewModel = viewModel,
                             size = size,
@@ -817,7 +866,7 @@
  */
 @Composable
 private fun EmptyStateCta(contentPadding: PaddingValues, viewModel: BaseCommunalViewModel) {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     Card(
         modifier = Modifier.height(hubDimensions.GridHeight).padding(contentPadding),
         colors = CardDefaults.cardColors(containerColor = Color.Transparent),
@@ -963,7 +1012,7 @@
     modifier: Modifier = Modifier,
     content: @Composable RowScope.() -> Unit,
 ) {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     AnimatedVisibility(
         visible = isPrimary,
         modifier = modifier,
@@ -1010,7 +1059,7 @@
 
 @Composable
 private fun filledButtonColors(): ButtonColors {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     return ButtonDefaults.buttonColors(
         containerColor = colors.primary,
         contentColor = colors.onPrimary,
@@ -1058,7 +1107,7 @@
 /** Creates an empty card used to highlight a particular spot on the grid. */
 @Composable
 fun HighlightedItem(modifier: Modifier = Modifier, alpha: Float = 1.0f) {
-    val brush = SolidColor(LocalAndroidColorScheme.current.primary)
+    val brush = SolidColor(MaterialTheme.colorScheme.primary)
     Box(
         modifier =
             // drawBehind lets us draw outside the bounds of the widgets so that we don't need to
@@ -1085,7 +1134,7 @@
     viewModel: BaseCommunalViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     Card(
         modifier = modifier,
         colors =
@@ -1301,7 +1350,7 @@
     modifier: Modifier = Modifier,
     widgetConfigurator: WidgetConfigurator,
 ) {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     val scope = rememberCoroutineScope()
 
     AnimatedVisibility(
@@ -1557,23 +1606,6 @@
     }
 }
 
-/**
- * Check whether the pointer position that the item is being dragged at is within the coordinates of
- * the remove button in the toolbar. Returns true if the item is removable.
- */
-@VisibleForTesting
-fun isPointerWithinEnabledRemoveButton(
-    removeEnabled: Boolean,
-    offset: Offset?,
-    containerToCheck: LayoutCoordinates?,
-): Boolean {
-    if (!removeEnabled || offset == null || containerToCheck == null) {
-        return false
-    }
-    val container = containerToCheck.boundsInWindow()
-    return container.contains(offset)
-}
-
 private fun CommunalContentSize.dp(): Dp {
     return when (this) {
         CommunalContentSize.FULL -> Dimensions.CardHeightFull
@@ -1657,6 +1689,8 @@
     }
 }
 
+data class WidgetSizeInfo(val minHeightPx: Int, val maxHeightPx: Int)
+
 private object Colors {
     val DisabledColorFilter by lazy { disabledColorMatrix() }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/EnableWidgetDialog.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/EnableWidgetDialog.kt
index df11206..b2407fa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/EnableWidgetDialog.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/EnableWidgetDialog.kt
@@ -41,7 +41,6 @@
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -93,7 +92,7 @@
     Box(
         Modifier.fillMaxWidth()
             .padding(top = 18.dp, bottom = 8.dp)
-            .background(LocalAndroidColorScheme.current.surfaceBright, RoundedCornerShape(28.dp))
+            .background(MaterialTheme.colorScheme.surfaceBright, RoundedCornerShape(28.dp))
     ) {
         Column(
             modifier = Modifier.fillMaxWidth(),
@@ -106,7 +105,7 @@
                 Text(
                     text = title,
                     style = MaterialTheme.typography.titleMedium,
-                    color = LocalAndroidColorScheme.current.onSurface,
+                    color = MaterialTheme.colorScheme.onSurface,
                     textAlign = TextAlign.Center,
                     maxLines = 1,
                 )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 101385f..5feb63d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -38,8 +38,9 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.round
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
 import com.android.systemui.Flags.communalWidgetResizing
@@ -57,11 +58,11 @@
 fun rememberGridDragDropState(
     gridState: LazyGridState,
     contentListState: ContentListState,
-    updateDragPositionForRemove: (offset: Offset) -> Boolean,
+    updateDragPositionForRemove: (boundingBox: IntRect) -> Boolean,
 ): GridDragDropState {
     val scope = rememberCoroutineScope()
     val state =
-        remember(gridState, contentListState) {
+        remember(gridState, contentListState, updateDragPositionForRemove) {
             GridDragDropState(
                 state = gridState,
                 contentListState = contentListState,
@@ -91,9 +92,9 @@
     private val state: LazyGridState,
     private val contentListState: ContentListState,
     private val scope: CoroutineScope,
-    private val updateDragPositionForRemove: (offset: Offset) -> Boolean,
+    private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean,
 ) {
-    var draggingItemIndex by mutableStateOf<Int?>(null)
+    var draggingItemKey by mutableStateOf<Any?>(null)
         private set
 
     var isDraggingToRemove by mutableStateOf(false)
@@ -103,7 +104,8 @@
 
     private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
     private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
-    private var dragStartPointerOffset by mutableStateOf(Offset.Zero)
+
+    private var previousTargetItemKey: Any? = null
 
     internal val draggingItemOffset: Offset
         get() =
@@ -112,7 +114,7 @@
             } ?: Offset.Zero
 
     private val draggingItemLayoutInfo: LazyGridItemInfo?
-        get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
+        get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.key == draggingItemKey }
 
     /**
      * Called when dragging is initiated.
@@ -136,8 +138,7 @@
             // before content padding from the initial pointer position
             .firstItemAtOffset(normalizedOffset - contentOffset)
             ?.apply {
-                dragStartPointerOffset = normalizedOffset - this.offset.toOffset()
-                draggingItemIndex = index
+                draggingItemKey = key
                 draggingItemInitialOffset = this.offset.toOffset()
                 return true
             }
@@ -146,19 +147,21 @@
     }
 
     internal fun onDragInterrupted() {
-        draggingItemIndex?.let {
+        draggingItemKey?.let {
             if (isDraggingToRemove) {
-                contentListState.onRemove(it)
+                contentListState.onRemove(
+                    contentListState.list.indexOfFirst { it.key == draggingItemKey }
+                )
                 isDraggingToRemove = false
-                updateDragPositionForRemove(Offset.Zero)
+                updateDragPositionForRemove(IntRect.Zero)
             }
             // persist list editing changes on dragging ends
             contentListState.onSaveList()
-            draggingItemIndex = null
+            draggingItemKey = null
         }
+        previousTargetItemKey = null
         draggingItemDraggedDelta = Offset.Zero
         draggingItemInitialOffset = Offset.Zero
-        dragStartPointerOffset = Offset.Zero
     }
 
     internal fun onDrag(offset: Offset, layoutDirection: LayoutDirection) {
@@ -170,15 +173,29 @@
         val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
         val endOffset = startOffset + draggingItem.size.toSize()
         val middleOffset = startOffset + (endOffset - startOffset) / 2f
+        val draggingBoundingBox =
+            IntRect(draggingItem.offset + draggingItemOffset.round(), draggingItem.size)
 
         val targetItem =
-            state.layoutInfo.visibleItemsInfo
-                .asSequence()
-                .filter { item -> contentListState.isItemEditable(item.index) }
-                .filter { item -> draggingItem.index != item.index }
-                .firstItemAtOffset(middleOffset)
+            if (communalWidgetResizing()) {
+                state.layoutInfo.visibleItemsInfo.findLast { item ->
+                    val itemBoundingBox = IntRect(item.offset, item.size)
+                    draggingItemKey != item.key &&
+                        contentListState.isItemEditable(item.index) &&
+                        draggingBoundingBox.contains(itemBoundingBox.center)
+                }
+            } else {
+                state.layoutInfo.visibleItemsInfo
+                    .asSequence()
+                    .filter { item -> contentListState.isItemEditable(item.index) }
+                    .filter { item -> draggingItem.index != item.index }
+                    .firstItemAtOffset(middleOffset)
+            }
 
-        if (targetItem != null) {
+        if (
+            targetItem != null &&
+                (!communalWidgetResizing() || targetItem.key != previousTargetItemKey)
+        ) {
             val scrollToIndex =
                 if (targetItem.index == state.firstVisibleItemIndex) {
                     draggingItem.index
@@ -187,6 +204,14 @@
                 } else {
                     null
                 }
+            if (communalWidgetResizing()) {
+                // Keep track of the previous target item, to avoid rapidly oscillating between
+                // items if the target item doesn't visually move as a result of the index change.
+                // In this case, even after the index changes, we'd still be colliding with the
+                // element, so it would be selected as the target item the next time this function
+                // runs again, which would trigger us to revert the index change we recently made.
+                previousTargetItemKey = targetItem.key
+            }
             if (scrollToIndex != null) {
                 scope.launch {
                     // this is needed to neutralize automatic keeping the first item first.
@@ -196,20 +221,17 @@
             } else {
                 contentListState.onMove(draggingItem.index, targetItem.index)
             }
-            draggingItemIndex = targetItem.index
             isDraggingToRemove = false
-        } else {
+        } else if (targetItem == null) {
             val overscroll = checkForOverscroll(startOffset, endOffset)
             if (overscroll != 0f) {
                 scrollChannel.trySend(overscroll)
             }
-            isDraggingToRemove = checkForRemove(startOffset)
+            isDraggingToRemove = checkForRemove(draggingBoundingBox)
+            previousTargetItemKey = null
         }
     }
 
-    private val LazyGridItemInfo.offsetEnd: IntOffset
-        get() = this.offset + this.size
-
     /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */
     private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float {
         return when {
@@ -222,10 +244,12 @@
     }
 
     /** Calls the callback with the updated drag position and returns whether to remove the item. */
-    private fun checkForRemove(startOffset: Offset): Boolean {
-        return if (draggingItemDraggedDelta.y < 0)
-            updateDragPositionForRemove(startOffset + dragStartPointerOffset)
-        else false
+    private fun checkForRemove(draggingItemBoundingBox: IntRect): Boolean {
+        return if (draggingItemDraggedDelta.y < 0) {
+            updateDragPositionForRemove(draggingItemBoundingBox)
+        } else {
+            false
+        }
     }
 }
 
@@ -237,7 +261,7 @@
     viewModel: BaseCommunalViewModel,
 ): Modifier {
     return this.then(
-        pointerInput(dragDropState, contentOffset) {
+        Modifier.pointerInput(dragDropState, contentOffset) {
             detectDragGesturesAfterLongPress(
                 onDrag = { change, offset ->
                     change.consume()
@@ -273,7 +297,7 @@
 @Composable
 fun LazyGridItemScope.DraggableItem(
     dragDropState: GridDragDropState,
-    index: Int,
+    key: Any,
     enabled: Boolean,
     selected: Boolean,
     modifier: Modifier = Modifier,
@@ -283,7 +307,7 @@
         return content(false)
     }
 
-    val dragging = index == dragDropState.draggingItemIndex
+    val dragging = key == dragDropState.draggingItemKey
     val itemAlpha: Float by
         animateFloatAsState(
             targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
index ef62eb7..521330f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResizeableItemFrame.kt
@@ -27,10 +27,14 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.foundation.lazy.grid.LazyGridState
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
@@ -47,7 +51,8 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastIsFinite
-import com.android.compose.theme.LocalAndroidColorScheme
+import androidx.compose.ui.zIndex
+import com.android.compose.modifiers.thenIf
 import com.android.systemui.communal.ui.viewmodel.DragHandle
 import com.android.systemui.communal.ui.viewmodel.ResizeInfo
 import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
@@ -60,9 +65,12 @@
     viewModel: ResizeableItemFrameViewModel,
     key: String,
     gridState: LazyGridState,
-    minItemSpan: Int,
     gridContentPadding: PaddingValues,
     verticalArrangement: Arrangement.Vertical,
+    minHeightPx: Int,
+    maxHeightPx: Int,
+    resizeMultiple: Int,
+    currentSpan: GridItemSpan,
 ) {
     val density = LocalDensity.current
     LaunchedEffect(
@@ -70,9 +78,12 @@
         viewModel,
         key,
         gridState,
-        minItemSpan,
         gridContentPadding,
         verticalArrangement,
+        minHeightPx,
+        maxHeightPx,
+        resizeMultiple,
+        currentSpan,
     ) {
         val verticalItemSpacingPx = with(density) { verticalArrangement.spacing.toPx() }
         val verticalContentPaddingPx =
@@ -92,13 +103,15 @@
             )
             .collectLatest { (maxItemSpan, viewportHeightPx, itemInfo) ->
                 viewModel.setGridLayoutInfo(
-                    verticalItemSpacingPx,
-                    verticalContentPaddingPx,
-                    viewportHeightPx,
-                    maxItemSpan,
-                    minItemSpan,
-                    itemInfo?.row,
-                    itemInfo?.span,
+                    verticalItemSpacingPx = verticalItemSpacingPx,
+                    currentRow = itemInfo?.row,
+                    maxHeightPx = maxHeightPx,
+                    minHeightPx = minHeightPx,
+                    currentSpan = currentSpan.currentLineSpan,
+                    resizeMultiple = resizeMultiple,
+                    totalSpans = maxItemSpan,
+                    viewportHeightPx = viewportHeightPx,
+                    verticalContentPaddingPx = verticalContentPaddingPx,
                 )
             }
     }
@@ -141,10 +154,9 @@
 /**
  * Draws a frame around the content with drag handles on the top and bottom of the content.
  *
- * @param index The index of this item in the [LazyGridState].
+ * @param key The unique key of this element, must be the same key used in the [LazyGridState].
+ * @param currentSpan The current span size of this item in the grid.
  * @param gridState The [LazyGridState] for the grid containing this item.
- * @param minItemSpan The minimum span that an item may occupy. Items are resized in multiples of
- *   this span.
  * @param gridContentPadding The content padding used for the grid, needed for determining offsets.
  * @param verticalArrangement The vertical arrangement of the grid items.
  * @param modifier Optional modifier to apply to the frame.
@@ -153,6 +165,10 @@
  * @param outlineColor Optional color to make the outline around the content.
  * @param cornerRadius Optional radius to give to the outline around the content.
  * @param strokeWidth Optional stroke width to draw the outline with.
+ * @param minHeightPx Optional minimum height in pixels that this widget can be resized to.
+ * @param maxHeightPx Optional maximum height in pixels that this widget can be resized to.
+ * @param resizeMultiple Optional number of spans that we allow resizing by. For example, if set to
+ *   3, then we only allow resizing in multiples of 3 spans.
  * @param alpha Optional function to provide an alpha value for the outline. Can be used to fade the
  *   outline in and out. This is wrapped in a function for performance, as the value is only
  *   accessed during the draw phase.
@@ -162,16 +178,19 @@
 @Composable
 fun ResizableItemFrame(
     key: String,
+    currentSpan: GridItemSpan,
     gridState: LazyGridState,
-    minItemSpan: Int,
     gridContentPadding: PaddingValues,
     verticalArrangement: Arrangement.Vertical,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     outlinePadding: Dp = 8.dp,
-    outlineColor: Color = LocalAndroidColorScheme.current.primary,
+    outlineColor: Color = MaterialTheme.colorScheme.primary,
     cornerRadius: Dp = 37.dp,
     strokeWidth: Dp = 3.dp,
+    minHeightPx: Int = 0,
+    maxHeightPx: Int = Int.MAX_VALUE,
+    resizeMultiple: Int = 1,
     alpha: () -> Float = { 1f },
     onResize: (info: ResizeInfo) -> Unit = {},
     content: @Composable () -> Unit,
@@ -179,15 +198,24 @@
     val brush = SolidColor(outlineColor)
     val onResizeUpdated by rememberUpdatedState(onResize)
     val viewModel =
-        rememberViewModel(traceName = "ResizeableItemFrame.viewModel") {
+        rememberViewModel(key = currentSpan, traceName = "ResizeableItemFrame.viewModel") {
             ResizeableItemFrameViewModel()
         }
 
     val dragHandleHeight = verticalArrangement.spacing - outlinePadding * 2
+    val isDragging by
+        remember(viewModel) {
+            derivedStateOf {
+                val topOffset = viewModel.topDragState.offset.takeIf { it.fastIsFinite() } ?: 0f
+                val bottomOffset =
+                    viewModel.bottomDragState.offset.takeIf { it.fastIsFinite() } ?: 0f
+                topOffset > 0 || bottomOffset > 0
+            }
+        }
 
     // Draw content surrounded by drag handles at top and bottom. Allow drag handles
     // to overlap content.
-    Box(modifier) {
+    Box(modifier.thenIf(isDragging) { Modifier.zIndex(1f) }) {
         content()
 
         if (enabled) {
@@ -230,12 +258,15 @@
             }
 
             UpdateGridLayoutInfo(
-                viewModel,
-                key,
-                gridState,
-                minItemSpan,
-                gridContentPadding,
-                verticalArrangement,
+                viewModel = viewModel,
+                key = key,
+                gridState = gridState,
+                currentSpan = currentSpan,
+                gridContentPadding = gridContentPadding,
+                verticalArrangement = verticalArrangement,
+                minHeightPx = minHeightPx,
+                maxHeightPx = maxHeightPx,
+                resizeMultiple = resizeMultiple,
             )
             LaunchedEffect(viewModel) {
                 viewModel.resizeInfo.collectLatest { info -> onResizeUpdated(info) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
index b4c1a2e..868e136 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
@@ -55,7 +55,6 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Popup
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.PopupType
 import com.android.systemui.res.R
@@ -112,7 +111,7 @@
             offset = IntOffset(0, 40),
             onDismissRequest = onDismissRequest,
         ) {
-            val colors = LocalAndroidColorScheme.current
+            val colors = MaterialTheme.colorScheme
             Button(
                 modifier =
                     Modifier.height(56.dp)
@@ -182,7 +181,7 @@
             offset = IntOffset(0, 40),
             onDismissRequest = onDismissRequest
         ) {
-            val colors = LocalAndroidColorScheme.current
+            val colors = MaterialTheme.colorScheme
             Row(
                 modifier =
                     Modifier.height(56.dp)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index 9444664..71230f9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -17,8 +17,9 @@
 package com.android.systemui.keyguard.ui.composable.modifier
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
@@ -41,15 +42,17 @@
     params: BurnInParameters,
     isClock: Boolean = false,
 ): Modifier {
-    val translationYState = remember { mutableStateOf(0F) }
-    viewModel.updateBurnInParams(params.copy(translationY = { translationYState.value }))
+    val cachedYTranslation = remember { mutableFloatStateOf(0f) }
+    LaunchedEffect(Unit) {
+        viewModel.updateBurnInParams(params.copy(translationY = { cachedYTranslation.floatValue }))
+    }
 
     val burnIn = viewModel.movement
     val translationX by
         burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
     val translationY by
         burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
-    translationYState.value = translationY
+    cachedYTranslation.floatValue = translationY
     val scaleViewModel by
         burnIn
             .map { BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index a525f36..9390664 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -30,14 +30,10 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
-import com.android.keyguard.LockIconView
-import com.android.keyguard.LockIconViewController
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
 import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
@@ -61,7 +57,6 @@
     private val windowManager: WindowManager,
     private val authController: AuthController,
     private val featureFlags: FeatureFlagsClassic,
-    private val lockIconViewController: Lazy<LockIconViewController>,
     private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
     private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
     private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
@@ -71,42 +66,28 @@
 ) {
     @Composable
     fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
-        if (!KeyguardBottomAreaRefactor.isEnabled && !DeviceEntryUdfpsRefactor.isEnabled) {
-            return
-        }
-
         val context = LocalContext.current
 
         AndroidView(
             factory = { context ->
-                val view =
-                    if (DeviceEntryUdfpsRefactor.isEnabled) {
-                        DeviceEntryIconView(
-                                context,
-                                null,
-                                logger = LongPressHandlingViewLogger(logBuffer, tag = TAG)
-                            )
-                            .apply {
-                                id = R.id.device_entry_icon_view
-                                DeviceEntryIconViewBinder.bind(
-                                    applicationScope,
-                                    this,
-                                    deviceEntryIconViewModel.get(),
-                                    deviceEntryForegroundViewModel.get(),
-                                    deviceEntryBackgroundViewModel.get(),
-                                    falsingManager.get(),
-                                    vibratorHelper.get(),
-                                    overrideColor,
-                                )
-                            }
-                    } else {
-                        // KeyguardBottomAreaRefactor.isEnabled
-                        LockIconView(context, null).apply {
-                            id = R.id.lock_icon_view
-                            lockIconViewController.get().setLockIconView(this)
-                        }
+                DeviceEntryIconView(
+                        context,
+                        null,
+                        logger = LongPressHandlingViewLogger(logBuffer, tag = TAG),
+                    )
+                    .apply {
+                        id = R.id.device_entry_icon_view
+                        DeviceEntryIconViewBinder.bind(
+                            applicationScope,
+                            this,
+                            deviceEntryIconViewModel.get(),
+                            deviceEntryForegroundViewModel.get(),
+                            deviceEntryBackgroundViewModel.get(),
+                            falsingManager.get(),
+                            vibratorHelper.get(),
+                            overrideColor,
+                        )
                     }
-                view
             },
             modifier =
                 modifier.element(LockIconElementKey).layout { measurable, _ ->
@@ -141,9 +122,7 @@
      * On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are
      * the same as the bounds of the sensor.
      */
-    private fun lockIconBounds(
-        context: Context,
-    ): IntRect {
+    private fun lockIconBounds(context: Context): IntRect {
         val windowViewBounds = windowManager.currentWindowMetrics.bounds
         var widthPx = windowViewBounds.right.toFloat()
         if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
@@ -162,10 +141,7 @@
         val (center, radius) =
             if (authController.isUdfpsSupported && udfpsLocation != null) {
                 Pair(
-                    IntOffset(
-                        x = udfpsLocation.x,
-                        y = udfpsLocation.y,
-                    ),
+                    IntOffset(x = udfpsLocation.x, y = udfpsLocation.y),
                     authController.udfpsRadius.toInt(),
                 )
             } else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index 2e39524..73c4fab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -33,7 +33,7 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
-import com.android.systemui.customization.R as customizationR
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys
 import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
@@ -57,12 +57,12 @@
         Row(
             modifier =
                 Modifier.padding(
-                        horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+                        horizontal = dimensionResource(customR.dimen.clock_padding_start)
                     )
                     .burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
         ) {
             WeatherElement(
-                weatherClockElementViewId = customizationR.id.weather_clock_time,
+                weatherClockElementViewId = customR.id.weather_clock_time,
                 clock = clock,
                 elementKey = WeatherClockElementKeys.timeElementKey,
             )
@@ -75,7 +75,7 @@
         modifier: Modifier = Modifier,
     ) {
         WeatherElement(
-            weatherClockElementViewId = customizationR.id.weather_clock_date,
+            weatherClockElementViewId = customR.id.weather_clock_date,
             clock = clock,
             elementKey = WeatherClockElementKeys.dateElementKey,
             modifier = modifier,
@@ -88,7 +88,7 @@
         modifier: Modifier = Modifier,
     ) {
         WeatherElement(
-            weatherClockElementViewId = customizationR.id.weather_clock_weather_icon,
+            weatherClockElementViewId = customR.id.weather_clock_weather_icon,
             clock = clock,
             elementKey = WeatherClockElementKeys.weatherIconElementKey,
             modifier = modifier.wrapContentSize(),
@@ -101,7 +101,7 @@
         modifier: Modifier = Modifier,
     ) {
         WeatherElement(
-            weatherClockElementViewId = customizationR.id.weather_clock_alarm_dnd,
+            weatherClockElementViewId = customR.id.weather_clock_alarm_dnd,
             clock = clock,
             elementKey = WeatherClockElementKeys.dndAlarmElementKey,
             modifier = modifier.wrapContentSize(),
@@ -114,7 +114,7 @@
         modifier: Modifier = Modifier,
     ) {
         WeatherElement(
-            weatherClockElementViewId = customizationR.id.weather_clock_temperature,
+            weatherClockElementViewId = customR.id.weather_clock_temperature,
             clock = clock,
             elementKey = WeatherClockElementKeys.temperatureElementKey,
             modifier = modifier.wrapContentSize(),
@@ -159,7 +159,7 @@
             modifier =
                 Modifier.height(IntrinsicSize.Max)
                     .padding(
-                        horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+                        horizontal = dimensionResource(customR.dimen.clock_padding_start)
                     )
                     .burnInAware(aodBurnInViewModel, burnInParams, isClock = true)
         ) {
@@ -168,7 +168,7 @@
                 modifier =
                     Modifier.fillMaxSize()
                         .padding(
-                            start = dimensionResource(customizationR.dimen.clock_padding_start)
+                            start = dimensionResource(customR.dimen.clock_padding_start)
                         )
             ) {
                 Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 3d8ca1e..b5d7839 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -34,7 +34,6 @@
 import com.android.compose.animation.scene.MovableElementKey
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.internal.R.attr.layout
 import com.android.systemui.media.controls.ui.composable.MediaCarouselStateLoader.stateForMediaCarouselContent
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.view.MediaHost
@@ -60,12 +59,12 @@
     carouselController: MediaCarouselController,
     offsetProvider: (() -> IntOffset)? = null,
     usingCollapsedLandscapeMedia: Boolean = false,
+    isInSplitShade: Boolean = false,
 ) {
     if (!isVisible || carouselController.isLockedAndHidden()) {
         return
     }
-
-    val carouselState = remember { { stateForMediaCarouselContent() } }
+    val carouselState = remember { { stateForMediaCarouselContent(isInSplitShade) } }
     val isCollapsed = usingCollapsedLandscapeMedia && isLandscape()
     val mediaHeight =
         if (isCollapsed && mediaHost.expansion == MediaHostState.COLLAPSED) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
index 4a0136c..bad7405 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
@@ -43,10 +43,13 @@
 
     /** Returns the corresponding media location for the given [scene] */
     @MediaLocation
-    private fun getMediaLocation(scene: SceneKey): Int {
+    private fun getMediaLocation(scene: SceneKey, isSplitShade: Boolean): Int {
         return when (scene) {
             Scenes.QuickSettings -> MediaHierarchyManager.LOCATION_QS
-            Scenes.Shade -> MediaHierarchyManager.LOCATION_QQS
+            Scenes.Shade -> {
+                if (isSplitShade) MediaHierarchyManager.LOCATION_QS
+                else MediaHierarchyManager.LOCATION_QQS
+            }
             Scenes.Lockscreen -> MediaHierarchyManager.LOCATION_LOCKSCREEN
             Scenes.Communal -> MediaHierarchyManager.LOCATION_COMMUNAL_HUB
             else -> MediaHierarchyManager.LOCATION_UNKNOWN
@@ -97,11 +100,11 @@
     }
 
     /** Returns the state of media carousel */
-    fun SceneScope.stateForMediaCarouselContent(): State {
+    fun SceneScope.stateForMediaCarouselContent(isInSplitShade: Boolean): State {
         return when (val transitionState = layoutState.transitionState) {
             is TransitionState.Idle -> {
                 if (MediaContentPicker.contents.contains(transitionState.currentScene)) {
-                    State.Idle(getMediaLocation(transitionState.currentScene))
+                    State.Idle(getMediaLocation(transitionState.currentScene, isInSplitShade))
                 } else {
                     State.Gone
                 }
@@ -114,14 +117,14 @@
                     ) {
                         State.InProgress(
                             min(progress, 1.0F),
-                            getMediaLocation(fromScene),
-                            getMediaLocation(toScene),
+                            getMediaLocation(fromScene, isInSplitShade),
+                            getMediaLocation(toScene, isInSplitShade),
                         )
                     } else if (MediaContentPicker.contents.contains(toScene)) {
                         State.InProgress(
                             transitionProgress = 1.0F,
                             startLocation = MediaHierarchyManager.LOCATION_UNKNOWN,
-                            getMediaLocation(toScene),
+                            getMediaLocation(toScene, isInSplitShade),
                         )
                     } else {
                         State.Gone
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index 8b9e927..e4c60e1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -18,6 +18,8 @@
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
 
 /**
@@ -44,7 +46,7 @@
         orientation = Orientation.Vertical,
         // scrolling up and inner content is taller than the scrim, so scrim needs to
         // expand; content can scroll once scrim is at the minScrimOffset.
-        canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+        canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
             offsetAvailable < 0 &&
                 offsetBeforeStart == 0f &&
                 contentHeight() > minVisibleScrimHeight() &&
@@ -52,36 +54,38 @@
         },
         // scrolling down and content is done scrolling to top. After that, the scrim
         // needs to collapse; collapse the scrim until it is at the maxScrimOffset.
-        canStartPostScroll = { offsetAvailable, _ ->
+        canStartPostScroll = { offsetAvailable, _, _ ->
             offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
         },
         canStartPostFling = { false },
-        canContinueScroll = {
-            val currentHeight = scrimOffset()
-            minScrimOffset() < currentHeight && currentHeight < maxScrimOffset
-        },
-        canScrollOnFling = true,
+        canStopOnPreFling = { false },
         onStart = { offsetAvailable -> onStart(offsetAvailable) },
-        onScroll = { offsetAvailable ->
+        onScroll = { offsetAvailable, _ ->
             val currentHeight = scrimOffset()
             val amountConsumed =
                 if (offsetAvailable > 0) {
                     val amountLeft = maxScrimOffset - currentHeight
-                    offsetAvailable.coerceAtMost(amountLeft)
+                    offsetAvailable.fastCoerceAtMost(amountLeft)
                 } else {
                     val amountLeft = minScrimOffset() - currentHeight
-                    offsetAvailable.coerceAtLeast(amountLeft)
+                    offsetAvailable.fastCoerceAtLeast(amountLeft)
                 }
             snapScrimOffset(currentHeight + amountConsumed)
             amountConsumed
         },
-        // Don't consume the velocity on pre/post fling
         onStop = { velocityAvailable ->
             onStop(velocityAvailable)
             if (scrimOffset() < minScrimOffset()) {
                 animateScrimOffset(minScrimOffset())
             }
-            { 0f }
+            // Don't consume the velocity on pre/post fling
+            0f
+        },
+        onCancel = {
+            onStop(0f)
+            if (scrimOffset() < minScrimOffset()) {
+                animateScrimOffset(minScrimOffset())
+            }
         },
     )
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index a706585..edb05eb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastCoerceAtLeast
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
 import kotlin.math.max
 import kotlin.math.roundToInt
@@ -86,21 +87,25 @@
 ): PriorityNestedScrollConnection {
     return PriorityNestedScrollConnection(
         orientation = Orientation.Vertical,
-        canStartPreScroll = { _, _ -> false },
-        canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
+        canStartPreScroll = { _, _, _ -> false },
+        canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
             offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
         },
         canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
-        canContinueScroll = { stackOffset() > 0f },
-        canScrollOnFling = true,
+        canStopOnPreFling = { false },
         onStart = { offsetAvailable -> onStart(offsetAvailable) },
-        onScroll = { offsetAvailable ->
-            onScroll(offsetAvailable)
-            offsetAvailable
+        onScroll = { offsetAvailable, _ ->
+            val minOffset = 0f
+            val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset())
+            if (consumed != 0f) {
+                onScroll(consumed)
+            }
+            consumed
         },
         onStop = { velocityAvailable ->
             onStop(velocityAvailable)
-            suspend { velocityAvailable }
+            velocityAvailable
         },
+        onCancel = { onStop(0f) },
     )
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 4c6834c..834a7f5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -50,9 +50,11 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
@@ -60,6 +62,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
@@ -95,13 +98,17 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.ui.composable.ShadeHeader
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import kotlin.math.max
 import kotlin.math.roundToInt
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
@@ -315,6 +322,12 @@
      */
     val stackHeight = remember { mutableIntStateOf(0) }
 
+    /**
+     * Space available for the notification stack on the screen. These bounds don't scroll off the
+     * screen, and respect the scrim paddings, scrim clipping.
+     */
+    val stackBoundsOnScreen = remember { mutableStateOf(Rect.Zero) }
+
     val scrimRounding =
         viewModel.shadeScrimRounding.collectAsStateWithLifecycle(ShadeScrimRounding())
 
@@ -348,12 +361,19 @@
     // The top y bound of the IME.
     val imeTop = remember { mutableFloatStateOf(0f) }
 
-    // we are not scrolled to the top unless the scrim is at its maximum offset.
-    LaunchedEffect(viewModel, scrimOffset) {
-        snapshotFlow { scrimOffset.value >= 0f }
-            .collect { isScrolledToTop -> viewModel.setScrolledToTop(isScrolledToTop) }
+    val shadeScrollState by remember {
+        derivedStateOf {
+            ShadeScrollState(
+                // we are not scrolled to the top unless the scrim is at its maximum offset.
+                isScrolledToTop = scrimOffset.value >= 0f,
+                scrollPosition = scrollState.value,
+                maxScrollPosition = scrollState.maxValue,
+            )
+        }
     }
 
+    LaunchedEffect(shadeScrollState) { viewModel.setScrollState(shadeScrollState) }
+
     // if contentHeight drops below minimum visible scrim height while scrim is
     // expanded, reset scrim offset.
     LaunchedEffect(stackHeight, scrimOffset) {
@@ -395,6 +415,38 @@
             }
     }
 
+    // TalkBack sends a scroll event, when it wants to navigate to an item that is not displayed in
+    // the current viewport.
+    LaunchedEffect(viewModel) {
+        viewModel.setAccessibilityScrollEventConsumer { event ->
+            // scroll up, or down by the height of the visible portion of the notification stack
+            val direction =
+                when (event) {
+                    AccessibilityScrollEvent.SCROLL_UP -> -1
+                    AccessibilityScrollEvent.SCROLL_DOWN -> 1
+                }
+            val viewPortHeight = stackBoundsOnScreen.value.height
+            val scrollStep = max(0f, viewPortHeight - stackScrollView.stackBottomInset)
+            val scrollPosition = scrollState.value.toFloat()
+            val scrollRange = scrollState.maxValue.toFloat()
+            val targetScroll = (scrollPosition + direction * scrollStep).coerceIn(0f, scrollRange)
+            coroutineScope.launch {
+                scrollNotificationStack(
+                    delta = targetScroll - scrollPosition,
+                    animate = false,
+                    scrimOffset = scrimOffset,
+                    minScrimOffset = minScrimOffset,
+                    scrollState = scrollState,
+                )
+            }
+        }
+        try {
+            awaitCancellation()
+        } finally {
+            viewModel.setAccessibilityScrollEventConsumer(null)
+        }
+    }
+
     val scrimNestedScrollConnection =
         shadeSession.rememberSession(
             scrimOffset,
@@ -520,6 +572,9 @@
                         .verticalScroll(scrollState)
                         .padding(top = topPadding)
                         .fillMaxWidth()
+                        .onGloballyPositioned { coordinates ->
+                            stackBoundsOnScreen.value = coordinates.boundsInWindow()
+                        }
             ) {
                 NotificationPlaceholder(
                     stackScrollView = stackScrollView,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index e8da4bd..e382e16 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -73,7 +73,6 @@
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.background
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.theme.colorAttr
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
@@ -163,7 +162,7 @@
     }
 
     val backgroundColor = colorAttr(R.attr.underSurface)
-    val contentColor = LocalAndroidColorScheme.current.onSurface
+    val contentColor = MaterialTheme.colorScheme.onSurface
     val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
     val backgroundModifier =
         remember(
@@ -344,7 +343,7 @@
 @Composable
 private fun NewChangesDot(modifier: Modifier = Modifier) {
     val contentDescription = stringResource(R.string.fgs_dot_content_description)
-    val color = LocalAndroidColorScheme.current.tertiary
+    val color = MaterialTheme.colorScheme.tertiary
 
     Canvas(modifier.size(12.dp).semantics { this.contentDescription = contentDescription }) {
         drawCircle(color)
@@ -363,7 +362,7 @@
     Expandable(
         shape = CircleShape,
         color = colorAttr(R.attr.underSurface),
-        contentColor = LocalAndroidColorScheme.current.onSurfaceVariant,
+        contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
         borderStroke = BorderStroke(1.dp, colorAttr(R.attr.shadeInactive)),
         modifier = modifier.padding(horizontal = 4.dp),
         onClick = onClick,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index e9c5c03..58801e0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -24,7 +24,9 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.layout.layout
@@ -38,6 +40,7 @@
 import com.android.compose.animation.scene.MovableElementContentPicker
 import com.android.compose.animation.scene.MovableElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.ValueKey
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.thenIf
@@ -158,12 +161,10 @@
     squishiness: () -> Float = { QuickSettings.SharedValues.SquishinessValues.Default },
 ) {
     val contentState = { stateForQuickSettingsContent(isSplitShade, squishiness) }
-    val transitionState = layoutState.transitionState
-    val isClosing =
-        transitionState is TransitionState.Transition &&
-            transitionState.progress >= 0.9f && // almost done closing
-            !(layoutState.isTransitioning(to = Scenes.Shade) ||
-                layoutState.isTransitioning(to = Scenes.QuickSettings))
+
+    // Note: We use derivedStateOf {} here because isClosing() is reading the current transition
+    // progress and we don't want to recompose this scene each time the progress has changed.
+    val isClosing by remember(layoutState) { derivedStateOf { isClosing(layoutState) } }
 
     if (isClosing) {
         DisposableEffect(Unit) {
@@ -188,6 +189,14 @@
     }
 }
 
+private fun isClosing(layoutState: SceneTransitionLayoutState): Boolean {
+    val transitionState = layoutState.transitionState
+    return transitionState is TransitionState.Transition &&
+        !(layoutState.isTransitioning(to = Scenes.Shade) ||
+            layoutState.isTransitioning(to = Scenes.QuickSettings)) &&
+        transitionState.progress >= 0.9f // almost done closing
+}
+
 @Composable
 private fun QuickSettingsContent(
     qsSceneAdapter: QSSceneAdapter,
@@ -199,7 +208,7 @@
     QuickSettingsTheme {
         val context = LocalContext.current
 
-        LaunchedEffect(key1 = context) {
+        LaunchedEffect(context) {
             if (qsView == null) {
                 qsSceneAdapter.inflate(context)
             }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index d75a776..0e7165c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -198,15 +198,17 @@
     val shadeHorizontalPadding =
         dimensionResource(id = R.dimen.notification_panel_margin_horizontal)
 
-    BrightnessMirror(
-        viewModel = brightnessMirrorViewModel,
-        qsSceneAdapter = viewModel.qsSceneAdapter,
-        modifier =
-            Modifier.thenIf(cutoutLocation != CutoutLocation.CENTER) {
-                    Modifier.displayCutoutPadding()
-                }
-                .padding(horizontal = shadeHorizontalPadding),
-    )
+    Box(modifier = Modifier.fillMaxSize()) {
+        BrightnessMirror(
+            viewModel = brightnessMirrorViewModel,
+            qsSceneAdapter = viewModel.qsSceneAdapter,
+            modifier =
+                Modifier.thenIf(cutoutLocation != CutoutLocation.CENTER) {
+                        Modifier.displayCutoutPadding()
+                    }
+                    .align(Alignment.TopCenter),
+        )
+    }
 
     val shouldPunchHoleBehindScrim =
         layoutState.isTransitioningBetween(Scenes.Gone, Scenes.QuickSettings) ||
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 54497f6..2a91bd8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -133,7 +133,14 @@
     Column(
         verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
         horizontalAlignment = Alignment.CenterHorizontally,
-        modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
+        modifier =
+            modifier
+                .fillMaxWidth()
+                .padding(
+                    start = QuickSettingsShade.Dimensions.Padding,
+                    end = QuickSettingsShade.Dimensions.Padding,
+                    top = QuickSettingsShade.Dimensions.Padding,
+                ),
     ) {
         BrightnessSliderContainer(
             viewModel = viewModel.brightnessSliderViewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 417f2b8..2d58c8c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -24,6 +24,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateMapOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -31,6 +32,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalContext
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.OverlayKey
@@ -39,9 +42,13 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.observableTransitionState
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.qs.ui.composable.QuickSettingsTheme
 import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import javax.inject.Provider
 import kotlinx.coroutines.flow.collectLatest
 
 /**
@@ -73,6 +80,7 @@
     overlayByKey: Map<OverlayKey, Overlay>,
     initialSceneKey: SceneKey,
     dataSourceDelegator: SceneDataSourceDelegator,
+    qsSceneAdapter: Provider<QSSceneAdapter>,
     modifier: Modifier = Modifier,
 ) {
     val coroutineScope = rememberCoroutineScope()
@@ -118,6 +126,24 @@
         }
     }
 
+    // Inflate qsView here so that shade has the correct qqs height in the first measure pass after
+    // rebooting
+    if (
+        viewModel.allContentKeys.contains(Scenes.QuickSettings) ||
+            viewModel.allContentKeys.contains(Scenes.Shade)
+    ) {
+        val qsAdapter = qsSceneAdapter.get()
+        QuickSettingsTheme {
+            val context = LocalContext.current
+            val qsView by qsAdapter.qsView.collectAsStateWithLifecycle()
+            LaunchedEffect(context) {
+                if (qsView == null) {
+                    qsAdapter.inflate(context)
+                }
+            }
+        }
+    }
+
     Box(
         modifier =
             Modifier.fillMaxSize().pointerInput(Unit) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
index dd37b53..cdf8d00 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
@@ -1,6 +1,6 @@
 package com.android.systemui.scene.ui.composable.transitions
 
-import androidx.compose.animation.core.CubicBezierEasing
+import com.android.compose.animation.Easings
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.systemui.bouncer.ui.composable.Bouncer
 
@@ -9,7 +9,7 @@
 }
 
 fun TransitionBuilder.bouncerToLockscreenPreview() {
-    fractionRange(easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)) {
+    fractionRange(easing = Easings.PredictiveBack) {
         scaleDraw(Bouncer.Elements.Content, scaleY = 0.8f, scaleX = 0.8f)
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 3ec057b..491221f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -361,6 +361,7 @@
                     carouselController = mediaCarouselController,
                     modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Media),
                     usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
+                    isInSplitShade = false,
                 )
 
                 NotificationScrollingStack(
@@ -565,6 +566,7 @@
                                         Modifier.zIndex(1f)
                                     },
                                 carouselController = mediaCarouselController,
+                                isInSplitShade = true,
                             )
                         }
                         FooterActionsWithAnimatedVisibility(
@@ -619,6 +621,7 @@
     mediaOffsetProvider: ShadeMediaOffsetProvider,
     modifier: Modifier = Modifier,
     usingCollapsedLandscapeMedia: Boolean = false,
+    isInSplitShade: Boolean,
 ) {
     MediaCarousel(
         modifier = modifier.fillMaxWidth(),
@@ -632,5 +635,6 @@
                 { mediaOffsetProvider.offset }
             },
         usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
+        isInSplitShade = isInSplitShade,
     )
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
index fe97405..e9b7335 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
@@ -16,34 +16,58 @@
 
 package com.android.systemui.statusbar.phone
 
+import android.app.Dialog
 import android.content.Context
 import android.content.res.Configuration
 import android.os.Bundle
 import androidx.annotation.GravityInt
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.AnchoredDraggableDefaults
+import androidx.compose.foundation.gestures.AnchoredDraggableState
+import androidx.compose.foundation.gestures.DraggableAnchors
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.anchoredDraggable
 import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsDraggedAsState
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.safeDrawing
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.layout.wrapContentWidth
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.isSpecified
 import com.android.compose.theme.PlatformTheme
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
 import com.android.systemui.res.R
+import kotlin.math.roundToInt
 
 /**
  * Create a [SystemUIDialog] with the given [content].
@@ -97,6 +121,9 @@
     theme: Int = R.style.Theme_SystemUI_BottomSheet,
     dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
     content: @Composable (SystemUIDialog) -> Unit,
+    isDraggable: Boolean = true,
+    // TODO(b/337205027): remove maxWidth parameter when aligned to M3 spec
+    maxWidth: Dp = Dp.Unspecified,
 ): ComponentSystemUIDialog {
     return create(
         context = context,
@@ -104,9 +131,49 @@
         dismissOnDeviceLock = dismissOnDeviceLock,
         delegate = EdgeToEdgeDialogDelegate(),
         content = { dialog ->
+            val dragState =
+                if (isDraggable)
+                    remember { AnchoredDraggableState(initialValue = DragAnchors.Start) }
+                else null
+            val interactionSource =
+                if (isDraggable) remember { MutableInteractionSource() } else null
+            if (dragState != null) {
+                val isDragged by interactionSource!!.collectIsDraggedAsState()
+                LaunchedEffect(dragState.currentValue, isDragged) {
+                    if (!isDragged && dragState.currentValue == DragAnchors.End) dialog.dismiss()
+                }
+            }
             Box(
-                modifier = Modifier.bottomSheetClickable { dialog.dismiss() },
-                contentAlignment = Alignment.BottomCenter
+                modifier =
+                    Modifier.bottomSheetClickable { dialog.dismiss() }
+                        .then(
+                            if (isDraggable)
+                                Modifier.anchoredDraggable(
+                                        state = dragState!!,
+                                        interactionSource = interactionSource,
+                                        orientation = Orientation.Vertical,
+                                        flingBehavior =
+                                            AnchoredDraggableDefaults.flingBehavior(
+                                                state = dragState
+                                            ),
+                                    )
+                                    .offset {
+                                        IntOffset(x = 0, y = dragState.requireOffset().roundToInt())
+                                    }
+                                    .onSizeChanged { layoutSize ->
+                                        val dragEndPoint = layoutSize.height - dialog.height
+                                        dragState.updateAnchors(
+                                            DraggableAnchors {
+                                                DragAnchors.entries.forEach { anchor ->
+                                                    anchor at dragEndPoint * anchor.fraction
+                                                }
+                                            }
+                                        )
+                                    }
+                                    .padding(top = draggableTopPadding())
+                            else Modifier // No-Op
+                        ),
+                contentAlignment = Alignment.BottomCenter,
             ) {
                 val radius = dimensionResource(R.dimen.bottom_sheet_corner_radius)
                 Surface(
@@ -114,8 +181,11 @@
                         Modifier.bottomSheetPaddings()
                             // consume input so it doesn't get to the parent Composable
                             .bottomSheetClickable {}
-                            // TODO(b/337205027) change width
-                            .widthIn(max = 800.dp),
+                            .widthIn(
+                                max =
+                                    if (maxWidth.isSpecified) maxWidth
+                                    else DraggableBottomSheet.MaxWidth
+                            ),
                     shape = RoundedCornerShape(topStart = radius, topEnd = radius),
                     color = MaterialTheme.colorScheme.surfaceContainer,
                 ) {
@@ -127,7 +197,17 @@
                                 }
                         )
                     ) {
-                        content(dialog)
+                        if (isDraggable) {
+                            Column(
+                                Modifier.wrapContentWidth(Alignment.CenterHorizontally),
+                                horizontalAlignment = Alignment.CenterHorizontally,
+                            ) {
+                                DragHandle(dialog)
+                                content(dialog)
+                            }
+                        } else {
+                            content(dialog)
+                        }
                     }
                 }
             }
@@ -135,6 +215,11 @@
     )
 }
 
+private enum class DragAnchors(val fraction: Float) {
+    Start(0f),
+    End(1f),
+}
+
 private fun SystemUIDialogFactory.create(
     context: Context,
     theme: Int,
@@ -177,7 +262,7 @@
         padding(
             start = insets.getLeft(this, LocalLayoutDirection.current).toDp() + horizontalPadding,
             top = insets.getTop(this).toDp(),
-            end = insets.getRight(this, LocalLayoutDirection.current).toDp() + horizontalPadding
+            end = insets.getRight(this, LocalLayoutDirection.current).toDp() + horizontalPadding,
         )
     }
 }
@@ -191,3 +276,32 @@
 @Composable
 private fun Modifier.bottomSheetClickable(onClick: () -> Unit) =
     pointerInput(onClick) { detectTapGestures { onClick() } }
+
+@Composable
+private fun DragHandle(dialog: Dialog) {
+    // TODO(b/373340318): Rename drag handle string resource.
+    val dragHandleContentDescription =
+        stringResource(id = R.string.shortcut_helper_content_description_drag_handle)
+    Surface(
+        modifier =
+            Modifier.padding(top = 16.dp, bottom = 6.dp)
+                .semantics { contentDescription = dragHandleContentDescription }
+                .clickable { dialog.dismiss() },
+        color = MaterialTheme.colorScheme.outlineVariant,
+        shape = MaterialTheme.shapes.extraLarge,
+    ) {
+        Box(Modifier.size(width = 32.dp, height = 4.dp))
+    }
+}
+
+@Composable
+private fun draggableTopPadding(): Dp {
+    return if (hasCompactWindowSize()) DraggableBottomSheet.DefaultTopPadding
+    else DraggableBottomSheet.LargeScreenTopPadding
+}
+
+private object DraggableBottomSheet {
+    val DefaultTopPadding = 64.dp
+    val LargeScreenTopPadding = 72.dp
+    val MaxWidth = 640.dp
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 1cc0fb2..cf74785 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -31,7 +31,6 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.theme.PlatformTheme
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
@@ -43,30 +42,20 @@
 private val padding = 24.dp
 
 @Composable
-fun VolumePanelRoot(
-    viewModel: VolumePanelViewModel,
-    modifier: Modifier = Modifier,
-) {
+fun VolumePanelRoot(viewModel: VolumePanelViewModel, modifier: Modifier = Modifier) {
     val accessibilityTitle = stringResource(R.string.accessibility_volume_settings)
     val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle()
     val components by viewModel.componentsLayout.collectAsStateWithLifecycle()
 
     with(VolumePanelComposeScope(state)) {
         components?.let { componentsState ->
-            PlatformTheme {
-                Components(
-                    componentsState,
-                    modifier
-                        .sysuiResTag(VolumePanelTestTag)
-                        .semantics { paneTitle = accessibilityTitle }
-                        .padding(
-                            start = padding,
-                            top = padding,
-                            end = padding,
-                            bottom = 20.dp,
-                        )
-                )
-            }
+            Components(
+                componentsState,
+                modifier
+                    .sysuiResTag(VolumePanelTestTag)
+                    .semantics { paneTitle = accessibilityTitle }
+                    .padding(start = padding, top = padding, end = padding, bottom = 20.dp),
+            )
         }
     }
 }
@@ -74,7 +63,7 @@
 @Composable
 private fun VolumePanelComposeScope.Components(
     layout: ComponentsLayout,
-    modifier: Modifier = Modifier
+    modifier: Modifier = Modifier,
 ) {
     val arrangement: Arrangement.Vertical =
         if (isLargeScreen) {
@@ -82,14 +71,11 @@
         } else {
             if (isPortrait) Arrangement.spacedBy(padding) else Arrangement.spacedBy(4.dp)
         }
-    Column(
-        modifier = modifier,
-        verticalArrangement = arrangement,
-    ) {
+    Column(modifier = modifier, verticalArrangement = arrangement) {
         if (isPortrait || isLargeScreen) {
             VerticalVolumePanelContent(
                 modifier = Modifier.weight(weight = 1f, fill = false),
-                layout = layout
+                layout = layout,
             )
         } else {
             HorizontalVolumePanelContent(
@@ -97,23 +83,17 @@
                 layout = layout,
             )
         }
-        BottomBar(
-            modifier = Modifier,
-            layout = layout,
-        )
+        BottomBar(modifier = Modifier, layout = layout)
     }
 }
 
 @Composable
 private fun VolumePanelComposeScope.BottomBar(
     layout: ComponentsLayout,
-    modifier: Modifier = Modifier
+    modifier: Modifier = Modifier,
 ) {
     if (layout.bottomBarComponent.isVisible) {
-        Box(
-            modifier = modifier.fillMaxWidth(),
-            contentAlignment = Alignment.Center,
-        ) {
+        Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
             with(layout.bottomBarComponent.component as ComposeVolumePanelUiComponent) {
                 Content(Modifier)
             }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 085157a..9f99c37 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -27,9 +27,10 @@
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
-import com.android.compose.nestedscroll.SuspendedValue
 import kotlin.math.absoluteValue
 
+internal typealias SuspendedValue<T> = suspend () -> T
+
 internal interface DraggableHandler {
     /**
      * Start a drag in the given [startedPosition], with the given [overSlop] and number of
@@ -612,7 +613,7 @@
 
         return PriorityNestedScrollConnection(
             orientation = orientation,
-            canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+            canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
                 canChangeScene =
                     if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
 
@@ -630,7 +631,13 @@
                     return@PriorityNestedScrollConnection false
                 }
 
-                _lastPointersInfo = pointersInfoOwner.pointersInfo()
+                val pointersInfo = pointersInfoOwner.pointersInfo()
+
+                if (pointersInfo.isMouseWheel) {
+                    // Do not support mouse wheel interactions
+                    return@PriorityNestedScrollConnection false
+                }
+                _lastPointersInfo = pointersInfo
 
                 // If the current swipe transition is *not* closed to 0f or 1f, then we want the
                 // scroll events to intercept the current transition to continue the scene
@@ -638,7 +645,7 @@
                 isIntercepting = true
                 true
             },
-            canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
+            canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
                 val behavior: NestedScrollBehavior =
                     when {
                         offsetAvailable > 0f -> topOrLeftBehavior
@@ -649,7 +656,12 @@
                 val isZeroOffset =
                     if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
 
-                _lastPointersInfo = pointersInfoOwner.pointersInfo()
+                val pointersInfo = pointersInfoOwner.pointersInfo()
+                if (pointersInfo.isMouseWheel) {
+                    // Do not support mouse wheel interactions
+                    return@PriorityNestedScrollConnection false
+                }
+                _lastPointersInfo = pointersInfo
 
                 val canStart =
                     when (behavior) {
@@ -684,7 +696,12 @@
                 // We could start an overscroll animation
                 canChangeScene = false
 
-                _lastPointersInfo = pointersInfoOwner.pointersInfo()
+                val pointersInfo = pointersInfoOwner.pointersInfo()
+                if (pointersInfo.isMouseWheel) {
+                    // Do not support mouse wheel interactions
+                    return@PriorityNestedScrollConnection false
+                }
+                _lastPointersInfo = pointersInfo
 
                 val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
                 if (canStart) {
@@ -693,8 +710,7 @@
 
                 canStart
             },
-            canContinueScroll = { true },
-            canScrollOnFling = false,
+            canStopOnPreFling = { true },
             onStart = { offsetAvailable ->
                 val pointersInfo = pointersInfo()
                 dragController =
@@ -704,19 +720,33 @@
                         overSlop = if (isIntercepting) 0f else offsetAvailable,
                     )
             },
-            onScroll = { offsetAvailable ->
+            onScroll = { offsetAvailable, _ ->
                 val controller = dragController ?: error("Should be called after onStart")
 
+                val pointersInfo = pointersInfoOwner.pointersInfo()
+                if (pointersInfo.isMouseWheel) {
+                    // Do not support mouse wheel interactions
+                    return@PriorityNestedScrollConnection 0f
+                }
+
                 // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
                 // initiated in a nested child.
                 controller.onDrag(delta = offsetAvailable)
             },
             onStop = { velocityAvailable ->
                 val controller = dragController ?: error("Should be called after onStart")
-
-                controller
-                    .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
-                    .also { dragController = null }
+                try {
+                    controller
+                        .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
+                        .invoke()
+                } finally {
+                    dragController = null
+                }
+            },
+            onCancel = {
+                val controller = dragController ?: error("Should be called after onStart")
+                controller.onStop(velocity = 0f, canChangeContent = canChangeScene)
+                dragController = null
             },
         )
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
index ca68c25..7728727 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
@@ -22,19 +22,52 @@
     fun matches(key: ElementKey, content: ContentKey): Boolean
 }
 
-/**
- * Returns an [ElementMatcher] that matches elements in [content] also matching [this]
- * [ElementMatcher].
- */
-fun ElementMatcher.inContent(content: ContentKey): ElementMatcher {
-    val delegate = this
-    val matcherScene = content
+/** Returns an [ElementMatcher] that matches any element in [content]. */
+fun inContent(content: ContentKey): ElementMatcher {
+    val matcherContent = content
     return object : ElementMatcher {
         override fun matches(key: ElementKey, content: ContentKey): Boolean {
-            return content == matcherScene && delegate.matches(key, content)
+            return content == matcherContent
         }
     }
 }
 
-@Deprecated("Use inContent() instead", replaceWith = ReplaceWith("inContent(scene)"))
-fun ElementMatcher.inScene(scene: SceneKey) = inContent(scene)
+/** Returns an [ElementMatcher] that matches all elements not matching [this] matcher. */
+operator fun ElementMatcher.not(): ElementMatcher {
+    val delegate = this
+    return object : ElementMatcher {
+        override fun matches(key: ElementKey, content: ContentKey): Boolean {
+            return !delegate.matches(key, content)
+        }
+    }
+}
+
+/**
+ * Returns an [ElementMatcher] that matches all elements matching both [this] matcher and [other].
+ */
+infix fun ElementMatcher.and(other: ElementMatcher): ElementMatcher {
+    val delegate = this
+    return object : ElementMatcher {
+        override fun matches(key: ElementKey, content: ContentKey): Boolean {
+            return delegate.matches(key, content) && other.matches(key, content)
+        }
+    }
+}
+
+/**
+ * Returns an [ElementMatcher] that matches all elements either [this] matcher, or [other], or both.
+ */
+infix fun ElementMatcher.or(other: ElementMatcher): ElementMatcher {
+    val delegate = this
+    return object : ElementMatcher {
+        override fun matches(key: ElementKey, content: ContentKey): Boolean {
+            return delegate.matches(key, content) || other.matches(key, content)
+        }
+    }
+}
+
+@Deprecated(
+    "Use `this and inContent()` instead",
+    replaceWith = ReplaceWith("this and inContent(scene)"),
+)
+fun ElementMatcher.inScene(scene: SceneKey) = this and inContent(scene)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index aa70a0c..8613f6d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerEventType
 import androidx.compose.ui.input.pointer.PointerId
 import androidx.compose.ui.input.pointer.PointerInputChange
 import androidx.compose.ui.input.pointer.PointerInputScope
@@ -184,6 +185,7 @@
 
     private var startedPosition: Offset? = null
     private var pointersDown: Int = 0
+    private var isMouseWheel: Boolean = false
 
     internal fun pointersInfo(): PointersInfo {
         return PointersInfo(
@@ -191,6 +193,7 @@
             startedPosition = startedPosition,
             // We could have 0 pointers during fling or for other reasons.
             pointersDown = pointersDown.coerceAtLeast(1),
+            isMouseWheel = isMouseWheel,
         )
     }
 
@@ -202,8 +205,15 @@
             // [requireAncestorPointersInfoOwner], to our descendants.
             while (currentContext.isActive) {
                 // During the Initial pass, we receive the event after our ancestors.
-                val changes = awaitPointerEvent(PointerEventPass.Initial).changes
+                val pointerEvent = awaitPointerEvent(PointerEventPass.Initial)
+
+                // Ignore cursor has entered the input region.
+                // This will only be sent after the cursor is hovering when in the input region.
+                if (pointerEvent.type == PointerEventType.Enter) continue
+
+                val changes = pointerEvent.changes
                 pointersDown = changes.countDown()
+                isMouseWheel = pointerEvent.type == PointerEventType.Scroll
 
                 when {
                     // There are no more pointers down.
@@ -223,7 +233,8 @@
 
                     // The first pointer down, startedPosition was not set.
                     startedPosition == null -> {
-                        val firstPointerDown = changes.single()
+                        // Mouse wheel could start with multiple pointer down
+                        val firstPointerDown = changes.first()
                         velocityPointerId = firstPointerDown.id
                         velocityTracker.resetTracking()
                         velocityTracker.addPointerInputChange(firstPointerDown)
@@ -647,4 +658,8 @@
     fun pointersInfo(): PointersInfo
 }
 
-internal data class PointersInfo(val startedPosition: Offset?, val pointersDown: Int)
+internal data class PointersInfo(
+    val startedPosition: Offset?,
+    val pointersDown: Int,
+    val isMouseWheel: Boolean,
+)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 2657d7c..a9a8668 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -387,11 +387,11 @@
         transition.transformationSpec =
             transitions
                 .transitionSpec(fromContent, toContent, key = transition.key)
-                .transformationSpec()
+                .transformationSpec(transition)
         transition.previewTransformationSpec =
             transitions
                 .transitionSpec(fromContent, toContent, key = transition.key)
-                .previewTransformationSpec()
+                .previewTransformationSpec(transition)
         if (orientation != null) {
             transition.updateOverscrollSpecs(
                 fromSpec = transitions.overscrollSpec(fromContent, orientation),
@@ -430,6 +430,10 @@
                     // Replace the transition.
                     transitionStates =
                         transitionStates.subList(0, transitionStates.lastIndex) + transition
+
+                    // Make sure it is removed from the finishedTransitions set if it was already
+                    // finished.
+                    finishedTransitions.remove(currentState)
                 } else {
                     // Append the new transition.
                     transitionStates = transitionStates + transition
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 879dc54..8866fbf 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -25,6 +25,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transformation.AnchoredSize
 import com.android.compose.animation.scene.transformation.AnchoredTranslate
 import com.android.compose.animation.scene.transformation.DrawScale
@@ -191,20 +192,21 @@
     fun reversed(): TransitionSpec
 
     /**
-     * The [TransformationSpec] associated to this [TransitionSpec].
+     * The [TransformationSpec] associated to this [TransitionSpec] for the given [transition].
      *
      * Note that this is called once whenever a transition associated to this [TransitionSpec] is
      * started.
      */
-    fun transformationSpec(): TransformationSpec
+    fun transformationSpec(transition: TransitionState.Transition): TransformationSpec
 
     /**
-     * The preview [TransformationSpec] associated to this [TransitionSpec].
+     * The preview [TransformationSpec] associated to this [TransitionSpec] for the given
+     * [transition].
      *
      * Note that this is called once whenever a transition associated to this [TransitionSpec] is
      * started.
      */
-    fun previewTransformationSpec(): TransformationSpec?
+    fun previewTransformationSpec(transition: TransitionState.Transition): TransformationSpec?
 }
 
 interface TransformationSpec {
@@ -241,7 +243,7 @@
                 distance = null,
                 transformations = emptyList(),
             )
-        internal val EmptyProvider = { Empty }
+        internal val EmptyProvider = { _: TransitionState.Transition -> Empty }
     }
 }
 
@@ -249,9 +251,13 @@
     override val key: TransitionKey?,
     override val from: ContentKey?,
     override val to: ContentKey?,
-    private val previewTransformationSpec: (() -> TransformationSpecImpl)? = null,
-    private val reversePreviewTransformationSpec: (() -> TransformationSpecImpl)? = null,
-    private val transformationSpec: () -> TransformationSpecImpl,
+    private val previewTransformationSpec:
+        ((TransitionState.Transition) -> TransformationSpecImpl)? =
+        null,
+    private val reversePreviewTransformationSpec:
+        ((TransitionState.Transition) -> TransformationSpecImpl)? =
+        null,
+    private val transformationSpec: (TransitionState.Transition) -> TransformationSpecImpl,
 ) : TransitionSpec {
     override fun reversed(): TransitionSpecImpl {
         return TransitionSpecImpl(
@@ -260,8 +266,8 @@
             to = from,
             previewTransformationSpec = reversePreviewTransformationSpec,
             reversePreviewTransformationSpec = previewTransformationSpec,
-            transformationSpec = {
-                val reverse = transformationSpec.invoke()
+            transformationSpec = { transition ->
+                val reverse = transformationSpec.invoke(transition)
                 TransformationSpecImpl(
                     progressSpec = reverse.progressSpec,
                     swipeSpec = reverse.swipeSpec,
@@ -272,10 +278,13 @@
         )
     }
 
-    override fun transformationSpec(): TransformationSpecImpl = this.transformationSpec.invoke()
+    override fun transformationSpec(
+        transition: TransitionState.Transition
+    ): TransformationSpecImpl = transformationSpec.invoke(transition)
 
-    override fun previewTransformationSpec(): TransformationSpecImpl? =
-        previewTransformationSpec?.invoke()
+    override fun previewTransformationSpec(
+        transition: TransitionState.Transition
+    ): TransformationSpecImpl? = previewTransformationSpec?.invoke(transition)
 }
 
 /** The definition of the overscroll behavior of the [content]. */
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 205267d..f0043e1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -27,7 +27,6 @@
 import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
-import com.android.compose.nestedscroll.SuspendedValue
 import kotlin.math.absoluteValue
 import kotlinx.coroutines.CompletableDeferred
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 061583a..a3f2a43 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -81,16 +81,16 @@
     draggableHandler: DraggableHandlerImpl,
     swipeDetector: SwipeDetector,
 ) : DelegatingNode() {
-    private var delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
+    private var delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
 
     fun update(draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector) {
-        if (draggableHandler == delegate.draggableHandler) {
+        if (draggableHandler == delegateNode.draggableHandler) {
             // Simple update, just update the swipe detector directly and keep the node.
-            delegate.swipeDetector = swipeDetector
+            delegateNode.swipeDetector = swipeDetector
         } else {
             // The draggableHandler changed, force recreate the underlying SwipeToSceneNode.
-            undelegate(delegate)
-            delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
+            undelegate(delegateNode)
+            delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
         }
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 763dc6b..e825c6e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -156,6 +156,9 @@
 
 @TransitionDsl
 interface TransitionBuilder : BaseTransitionBuilder {
+    /** The [TransitionState.Transition] for which we currently compute the transformations. */
+    val transition: TransitionState.Transition
+
     /**
      * The [AnimationSpec] used to animate the associated transition progress from `0` to `1` when
      * the transition is triggered (i.e. it is not gesture-based).
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 7ec5e4f..a5ad999 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -27,6 +27,7 @@
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.Dp
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transformation.AnchoredSize
 import com.android.compose.animation.scene.transformation.AnchoredTranslate
 import com.android.compose.animation.scene.transformation.DrawScale
@@ -128,8 +129,11 @@
         reversePreview: (TransitionBuilder.() -> Unit)?,
         builder: TransitionBuilder.() -> Unit,
     ): TransitionSpec {
-        fun transformationSpec(builder: TransitionBuilder.() -> Unit): TransformationSpecImpl {
-            val impl = TransitionBuilderImpl().apply(builder)
+        fun transformationSpec(
+            transition: TransitionState.Transition,
+            builder: TransitionBuilder.() -> Unit,
+        ): TransformationSpecImpl {
+            val impl = TransitionBuilderImpl(transition).apply(builder)
             return TransformationSpecImpl(
                 progressSpec = impl.spec,
                 swipeSpec = impl.swipeSpec,
@@ -138,17 +142,15 @@
             )
         }
 
-        val previewTransformationSpec = preview?.let { { transformationSpec(it) } }
-        val reversePreviewTransformationSpec = reversePreview?.let { { transformationSpec(it) } }
-        val transformationSpec = { transformationSpec(builder) }
         val spec =
             TransitionSpecImpl(
                 key,
                 from,
                 to,
-                previewTransformationSpec,
-                reversePreviewTransformationSpec,
-                transformationSpec,
+                previewTransformationSpec = preview?.let { { t -> transformationSpec(t, it) } },
+                reversePreviewTransformationSpec =
+                    reversePreview?.let { { t -> transformationSpec(t, it) } },
+                transformationSpec = { t -> transformationSpec(t, builder) },
             )
         transitionSpecs.add(spec)
         return spec
@@ -227,7 +229,8 @@
     }
 }
 
-internal class TransitionBuilderImpl : BaseTransitionBuilderImpl(), TransitionBuilder {
+internal class TransitionBuilderImpl(override val transition: TransitionState.Transition) :
+    BaseTransitionBuilderImpl(), TransitionBuilder {
     override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
     override var swipeSpec: SpringSpec<Float>? = null
     override var distance: UserActionDistance? = null
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
index 9d3f25e..3bd59df 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt
@@ -21,6 +21,7 @@
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import com.android.compose.animation.scene.ContentKey
@@ -249,18 +250,29 @@
         private var fromOverscrollSpec: OverscrollSpecImpl? = null
         private var toOverscrollSpec: OverscrollSpecImpl? = null
 
-        /** The current [OverscrollSpecImpl], if this transition is currently overscrolling. */
-        internal val currentOverscrollSpec: OverscrollSpecImpl?
-            get() {
-                if (this !is HasOverscrollProperties) return null
-                val progress = progress
-                val bouncingContent = bouncingContent
-                return when {
-                    progress < 0f || bouncingContent == fromContent -> fromOverscrollSpec
-                    progress > 1f || bouncingContent == toContent -> toOverscrollSpec
-                    else -> null
+        /**
+         * The current [OverscrollSpecImpl], if this transition is currently overscrolling.
+         *
+         * Note: This is backed by a State<OverscrollSpecImpl?> because the overscroll spec is
+         * derived from progress, and we don't want readers of currentOverscrollSpec to recompose
+         * every time progress is changed.
+         */
+        private val _currentOverscrollSpec: State<OverscrollSpecImpl?>? =
+            if (this !is HasOverscrollProperties) {
+                null
+            } else {
+                derivedStateOf {
+                    val progress = progress
+                    val bouncingContent = bouncingContent
+                    when {
+                        progress < 0f || bouncingContent == fromContent -> fromOverscrollSpec
+                        progress > 1f || bouncingContent == toContent -> toOverscrollSpec
+                        else -> null
+                    }
                 }
             }
+        internal val currentOverscrollSpec: OverscrollSpecImpl?
+            get() = _currentOverscrollSpec?.value
 
         /**
          * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index 4ae3235..ecf64b7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -18,6 +18,8 @@
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
 
 /**
  * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -43,35 +45,32 @@
         orientation = Orientation.Vertical,
         // When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will
         // expand. Then, you can then scroll down the content.
-        canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+        canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
             offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight()
         },
         // When swiping down, the content will scroll up until it reaches the top. Then, the
         // LargeTopAppBar will expand until it reaches its [maxHeight].
-        canStartPostScroll = { offsetAvailable, _ ->
+        canStartPostScroll = { offsetAvailable, _, _ ->
             offsetAvailable > 0 && height() < maxHeight()
         },
         canStartPostFling = { false },
-        canContinueScroll = {
-            val currentHeight = height()
-            minHeight() < currentHeight && currentHeight < maxHeight()
-        },
-        canScrollOnFling = true,
+        canStopOnPreFling = { false },
         onStart = { /* do nothing */ },
-        onScroll = { offsetAvailable ->
+        onScroll = { offsetAvailable, _ ->
             val currentHeight = height()
             val amountConsumed =
                 if (offsetAvailable > 0) {
                     val amountLeft = maxHeight() - currentHeight
-                    offsetAvailable.coerceAtMost(amountLeft)
+                    offsetAvailable.fastCoerceAtMost(amountLeft)
                 } else {
                     val amountLeft = minHeight() - currentHeight
-                    offsetAvailable.coerceAtLeast(amountLeft)
+                    offsetAvailable.fastCoerceAtLeast(amountLeft)
                 }
             onHeightChanged(currentHeight + amountConsumed)
             amountConsumed
         },
         // Don't consume the velocity on pre/post fling
-        onStop = { { 0f } },
+        onStop = { 0f },
+        onCancel = { /* do nothing */ },
     )
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index a3641e6..636c557 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,37 +16,59 @@
 
 package com.android.compose.nestedscroll
 
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.animateDecay
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.unit.Velocity
 import com.android.compose.ui.util.SpaceVectorConverter
+import kotlin.math.abs
 import kotlin.math.sign
-
-internal typealias SuspendedValue<T> = suspend () -> T
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
 
 /**
- * This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and
- * then decides (via [canStartPreScroll] or [canStartPostScroll]) if it should take over scrolling.
- * If it does, it will scroll before its children, until [canContinueScroll] allows it.
+ * A [NestedScrollConnection] that intercepts scroll events in priority mode.
  *
- * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop]
- * after [onStart].
+ * Priority mode allows this connection to take control over scroll events within a nested scroll
+ * hierarchy. When in priority mode, this connection consumes scroll events before its children,
+ * enabling custom scrolling behaviors like sticky headers.
  *
+ * @param orientation The orientation of the scroll.
+ * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll
+ *   events in pre-scroll mode.
+ * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll
+ *   events in post-scroll mode.
+ * @param canStartPostFling lambda that returns true if the connection can start consuming scroll
+ *   events in post-fling mode.
+ * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll
+ *   events in pre-fling (i.e. as soon as the user lifts their fingers).
+ * @param onStart lambda that is called when the connection starts consuming scroll events.
+ * @param onScroll lambda that is called when the connection consumes a scroll event and returns the
+ *   consumed amount.
+ * @param onStop lambda that is called when the connection stops consuming scroll events and returns
+ *   the consumed velocity.
+ * @param onCancel lambda that is called when the connection is cancelled.
  * @sample LargeTopAppBarNestedScrollConnection
  * @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection
  */
 class PriorityNestedScrollConnection(
     orientation: Orientation,
-    private val canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
-    private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
+    private val canStartPreScroll:
+        (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
+    private val canStartPostScroll:
+        (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
     private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
-    private val canContinueScroll: (source: NestedScrollSource) -> Boolean,
-    private val canScrollOnFling: Boolean,
+    private val canStopOnPreFling: () -> Boolean,
     private val onStart: (offsetAvailable: Float) -> Unit,
-    private val onScroll: (offsetAvailable: Float) -> Float,
-    private val onStop: (velocityAvailable: Float) -> SuspendedValue<Float>,
+    private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float,
+    private val onStop: suspend (velocityAvailable: Float) -> Float,
+    private val onCancel: () -> Unit,
 ) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
 
     /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
@@ -54,6 +76,9 @@
 
     private var offsetScrolledBeforePriorityMode = 0f
 
+    /** This job allows us to interrupt the onStop animation */
+    private var onStopJob: Deferred<Float> = CompletableDeferred(0f)
+
     override fun onPostScroll(
         consumed: Offset,
         available: Offset,
@@ -64,62 +89,48 @@
         // the beginning or from the last fling gesture.
         val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat
 
-        if (
-            isPriorityMode ||
-                (source == NestedScrollSource.SideEffect && !canScrollOnFling) ||
-                !canStartPostScroll(availableFloat, offsetBeforeStart)
-        ) {
+        if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
             // The priority mode cannot start so we won't consume the available offset.
             return Offset.Zero
         }
 
-        return onPriorityStart(availableFloat).toOffset()
+        return start(availableFloat, source).toOffset()
     }
 
     override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
         if (!isPriorityMode) {
-            if (source == NestedScrollSource.UserInput || canScrollOnFling) {
-                val availableFloat = available.toFloat()
-                if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode)) {
-                    return onPriorityStart(availableFloat).toOffset()
-                }
-                // We want to track the amount of offset consumed before entering priority mode
-                offsetScrolledBeforePriorityMode += availableFloat
+            val availableFloat = available.toFloat()
+            if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
+                return start(availableFloat, source).toOffset()
             }
-
-            return Offset.Zero
-        }
-
-        val availableFloat = available.toFloat()
-        if (!canContinueScroll(source)) {
-            // Step 3a: We have lost priority and we no longer need to intercept scroll events.
-            onPriorityStop(velocity = 0f)
-
-            // We've just reset offsetScrolledBeforePriorityMode to 0f
             // We want to track the amount of offset consumed before entering priority mode
             offsetScrolledBeforePriorityMode += availableFloat
-
             return Offset.Zero
         }
 
-        // Step 2: We have the priority and can consume the scroll events.
-        return onScroll(availableFloat).toOffset()
+        return scroll(available.toFloat(), source).toOffset()
     }
 
     override suspend fun onPreFling(available: Velocity): Velocity {
-        if (isPriorityMode && canScrollOnFling) {
-            // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+        if (!isPriorityMode) {
+            resetOffsetTracker()
             return Velocity.Zero
         }
-        // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed
-        // of the fling gesture.
-        return onPriorityStop(velocity = available.toFloat()).invoke().toVelocity()
+
+        if (canStopOnPreFling()) {
+            // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the
+            // velocity of the fling gesture.
+            return stop(velocityAvailable = available.toFloat()).toVelocity()
+        }
+
+        // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+        return Velocity.Zero
     }
 
     override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
         val availableFloat = available.toFloat()
         if (isPriorityMode) {
-            return onPriorityStop(velocity = availableFloat).invoke().toVelocity()
+            return stop(velocityAvailable = availableFloat).toVelocity()
         }
 
         if (!canStartPostFling(availableFloat)) {
@@ -131,10 +142,14 @@
         // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
         // overscroll behavior on the Scene level.
         val smallOffset = availableFloat.sign
-        onPriorityStart(availableOffset = smallOffset)
+        start(
+            availableOffset = smallOffset,
+            source = NestedScrollSource.SideEffect,
+            skipScroll = true,
+        )
 
         // This is the last event of a scroll gesture.
-        return onPriorityStop(availableFloat).invoke().toVelocity()
+        return stop(availableFloat).toVelocity()
     }
 
     /**
@@ -143,36 +158,76 @@
      * TODO(b/303224944) This method should be removed.
      */
     fun reset() {
-        // Step 3c: To ensure that an onStop is always called for every onStart.
-        onPriorityStop(velocity = 0f)
+        if (isPriorityMode) {
+            // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart.
+            cancel()
+        } else {
+            resetOffsetTracker()
+        }
     }
 
-    private fun onPriorityStart(availableOffset: Float): Float {
-        if (isPriorityMode) {
-            error("This should never happen, onPriorityStart() was called when isPriorityMode")
+    private fun shouldStop(consumed: Float): Boolean {
+        return consumed == 0f
+    }
+
+    private fun start(
+        availableOffset: Float,
+        source: NestedScrollSource,
+        skipScroll: Boolean = false,
+    ): Float {
+        check(!isPriorityMode) {
+            "This should never happen, start() was called when isPriorityMode"
         }
 
         // Step 1: It's our turn! We start capturing scroll events when one of our children has an
         // available offset following a scroll event.
         isPriorityMode = true
 
+        onStopJob.cancel()
+
         // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
         // lifted (step 3b), or this object has been destroyed (step 3c).
         onStart(availableOffset)
 
-        return onScroll(availableOffset)
+        return if (skipScroll) 0f else scroll(availableOffset, source)
     }
 
-    private fun onPriorityStop(velocity: Float): SuspendedValue<Float> {
-        // We can restart tracking the consumed offsets from scratch.
-        offsetScrolledBeforePriorityMode = 0f
+    private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float {
+        // Step 2: We have the priority and can consume the scroll events.
+        val consumedByScroll = onScroll(offsetAvailable, source)
 
-        if (!isPriorityMode) {
-            return { 0f }
+        if (shouldStop(consumedByScroll)) {
+            // Step 3a: We have lost priority and we no longer need to intercept scroll events.
+            cancel()
+
+            // We've just reset offsetScrolledBeforePriorityMode to 0f
+            // We want to track the amount of offset consumed before entering priority mode
+            offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll
         }
 
-        isPriorityMode = false
+        return consumedByScroll
+    }
 
-        return onStop(velocity)
+    /** Reset the tracking of consumed offsets before entering in priority mode. */
+    private fun resetOffsetTracker() {
+        offsetScrolledBeforePriorityMode = 0f
+    }
+
+    private suspend fun stop(velocityAvailable: Float): Float {
+        check(isPriorityMode) { "This should never happen, stop() was called before start()" }
+        isPriorityMode = false
+        resetOffsetTracker()
+
+        return coroutineScope {
+            onStopJob = async { onStop(velocityAvailable) }
+            onStopJob.await()
+        }
+    }
+
+    private fun cancel() {
+        check(isPriorityMode) { "This should never happen, cancel() was called before start()" }
+        isPriorityMode = false
+        resetOffsetTracker()
+        onCancel()
     }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index ecef6be..a0fed90 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -39,7 +39,6 @@
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.content.state.TransitionState.Transition
 import com.android.compose.animation.scene.subjects.assertThat
-import com.android.compose.nestedscroll.SuspendedValue
 import com.android.compose.test.MonotonicClockTestScope
 import com.android.compose.test.runMonotonicClockTest
 import com.android.compose.test.transition
@@ -128,7 +127,7 @@
         val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal)
 
         var pointerInfoOwner: () -> PointersInfo = {
-            PointersInfo(startedPosition = Offset.Zero, pointersDown = 1)
+            PointersInfo(startedPosition = Offset.Zero, pointersDown = 1, isMouseWheel = false)
         }
 
         fun nestedScrollConnection(
@@ -1188,7 +1187,9 @@
         val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
 
         // Drag from the **top** of the screen
-        pointerInfoOwner = { PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1) }
+        pointerInfoOwner = {
+            PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = false)
+        }
         assertIdle(currentScene = SceneC)
 
         nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f))
@@ -1206,7 +1207,11 @@
 
         // Drag from the **bottom** of the screen
         pointerInfoOwner = {
-            PointersInfo(startedPosition = Offset(0f, SCREEN_SIZE), pointersDown = 1)
+            PointersInfo(
+                startedPosition = Offset(0f, SCREEN_SIZE),
+                pointersDown = 1,
+                isMouseWheel = false,
+            )
         }
         assertIdle(currentScene = SceneC)
 
@@ -1221,6 +1226,22 @@
     }
 
     @Test
+    fun ignoreMouseWheel() = runGestureTest {
+        // Start at scene C.
+        navigateToSceneC()
+        val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
+
+        // Use mouse wheel
+        pointerInfoOwner = {
+            PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = true)
+        }
+        assertIdle(currentScene = SceneC)
+
+        nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f))
+        assertIdle(currentScene = SceneC)
+    }
+
+    @Test
     fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest {
         // Swipe up from the middle to transition to scene B.
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt
new file mode 100644
index 0000000..af09623
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestElements.Bar
+import com.android.compose.animation.scene.TestElements.Foo
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ElementMatcherTest {
+    @Test
+    fun and() {
+        val matcher = Foo and inContent(SceneA)
+        assertThat(matcher.matches(Foo, SceneA)).isTrue()
+        assertThat(matcher.matches(Foo, SceneB)).isFalse()
+        assertThat(matcher.matches(Bar, SceneA)).isFalse()
+        assertThat(matcher.matches(Bar, SceneB)).isFalse()
+    }
+
+    @Test
+    fun or() {
+        val matcher = Foo or inContent(SceneA)
+        assertThat(matcher.matches(Foo, SceneA)).isTrue()
+        assertThat(matcher.matches(Foo, SceneB)).isTrue()
+        assertThat(matcher.matches(Bar, SceneA)).isTrue()
+        assertThat(matcher.matches(Bar, SceneB)).isFalse()
+    }
+
+    @Test
+    fun not() {
+        val matcher = !Foo
+        assertThat(matcher.matches(Foo, SceneA)).isFalse()
+        assertThat(matcher.matches(Foo, SceneB)).isFalse()
+        assertThat(matcher.matches(Bar, SceneA)).isTrue()
+        assertThat(matcher.matches(Bar, SceneB)).isTrue()
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index a2c2729..ee807e6 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -46,6 +46,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.approachLayout
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
@@ -70,11 +71,13 @@
 import com.android.compose.animation.scene.TestScenes.SceneA
 import com.android.compose.animation.scene.TestScenes.SceneB
 import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.test.assertSizeIsEqualTo
 import com.android.compose.test.setContentAndCreateMainScope
 import com.android.compose.test.transition
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import org.junit.Assert.assertThrows
@@ -2218,8 +2221,8 @@
                             // In A => B, Foo is not shared and first fades out from A then fades in
                             // B.
                             sharedElement(TestElements.Foo, enabled = false)
-                            fractionRange(end = 0.5f) { fade(TestElements.Foo.inContent(SceneA)) }
-                            fractionRange(start = 0.5f) { fade(TestElements.Foo.inContent(SceneB)) }
+                            fractionRange(end = 0.5f) { fade(TestElements.Foo.inScene(SceneA)) }
+                            fractionRange(start = 0.5f) { fade(TestElements.Foo.inScene(SceneB)) }
                         }
 
                         from(SceneB, to = SceneA) {
@@ -2581,4 +2584,61 @@
             }
         }
     }
+
+    @Test
+    fun staticSharedElementShouldNotRemeasureOrReplaceDuringOverscrollableTransition() {
+        val size = 30.dp
+        var numberOfMeasurements = 0
+        var numberOfPlacements = 0
+
+        // Foo is a simple element that does not move or resize during the transition.
+        @Composable
+        fun SceneScope.Foo(modifier: Modifier = Modifier) {
+            Box(
+                modifier
+                    .element(TestElements.Foo)
+                    .layout { measurable, constraints ->
+                        numberOfMeasurements++
+                        measurable.measure(constraints).run {
+                            numberOfPlacements++
+                            layout(width, height) { place(0, 0) }
+                        }
+                    }
+                    .size(size)
+            )
+        }
+
+        val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } }
+                    scene(SceneB) { Box(Modifier.fillMaxSize()) { Foo() } }
+                }
+            }
+
+        // Start an overscrollable transition driven by progress.
+        var progress by mutableFloatStateOf(0f)
+        val transition = transition(from = SceneA, to = SceneB, progress = { progress })
+        assertThat(transition).isInstanceOf(TransitionState.HasOverscrollProperties::class.java)
+        scope.launch { state.startTransition(transition) }
+
+        // Reset the counters after the first animation frame.
+        rule.waitForIdle()
+        numberOfMeasurements = 0
+        numberOfPlacements = 0
+
+        // Change the progress a bunch of times.
+        val nFrames = 20
+        repeat(nFrames) { i ->
+            progress = i / nFrames.toFloat()
+            rule.waitForIdle()
+
+            // We shouldn't have remeasured or replaced Foo.
+            assertWithMessage("Frame $i didn't remeasure Foo")
+                .that(numberOfMeasurements)
+                .isEqualTo(0)
+            assertWithMessage("Frame $i didn't replace Foo").that(numberOfPlacements).isEqualTo(0)
+        }
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index c8f6e6d..3df6087 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -46,7 +46,6 @@
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.modifiers.thenIf
-import com.android.compose.nestedscroll.SuspendedValue
 import com.google.common.truth.Truth.assertThat
 import kotlin.properties.Delegates
 import kotlinx.coroutines.coroutineScope
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
index cae6617..7ea414d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
@@ -205,7 +206,8 @@
         val key = MovableElementKey("MovableBar", contents = setOf(SceneA, OverlayA, OverlayB))
         val elementChildTag = "elementChildTag"
 
-        fun elementChild(content: ContentKey) = hasTestTag(elementChildTag) and inContent(content)
+        fun elementChild(content: ContentKey) =
+            hasTestTag(elementChildTag) and SemanticsMatcher.inContent(content)
 
         @Composable
         fun ContentScope.MovableBar() {
@@ -773,7 +775,7 @@
         // Overscroll on Overlay A.
         scope.launch { state.startTransition(transition(SceneA, OverlayA, progress = { 1.5f })) }
         rule
-            .onNode(hasTestTag(movableElementChildTag) and inContent(SceneA))
+            .onNode(hasTestTag(movableElementChildTag) and SemanticsMatcher.inContent(SceneA))
             .assertPositionInRootIsEqualTo(0.dp, 0.dp)
             .assertSizeIsEqualTo(100.dp)
             .assertIsDisplayed()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index f3a3488..f5bb5ba 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -727,4 +727,45 @@
         // The previous job is cancelled and does not infinitely collect the progress.
         job.join()
     }
+
+    @Test
+    fun replacedTransitionIsRemovedFromFinishedTransitions() = runTest {
+        val state = MutableSceneTransitionLayoutState(SceneA)
+
+        val aToB =
+            transition(
+                SceneA,
+                SceneB,
+                onFreezeAndAnimate = {
+                    // Do nothing, so that this transition stays in the transitionStates list and we
+                    // can finish() it manually later.
+                },
+            )
+        val replacingAToB = transition(SceneB, SceneC)
+        val replacingBToC = transition(SceneB, SceneC, replacedTransition = replacingAToB)
+
+        // Start A => B.
+        val aToBJob = state.startTransitionImmediately(animationScope = this, aToB)
+
+        // Start B => C and immediately finish it. It will be flagged as finished in
+        // STLState.finishedTransitions given that A => B is not finished yet.
+        val bToCJob = state.startTransitionImmediately(animationScope = this, replacingAToB)
+        replacingAToB.finish()
+        bToCJob.join()
+
+        // Start a new B => C that replaces the previously finished B => C.
+        val replacingBToCJob =
+            state.startTransitionImmediately(animationScope = this, replacingBToC)
+
+        // Finish A => B.
+        aToB.finish()
+        aToBJob.join()
+
+        // Finish the new B => C.
+        replacingBToC.finish()
+        replacingBToCJob.join()
+
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneC)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 28d0a47..3001505 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -17,6 +17,8 @@
 package com.android.compose.animation.scene
 
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
@@ -39,12 +41,14 @@
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.ScrollWheel
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
 import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performMouseInput
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipeRight
 import androidx.compose.ui.test.swipeUp
@@ -102,26 +106,22 @@
             modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName),
         ) {
             scene(
-                SceneA,
+                key = SceneA,
                 userActions =
                     if (swipesEnabled())
-                        mapOf(
-                            Swipe.Left to SceneB,
-                            Swipe.Down to TestScenes.SceneC,
-                            Swipe.Up to SceneB,
-                        )
+                        mapOf(Swipe.Left to SceneB, Swipe.Down to SceneC, Swipe.Up to SceneB)
                     else emptyMap(),
             ) {
                 Box(Modifier.fillMaxSize())
             }
             scene(
-                SceneB,
+                key = SceneB,
                 userActions = if (swipesEnabled()) mapOf(Swipe.Right to SceneA) else emptyMap(),
             ) {
                 Box(Modifier.fillMaxSize())
             }
             scene(
-                TestScenes.SceneC,
+                key = SceneC,
                 userActions =
                     if (swipesEnabled())
                         mapOf(
@@ -196,7 +196,7 @@
         // Drag is in progress, so currentScene = SceneA and progress = 56dp / LayoutHeight
         transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
-        assertThat(transition).hasToScene(TestScenes.SceneC)
+        assertThat(transition).hasToScene(SceneC)
         assertThat(transition).hasCurrentScene(SceneA)
         assertThat(transition).hasProgress(56.dp / LayoutHeight)
         assertThat(transition).isInitiatedByUserInput()
@@ -206,15 +206,15 @@
         rule.onRoot().performTouchInput { up() }
         transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
-        assertThat(transition).hasToScene(TestScenes.SceneC)
-        assertThat(transition).hasCurrentScene(TestScenes.SceneC)
+        assertThat(transition).hasToScene(SceneC)
+        assertThat(transition).hasCurrentScene(SceneC)
         assertThat(transition).hasProgress(56.dp / LayoutHeight)
         assertThat(transition).isInitiatedByUserInput()
 
         // Wait for the animation to finish. We should now be in scene C.
         rule.waitForIdle()
         assertThat(layoutState.transitionState).isIdle()
-        assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
     }
 
     @Test
@@ -271,20 +271,20 @@
         // We should be animating to C (currentScene = SceneC).
         transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
-        assertThat(transition).hasToScene(TestScenes.SceneC)
-        assertThat(transition).hasCurrentScene(TestScenes.SceneC)
+        assertThat(transition).hasToScene(SceneC)
+        assertThat(transition).hasCurrentScene(SceneC)
         assertThat(transition).hasProgress(55.dp / LayoutHeight)
 
         // Wait for the animation to finish. We should now be in scene C.
         rule.waitForIdle()
         assertThat(layoutState.transitionState).isIdle()
-        assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
     }
 
     @Test
     fun multiPointerSwipe() {
         // Start at scene C.
-        val layoutState = layoutState(TestScenes.SceneC)
+        val layoutState = layoutState(SceneC)
 
         // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
         // detected as a drag event.
@@ -295,7 +295,7 @@
         }
 
         assertThat(layoutState.transitionState).isIdle()
-        assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
 
         // Swipe down with two fingers.
         rule.onRoot().performTouchInput {
@@ -307,7 +307,7 @@
 
         // We are transitioning to B because we used 2 fingers.
         val transition = assertThat(layoutState.transitionState).isSceneTransition()
-        assertThat(transition).hasFromScene(TestScenes.SceneC)
+        assertThat(transition).hasFromScene(SceneC)
         assertThat(transition).hasToScene(SceneB)
 
         // Release the fingers and wait for the animation to end. We are back to C because we only
@@ -315,13 +315,13 @@
         rule.onRoot().performTouchInput { repeat(2) { i -> up(pointerId = i) } }
         rule.waitForIdle()
         assertThat(layoutState.transitionState).isIdle()
-        assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
     }
 
     @Test
     fun defaultEdgeSwipe() {
         // Start at scene C.
-        val layoutState = layoutState(TestScenes.SceneC)
+        val layoutState = layoutState(SceneC)
 
         // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
         // detected as a drag event.
@@ -332,7 +332,7 @@
         }
 
         assertThat(layoutState.transitionState).isIdle()
-        assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
 
         // Swipe down from the top edge.
         rule.onRoot().performTouchInput {
@@ -342,7 +342,7 @@
 
         // We are transitioning to B (and not A) because we started from the top edge.
         var transition = assertThat(layoutState.transitionState).isSceneTransition()
-        assertThat(transition).hasFromScene(TestScenes.SceneC)
+        assertThat(transition).hasFromScene(SceneC)
         assertThat(transition).hasToScene(SceneB)
 
         // Release the fingers and wait for the animation to end. We are back to C because we only
@@ -350,7 +350,7 @@
         rule.onRoot().performTouchInput { up() }
         rule.waitForIdle()
         assertThat(layoutState.transitionState).isIdle()
-        assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
 
         // Swipe right from the left edge.
         rule.onRoot().performTouchInput {
@@ -360,7 +360,7 @@
 
         // We are transitioning to B (and not A) because we started from the left edge.
         transition = assertThat(layoutState.transitionState).isSceneTransition()
-        assertThat(transition).hasFromScene(TestScenes.SceneC)
+        assertThat(transition).hasFromScene(SceneC)
         assertThat(transition).hasToScene(SceneB)
 
         // Release the fingers and wait for the animation to end. We are back to C because we only
@@ -368,7 +368,7 @@
         rule.onRoot().performTouchInput { up() }
         rule.waitForIdle()
         assertThat(layoutState.transitionState).isIdle()
-        assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
     }
 
     @Test
@@ -434,7 +434,7 @@
 
         // We should still correctly compute that we are swiping down to scene C.
         var transition = assertThat(layoutState.transitionState).isSceneTransition()
-        assertThat(transition).hasToScene(TestScenes.SceneC)
+        assertThat(transition).hasToScene(SceneC)
 
         // Release the finger, animating back to scene A.
         rule.onRoot().performTouchInput { up() }
@@ -502,6 +502,89 @@
     }
 
     @Test
+    fun mouseWheel_pointerInputApi_ignoredByStl() {
+        val layoutState = layoutState()
+        var touchSlop = 0f
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            TestContent(layoutState)
+        }
+
+        rule.onRoot().performMouseInput {
+            enter(middle)
+            scroll(touchSlop, ScrollWheel.Vertical)
+        }
+
+        // Mouse wheel scroll are ignored
+        assertThat(layoutState.currentTransition).isNull()
+    }
+
+    @Test
+    fun mouseWheel_scrollableCannotScroll_ignoredByStl() {
+        val layoutState = layoutState()
+        var touchSlop = 0f
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) {
+                scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+                    Box(
+                        Modifier.fillMaxSize()
+                            // A scrollable that does not consume the scroll gesture
+                            .scrollable(rememberScrollableState { 0f }, Orientation.Vertical)
+                    )
+                }
+                scene(SceneB) { Box(Modifier.fillMaxSize()) }
+            }
+        }
+
+        rule.onRoot().performMouseInput {
+            enter(middle)
+            scroll(touchSlop, ScrollWheel.Vertical)
+        }
+
+        // Mouse wheel scroll are ignored
+        assertThat(layoutState.currentTransition).isNull()
+    }
+
+    @Test
+    fun mouseWheel_scrollableConsume_ignoredByStl() {
+        val layoutState = layoutState()
+        var touchSlop = 0f
+        var lastScroll = 0f
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) {
+                scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+                    Box(
+                        Modifier.fillMaxSize()
+                            // A scrollable that consumes the scroll gesture
+                            .scrollable(
+                                rememberScrollableState {
+                                    lastScroll = it
+                                    it
+                                },
+                                Orientation.Vertical,
+                            )
+                    )
+                }
+                scene(SceneB) { Box(Modifier.fillMaxSize()) }
+            }
+        }
+
+        rule.onRoot().performMouseInput {
+            enter(middle)
+            scroll(touchSlop * 10, ScrollWheel.Vertical)
+        }
+
+        // Mouse wheel scroll are ignored
+        assertThat(layoutState.currentTransition).isNull()
+
+        // Mouse wheel scroll can still be consumed by the scrollable
+        assertThat(lastScroll).isNotEqualTo(0f)
+        assertThat(touchSlop).isNotEqualTo(0f)
+    }
+
+    @Test
     fun transitionKey() {
         val transitionkey = TransitionKey(debugName = "foo")
         val state =
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index 223af80..d66d6b3 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -23,11 +23,17 @@
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.gestures.Orientation
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transformation.OverscrollTranslate
 import com.android.compose.animation.scene.transformation.Transformation
 import com.android.compose.animation.scene.transformation.TransformationRange
+import com.android.compose.test.transition
 import com.google.common.truth.Correspondence
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,9 +49,9 @@
     @Test
     fun manyTransitions() {
         val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB)
-            from(TestScenes.SceneB, to = TestScenes.SceneC)
-            from(TestScenes.SceneC, to = TestScenes.SceneA)
+            from(SceneA, to = SceneB)
+            from(SceneB, to = SceneC)
+            from(SceneC, to = SceneA)
         }
         assertThat(transitions.transitionSpecs).hasSize(3)
     }
@@ -53,9 +59,9 @@
     @Test
     fun toFromBuilders() {
         val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB)
-            from(TestScenes.SceneB)
-            to(TestScenes.SceneC)
+            from(SceneA, to = SceneB)
+            from(SceneB)
+            to(SceneC)
         }
 
         assertThat(transitions.transitionSpecs)
@@ -65,38 +71,34 @@
                     "has (from, to) equal to",
                 )
             )
-            .containsExactly(
-                TestScenes.SceneA to TestScenes.SceneB,
-                TestScenes.SceneB to null,
-                null to TestScenes.SceneC,
-            )
+            .containsExactly(SceneA to SceneB, SceneB to null, null to SceneC)
     }
 
+    private fun aToB() = transition(SceneA, SceneB)
+
     @Test
     fun defaultTransitionSpec() {
-        val transitions = transitions { from(TestScenes.SceneA, to = TestScenes.SceneB) }
-        val transformationSpec = transitions.transitionSpecs.single().transformationSpec()
+        val transitions = transitions { from(SceneA, to = SceneB) }
+        val transformationSpec = transitions.transitionSpecs.single().transformationSpec(aToB())
         assertThat(transformationSpec.progressSpec).isInstanceOf(SpringSpec::class.java)
     }
 
     @Test
     fun customTransitionSpec() {
         val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB) { spec = tween(durationMillis = 42) }
+            from(SceneA, to = SceneB) { spec = tween(durationMillis = 42) }
         }
-        val transformationSpec = transitions.transitionSpecs.single().transformationSpec()
+        val transformationSpec = transitions.transitionSpecs.single().transformationSpec(aToB())
         assertThat(transformationSpec.progressSpec).isInstanceOf(TweenSpec::class.java)
         assertThat((transformationSpec.progressSpec as TweenSpec).durationMillis).isEqualTo(42)
     }
 
     @Test
     fun defaultRange() {
-        val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB) { fade(TestElements.Foo) }
-        }
+        val transitions = transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } }
 
         val transformations =
-            transitions.transitionSpecs.single().transformationSpec().transformations
+            transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
         assertThat(transformations.size).isEqualTo(1)
         assertThat(transformations.single().range).isEqualTo(null)
     }
@@ -104,7 +106,7 @@
     @Test
     fun fractionRange() {
         val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB) {
+            from(SceneA, to = SceneB) {
                 fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
                 fractionRange(start = 0.2f) { fade(TestElements.Foo) }
                 fractionRange(end = 0.9f) { fade(TestElements.Foo) }
@@ -119,7 +121,7 @@
         }
 
         val transformations =
-            transitions.transitionSpecs.single().transformationSpec().transformations
+            transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
         assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
             .containsExactly(
@@ -133,7 +135,7 @@
     @Test
     fun timestampRange() {
         val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB) {
+            from(SceneA, to = SceneB) {
                 spec = tween(500)
 
                 timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
@@ -150,7 +152,7 @@
         }
 
         val transformations =
-            transitions.transitionSpecs.single().transformationSpec().transformations
+            transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
         assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
             .containsExactly(
@@ -168,7 +170,7 @@
     @Test
     fun reversed() {
         val transitions = transitions {
-            from(TestScenes.SceneA, to = TestScenes.SceneB) {
+            from(SceneA, to = SceneB) {
                 spec = tween(500)
                 reversed {
                     fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
@@ -178,7 +180,7 @@
         }
 
         val transformations =
-            transitions.transitionSpecs.single().transformationSpec().transformations
+            transitions.transitionSpecs.single().transformationSpec(aToB()).transformations
         assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
             .containsExactly(
@@ -191,8 +193,8 @@
     fun defaultReversed() {
         val transitions = transitions {
             from(
-                TestScenes.SceneA,
-                to = TestScenes.SceneB,
+                SceneA,
+                to = SceneB,
                 preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } },
                 reversePreview = {
                     fractionRange(start = 0.5f, end = 0.6f) { fade(TestElements.Foo) }
@@ -206,10 +208,9 @@
 
         // Fetch the transition from B to A, which will automatically reverse the transition from A
         // to B we defined.
-        val transitionSpec =
-            transitions.transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA, key = null)
+        val transitionSpec = transitions.transitionSpec(from = SceneB, to = SceneA, key = null)
 
-        val transformations = transitionSpec.transformationSpec().transformations
+        val transformations = transitionSpec.transformationSpec(aToB()).transformations
 
         assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -218,7 +219,8 @@
                 TransformationRange(start = 1f - 300 / 500f, end = 1f - 100 / 500f),
             )
 
-        val previewTransformations = transitionSpec.previewTransformationSpec()?.transformations
+        val previewTransformations =
+            transitionSpec.previewTransformationSpec(aToB())?.transformations
 
         assertThat(previewTransformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -229,8 +231,8 @@
     fun defaultPredictiveBack() {
         val transitions = transitions {
             from(
-                TestScenes.SceneA,
-                to = TestScenes.SceneB,
+                SceneA,
+                to = SceneB,
                 preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } },
             ) {
                 spec = tween(500)
@@ -243,12 +245,12 @@
         // transition despite it not having the PredictiveBack key set.
         val transitionSpec =
             transitions.transitionSpec(
-                from = TestScenes.SceneA,
-                to = TestScenes.SceneB,
+                from = SceneA,
+                to = SceneB,
                 key = TransitionKey.PredictiveBack,
             )
 
-        val transformations = transitionSpec.transformationSpec().transformations
+        val transformations = transitionSpec.transformationSpec(aToB()).transformations
 
         assertThat(transformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -257,7 +259,8 @@
                 TransformationRange(start = 100 / 500f, end = 300 / 500f),
             )
 
-        val previewTransformations = transitionSpec.previewTransformationSpec()?.transformations
+        val previewTransformations =
+            transitionSpec.previewTransformationSpec(aToB())?.transformations
 
         assertThat(previewTransformations)
             .comparingElementsUsing(TRANSFORMATION_RANGE)
@@ -271,10 +274,10 @@
         val transitions = transitions {
             defaultSwipeSpec = defaultSpec
 
-            from(TestScenes.SceneA, to = TestScenes.SceneB) {
+            from(SceneA, to = SceneB) {
                 // Default swipe spec.
             }
-            from(TestScenes.SceneA, to = TestScenes.SceneC) { swipeSpec = specFromAToC }
+            from(SceneA, to = SceneC) { swipeSpec = specFromAToC }
         }
 
         assertThat(transitions.defaultSwipeSpec).isSameInstanceAs(defaultSpec)
@@ -282,8 +285,8 @@
         // A => B does not have a custom spec.
         assertThat(
                 transitions
-                    .transitionSpec(from = TestScenes.SceneA, to = TestScenes.SceneB, key = null)
-                    .transformationSpec()
+                    .transitionSpec(from = SceneA, to = SceneB, key = null)
+                    .transformationSpec(aToB())
                     .swipeSpec
             )
             .isNull()
@@ -291,8 +294,8 @@
         // A => C has a custom swipe spec.
         assertThat(
                 transitions
-                    .transitionSpec(from = TestScenes.SceneA, to = TestScenes.SceneC, key = null)
-                    .transformationSpec()
+                    .transitionSpec(from = SceneA, to = SceneC, key = null)
+                    .transformationSpec(transition(from = SceneA, to = SceneC))
                     .swipeSpec
             )
             .isSameInstanceAs(specFromAToC)
@@ -301,7 +304,7 @@
     @Test
     fun overscrollSpec() {
         val transitions = transitions {
-            overscroll(TestScenes.SceneA, Orientation.Vertical) {
+            overscroll(SceneA, Orientation.Vertical) {
                 translate(TestElements.Bar, x = { 1f }, y = { 2f })
             }
         }
@@ -313,9 +316,7 @@
 
     @Test
     fun overscrollSpec_for_overscrollDisabled() {
-        val transitions = transitions {
-            overscrollDisabled(TestScenes.SceneA, Orientation.Vertical)
-        }
+        val transitions = transitions { overscrollDisabled(SceneA, Orientation.Vertical) }
         val overscrollSpec = transitions.overscrollSpecs.single()
         assertThat(overscrollSpec.transformationSpec.transformations).isEmpty()
     }
@@ -323,10 +324,24 @@
     @Test
     fun overscrollSpec_throwIfTransformationsIsEmpty() {
         assertThrows(IllegalStateException::class.java) {
-            transitions { overscroll(TestScenes.SceneA, Orientation.Vertical) {} }
+            transitions { overscroll(SceneA, Orientation.Vertical) {} }
         }
     }
 
+    @Test
+    fun transitionIsPassedToBuilder() = runTest {
+        var transitionPassedToBuilder: TransitionState.Transition? = null
+        val state =
+            MutableSceneTransitionLayoutState(
+                SceneA,
+                transitions { from(SceneA, to = SceneB) { transitionPassedToBuilder = transition } },
+            )
+
+        val transition = aToB()
+        state.startTransitionImmediately(animationScope = backgroundScope, transition)
+        assertThat(transitionPassedToBuilder).isSameInstanceAs(transition)
+    }
+
     companion object {
         private val TRANSFORMATION_RANGE =
             Correspondence.transforming<Transformation, TransformationRange?>(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
index 4877cd6..2e3a934 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -31,7 +31,7 @@
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TestElements
 import com.android.compose.animation.scene.TestScenes
-import com.android.compose.animation.scene.inContent
+import com.android.compose.animation.scene.inScene
 import com.android.compose.animation.scene.testTransition
 import com.android.compose.test.assertSizeIsEqualTo
 import org.junit.Rule
@@ -125,10 +125,10 @@
                 sharedElement(TestElements.Foo, enabled = false)
 
                 // In SceneA, Foo leaves to the left edge.
-                translate(TestElements.Foo.inContent(TestScenes.SceneA), Edge.Left)
+                translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left)
 
                 // In SceneB, Foo comes from the bottom edge.
-                translate(TestElements.Foo.inContent(TestScenes.SceneB), Edge.Bottom)
+                translate(TestElements.Foo.inScene(TestScenes.SceneB), Edge.Bottom)
             },
         ) {
             before { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp) }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index badc43b..1a3b86b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -34,30 +34,31 @@
     private var canStartPreScroll = false
     private var canStartPostScroll = false
     private var canStartPostFling = false
-    private var canContinueScroll = false
+    private var canStopOnPreFling = true
     private var isStarted = false
     private var lastScroll: Float? = null
-    private var returnOnScroll = 0f
+    private var consumeScroll = true
     private var lastStop: Float? = null
-    private var returnOnStop = 0f
+    private var isCancelled: Boolean = false
+    private var consumeStop = true
 
     private val scrollConnection =
         PriorityNestedScrollConnection(
             orientation = Orientation.Vertical,
-            canStartPreScroll = { _, _ -> canStartPreScroll },
-            canStartPostScroll = { _, _ -> canStartPostScroll },
+            canStartPreScroll = { _, _, _ -> canStartPreScroll },
+            canStartPostScroll = { _, _, _ -> canStartPostScroll },
             canStartPostFling = { canStartPostFling },
-            canContinueScroll = { canContinueScroll },
-            canScrollOnFling = false,
+            canStopOnPreFling = { canStopOnPreFling },
             onStart = { isStarted = true },
-            onScroll = {
-                lastScroll = it
-                returnOnScroll
+            onScroll = { offsetAvailable, _ ->
+                lastScroll = offsetAvailable
+                if (consumeScroll) offsetAvailable else 0f
             },
             onStop = {
                 lastStop = it
-                { returnOnStop }
+                if (consumeStop) it else 0f
             },
+            onCancel = { isCancelled = true },
         )
 
     @Test
@@ -85,7 +86,7 @@
         canStartPostScroll = true
         scrollConnection.onPostScroll(
             consumed = Offset.Zero,
-            available = Offset.Zero,
+            available = Offset(1f, 1f),
             source = UserInput,
         )
     }
@@ -136,45 +137,55 @@
     @Test
     fun step2_onPriorityMode_shouldContinueIfAllowed() {
         startPriorityModePostScroll()
-        canContinueScroll = true
 
-        scrollConnection.onPreScroll(available = Offset(1f, 1f), source = UserInput)
+        val scroll1 = scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput)
         assertThat(lastScroll).isEqualTo(1f)
+        assertThat(scroll1.y).isEqualTo(1f)
 
-        canContinueScroll = false
-        scrollConnection.onPreScroll(available = Offset(2f, 2f), source = UserInput)
-        assertThat(lastScroll).isNotEqualTo(2f)
-        assertThat(lastScroll).isEqualTo(1f)
+        consumeScroll = false
+        val scroll2 = scrollConnection.onPreScroll(available = Offset(0f, 2f), source = UserInput)
+        assertThat(lastScroll).isEqualTo(2f)
+        assertThat(scroll2.y).isEqualTo(0f)
     }
 
     @Test
-    fun step3a_onPriorityMode_shouldStopIfCannotContinue() {
+    fun step3a_onPriorityMode_shouldCancelIfCannotContinue() {
         startPriorityModePostScroll()
-        canContinueScroll = false
+        consumeScroll = false
 
-        scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
+        scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput)
 
-        assertThat(lastStop).isNotNull()
+        assertThat(isCancelled).isTrue()
     }
 
     @Test
     fun step3b_onPriorityMode_shouldStopOnFling() = runTest {
         startPriorityModePostScroll()
-        canContinueScroll = true
 
         scrollConnection.onPreFling(available = Velocity.Zero)
 
-        assertThat(lastStop).isNotNull()
+        assertThat(lastStop).isEqualTo(0f)
     }
 
     @Test
-    fun step3c_onPriorityMode_shouldStopOnReset() {
+    fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest {
         startPriorityModePostScroll()
-        canContinueScroll = true
+        canStopOnPreFling = false
+
+        scrollConnection.onPreFling(available = Velocity.Zero)
+        assertThat(lastStop).isNull()
+
+        scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
+        assertThat(lastStop).isEqualTo(0f)
+    }
+
+    @Test
+    fun step3c_onPriorityMode_shouldCancelOnReset() {
+        startPriorityModePostScroll()
 
         scrollConnection.reset()
 
-        assertThat(lastStop).isNotNull()
+        assertThat(isCancelled).isTrue()
     }
 
     @Test
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
index 22450d3..b3201d0 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
@@ -25,11 +25,11 @@
     return if (content == null) {
         hasTestTag(element.testTag)
     } else {
-        hasTestTag(element.testTag) and inContent(content)
+        hasTestTag(element.testTag) and SemanticsMatcher.inContent(content)
     }
 }
 
 /** A [SemanticsMatcher] that matches anything inside [content]. */
-fun inContent(content: ContentKey): SemanticsMatcher {
+fun SemanticsMatcher.Companion.inContent(content: ContentKey): SemanticsMatcher {
     return hasAnyAncestor(hasTestTag(content.testTag))
 }
diff --git a/packages/SystemUI/customization/res/values/ids.xml b/packages/SystemUI/customization/res/values/ids.xml
index ec466f0..3a3e06b 100644
--- a/packages/SystemUI/customization/res/values/ids.xml
+++ b/packages/SystemUI/customization/res/values/ids.xml
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
+    <item type="id" name="lockscreen_clock_view_large" />
+    <item type="id" name="lockscreen_clock_view" />
+
     <!-- View ids for elements in large weather clock -->
     <item type="id" name="weather_clock_time" />
     <item type="id" name="weather_clock_date" />
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
index d001ef96..031fbab 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt
@@ -68,7 +68,10 @@
         seedColor = null,
         overrideChroma = null,
         typefaceCache =
-            TypefaceCache(messageBuffer) { Typeface.createFromAsset(pluginCtx.assets, it) },
+            TypefaceCache(messageBuffer) {
+                // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
+                return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL)
+            },
         getThemeSeedColor = getThemeSeedColor ?: Companion::getThemeSeedColor,
         messageBuffer = messageBuffer,
     )
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
new file mode 100644
index 0000000..eedddb2
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockEvents
+import com.android.systemui.plugins.clocks.ClockFaceConfig
+import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.WeatherData
+import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.view.FlexClockView
+import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
+import java.util.Locale
+import java.util.TimeZone
+
+class ComposedDigitalLayerController(
+    private val ctx: Context,
+    private val resources: Resources,
+    private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+    private val layer: ComposedDigitalHandLayer,
+    messageBuffer: MessageBuffer,
+) : SimpleClockLayerController {
+    private val logger = Logger(messageBuffer, ComposedDigitalLayerController::class.simpleName!!)
+
+    val layerControllers = mutableListOf<SimpleClockLayerController>()
+    val dozeState = DefaultClockController.AnimationState(1F)
+    var isRegionDark = true
+
+    override val view = FlexClockView(ctx, assets, messageBuffer)
+
+    init {
+        layer.digitalLayers.forEach {
+            val childView = SimpleDigitalClockTextView(ctx, messageBuffer)
+            val controller =
+                SimpleDigitalHandLayerController(
+                    ctx,
+                    resources,
+                    assets,
+                    it as DigitalHandLayer,
+                    childView,
+                    messageBuffer,
+                )
+
+            view.addView(childView)
+            layerControllers.add(controller)
+        }
+    }
+
+    private fun refreshTime() {
+        layerControllers.forEach { it.faceEvents.onTimeTick() }
+        view.refreshTime()
+    }
+
+    override val events =
+        object : ClockEvents {
+            override fun onTimeZoneChanged(timeZone: TimeZone) {
+                layerControllers.forEach { it.events.onTimeZoneChanged(timeZone) }
+                refreshTime()
+            }
+
+            override fun onTimeFormatChanged(is24Hr: Boolean) {
+                layerControllers.forEach { it.events.onTimeFormatChanged(is24Hr) }
+                refreshTime()
+            }
+
+            override fun onLocaleChanged(locale: Locale) {
+                layerControllers.forEach { it.events.onLocaleChanged(locale) }
+                view.onLocaleChanged(locale)
+                refreshTime()
+            }
+
+            override fun onWeatherDataChanged(data: WeatherData) {
+                view.onWeatherDataChanged(data)
+            }
+
+            override fun onAlarmDataChanged(data: AlarmData) {
+                view.onAlarmDataChanged(data)
+            }
+
+            override fun onZenDataChanged(data: ZenData) {
+                view.onZenDataChanged(data)
+            }
+
+            override fun onColorPaletteChanged(resources: Resources) {}
+
+            override fun onSeedColorChanged(seedColor: Int?) {}
+
+            override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+
+            override var isReactiveTouchInteractionEnabled
+                get() = view.isReactiveTouchInteractionEnabled
+                set(value) {
+                    view.isReactiveTouchInteractionEnabled = value
+                }
+        }
+
+    override fun updateColors() {
+        view.updateColors(assets, isRegionDark)
+    }
+
+    override val animations =
+        object : ClockAnimations {
+            override fun enter() {
+                refreshTime()
+            }
+
+            override fun doze(fraction: Float) {
+                val (hasChanged, hasJumped) = dozeState.update(fraction)
+                if (hasChanged) view.animateDoze(dozeState.isActive, !hasJumped)
+                view.dozeFraction = fraction
+                view.invalidate()
+            }
+
+            override fun fold(fraction: Float) {
+                refreshTime()
+            }
+
+            override fun charge() {
+                view.animateCharge()
+            }
+
+            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
+                view.onPositionUpdated(fromLeft, direction, fraction)
+            }
+
+            override fun onPositionUpdated(distance: Float, fraction: Float) {}
+
+            override fun onPickerCarouselSwiping(swipingFraction: Float) {
+                view.onPickerCarouselSwiping(swipingFraction)
+            }
+        }
+
+    override val faceEvents =
+        object : ClockFaceEvents {
+            override fun onTimeTick() {
+                refreshTime()
+            }
+
+            override fun onRegionDarknessChanged(isRegionDark: Boolean) {
+                this@ComposedDigitalLayerController.isRegionDark = isRegionDark
+                updateColors()
+            }
+
+            override fun onFontSettingChanged(fontSizePx: Float) {
+                view.onFontSettingChanged(fontSizePx)
+            }
+
+            override fun onTargetRegionChanged(targetRegion: Rect?) {}
+
+            override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {}
+        }
+
+    override val config =
+        ClockFaceConfig(
+            hasCustomWeatherDataDisplay = view.hasCustomWeatherDataDisplay,
+            hasCustomPositionUpdatedAnimation = view.hasCustomPositionUpdatedAnimation,
+            useCustomClockScene = view.useCustomClockScene,
+        )
+
+    @VisibleForTesting
+    override var fakeTimeMills: Long? = null
+        get() = field
+        set(timeInMills) {
+            field = timeInMills
+            for (layerController in layerControllers) {
+                layerController.fakeTimeMills = timeInMills
+            }
+        }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 07191c6..900971b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -17,6 +17,8 @@
 import android.content.res.Resources
 import android.view.LayoutInflater
 import com.android.systemui.customization.R
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogcatOnlyMessageBuffer
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
@@ -24,6 +26,8 @@
 import com.android.systemui.plugins.clocks.ClockPickerConfig
 import com.android.systemui.plugins.clocks.ClockProvider
 import com.android.systemui.plugins.clocks.ClockSettings
+import com.android.systemui.shared.clocks.view.HorizontalAlignment
+import com.android.systemui.shared.clocks.view.VerticalAlignment
 
 private val TAG = DefaultClockProvider::class.simpleName
 const val DEFAULT_CLOCK_ID = "DEFAULT"
@@ -33,8 +37,9 @@
     val ctx: Context,
     val layoutInflater: LayoutInflater,
     val resources: Resources,
-    val hasStepClockAnimation: Boolean = false,
-    val migratedClocks: Boolean = false,
+    private val hasStepClockAnimation: Boolean = false,
+    private val migratedClocks: Boolean = false,
+    private val clockReactiveVariants: Boolean = false,
 ) : ClockProvider {
     private var messageBuffers: ClockMessageBuffers? = null
 
@@ -49,15 +54,22 @@
             throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
         }
 
-        return DefaultClockController(
-            ctx,
-            layoutInflater,
-            resources,
-            settings,
-            hasStepClockAnimation,
-            migratedClocks,
-            messageBuffers,
-        )
+        return if (clockReactiveVariants) {
+            val buffer =
+                messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO)
+            val assets = AssetLoader(ctx, ctx, "clocks/", buffer)
+            FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers)
+        } else {
+            DefaultClockController(
+                ctx,
+                layoutInflater,
+                resources,
+                settings,
+                hasStepClockAnimation,
+                migratedClocks,
+                messageBuffers,
+            )
+        }
     }
 
     override fun getClockPickerConfig(id: ClockId): ClockPickerConfig {
@@ -71,6 +83,168 @@
             resources.getString(R.string.clock_default_description),
             // TODO(b/352049256): Update placeholder to actual resource
             resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+            isReactiveToTone = true,
+            isReactiveToTouch = clockReactiveVariants,
+            axes = listOf(), // TODO: Ater some picker definition
         )
     }
+
+    companion object {
+        val FLEX_DESIGN = run {
+            val largeLayer =
+                listOf(
+                    ComposedDigitalHandLayer(
+                        layerBounds = LayerBounds.FIT,
+                        customizedView = "FlexClockView",
+                        digitalLayers =
+                            listOf(
+                                DigitalHandLayer(
+                                    layerBounds = LayerBounds.FIT,
+                                    timespec = DigitalTimespec.FIRST_DIGIT,
+                                    style =
+                                        FontTextStyle(
+                                            fontFamily = "google_sans_flex.ttf",
+                                            lineHeight = 147.25f,
+                                            fontVariation =
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                        ),
+                                    aodStyle =
+                                        FontTextStyle(
+                                            fontVariation =
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                            fontFamily = "google_sans_flex.ttf",
+                                            fillColorLight = "#FFFFFFFF",
+                                            outlineColor = "#00000000",
+                                            renderType = RenderType.CHANGE_WEIGHT,
+                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
+                                            transitionDuration = 750,
+                                        ),
+                                    alignment =
+                                        DigitalAlignment(
+                                            HorizontalAlignment.CENTER,
+                                            VerticalAlignment.CENTER,
+                                        ),
+                                    dateTimeFormat = "hh",
+                                ),
+                                DigitalHandLayer(
+                                    layerBounds = LayerBounds.FIT,
+                                    timespec = DigitalTimespec.SECOND_DIGIT,
+                                    style =
+                                        FontTextStyle(
+                                            fontFamily = "google_sans_flex.ttf",
+                                            lineHeight = 147.25f,
+                                            fontVariation =
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                        ),
+                                    aodStyle =
+                                        FontTextStyle(
+                                            fontVariation =
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                            fontFamily = "google_sans_flex.ttf",
+                                            fillColorLight = "#FFFFFFFF",
+                                            outlineColor = "#00000000",
+                                            renderType = RenderType.CHANGE_WEIGHT,
+                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
+                                            transitionDuration = 750,
+                                        ),
+                                    alignment =
+                                        DigitalAlignment(
+                                            HorizontalAlignment.CENTER,
+                                            VerticalAlignment.CENTER,
+                                        ),
+                                    dateTimeFormat = "hh",
+                                ),
+                                DigitalHandLayer(
+                                    layerBounds = LayerBounds.FIT,
+                                    timespec = DigitalTimespec.FIRST_DIGIT,
+                                    style =
+                                        FontTextStyle(
+                                            fontFamily = "google_sans_flex.ttf",
+                                            lineHeight = 147.25f,
+                                            fontVariation =
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                        ),
+                                    aodStyle =
+                                        FontTextStyle(
+                                            fontVariation =
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                            fontFamily = "google_sans_flex.ttf",
+                                            fillColorLight = "#FFFFFFFF",
+                                            outlineColor = "#00000000",
+                                            renderType = RenderType.CHANGE_WEIGHT,
+                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
+                                            transitionDuration = 750,
+                                        ),
+                                    alignment =
+                                        DigitalAlignment(
+                                            HorizontalAlignment.CENTER,
+                                            VerticalAlignment.CENTER,
+                                        ),
+                                    dateTimeFormat = "mm",
+                                ),
+                                DigitalHandLayer(
+                                    layerBounds = LayerBounds.FIT,
+                                    timespec = DigitalTimespec.SECOND_DIGIT,
+                                    style =
+                                        FontTextStyle(
+                                            fontFamily = "google_sans_flex.ttf",
+                                            lineHeight = 147.25f,
+                                            fontVariation =
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                        ),
+                                    aodStyle =
+                                        FontTextStyle(
+                                            fontVariation =
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                            fontFamily = "google_sans_flex.ttf",
+                                            fillColorLight = "#FFFFFFFF",
+                                            outlineColor = "#00000000",
+                                            renderType = RenderType.CHANGE_WEIGHT,
+                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
+                                            transitionDuration = 750,
+                                        ),
+                                    alignment =
+                                        DigitalAlignment(
+                                            HorizontalAlignment.CENTER,
+                                            VerticalAlignment.CENTER,
+                                        ),
+                                    dateTimeFormat = "mm",
+                                ),
+                            ),
+                    )
+                )
+
+            val smallLayer =
+                listOf(
+                    DigitalHandLayer(
+                        layerBounds = LayerBounds.FIT,
+                        timespec = DigitalTimespec.TIME_FULL_FORMAT,
+                        style =
+                            FontTextStyle(
+                                fontFamily = "google_sans_flex.ttf",
+                                fontVariation = "'wght' 600, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                fontSizeScale = 0.98f,
+                            ),
+                        aodStyle =
+                            FontTextStyle(
+                                fontFamily = "google_sans_flex.ttf",
+                                fontVariation = "'wght' 133, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                fillColorLight = "#FFFFFFFF",
+                                outlineColor = "#00000000",
+                                renderType = RenderType.CHANGE_WEIGHT,
+                            ),
+                        alignment = DigitalAlignment(HorizontalAlignment.LEFT, null),
+                        dateTimeFormat = "h:mm",
+                    )
+                )
+
+            ClockDesign(
+                id = DEFAULT_CLOCK_ID,
+                name = "@string/clock_default_name",
+                description = "@string/clock_default_description",
+                large = ClockFace(layers = largeLayer),
+                small = ClockFace(layers = smallLayer),
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
new file mode 100644
index 0000000..b8ebd0f
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.content.Context
+import android.content.res.Resources
+import com.android.systemui.customization.R
+import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.ClockConfig
+import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockEvents
+import com.android.systemui.plugins.clocks.ClockMessageBuffers
+import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.WeatherData
+import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.view.FlexClockView
+import java.io.PrintWriter
+import java.util.Locale
+import java.util.TimeZone
+
+/** Controller for the default flex clock */
+class FlexClockController(
+    private val ctx: Context,
+    private val resources: Resources,
+    private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+    val design: ClockDesign, // TODO(b/364680879): Remove when done inlining
+    val messageBuffers: ClockMessageBuffers?,
+) : ClockController {
+    override val smallClock = run {
+        val buffer = messageBuffers?.smallClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER
+        FlexClockFaceController(
+            ctx,
+            resources,
+            assets.copy(messageBuffer = buffer),
+            design.small ?: design.large!!,
+            false,
+            buffer,
+        )
+    }
+
+    override val largeClock = run {
+        val buffer = messageBuffers?.largeClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER
+        FlexClockFaceController(
+            ctx,
+            resources,
+            assets.copy(messageBuffer = buffer),
+            design.large ?: design.small!!,
+            true,
+            buffer,
+        )
+    }
+
+    override val config: ClockConfig by lazy {
+        ClockConfig(
+            DEFAULT_CLOCK_ID,
+            resources.getString(R.string.clock_default_name),
+            resources.getString(R.string.clock_default_description),
+            isReactiveToTone = true,
+        )
+    }
+
+    override val events =
+        object : ClockEvents {
+            override var isReactiveTouchInteractionEnabled = false
+                set(value) {
+                    field = value
+                    val view = largeClock.view as FlexClockView
+                    view.isReactiveTouchInteractionEnabled = value
+                }
+
+            override fun onTimeZoneChanged(timeZone: TimeZone) {
+                smallClock.events.onTimeZoneChanged(timeZone)
+                largeClock.events.onTimeZoneChanged(timeZone)
+            }
+
+            override fun onTimeFormatChanged(is24Hr: Boolean) {
+                smallClock.events.onTimeFormatChanged(is24Hr)
+                largeClock.events.onTimeFormatChanged(is24Hr)
+            }
+
+            override fun onLocaleChanged(locale: Locale) {
+                smallClock.events.onLocaleChanged(locale)
+                largeClock.events.onLocaleChanged(locale)
+            }
+
+            override fun onColorPaletteChanged(resources: Resources) {
+                assets.refreshColorPalette(design.colorPalette)
+                smallClock.assets.refreshColorPalette(design.colorPalette)
+                largeClock.assets.refreshColorPalette(design.colorPalette)
+
+                smallClock.events.onColorPaletteChanged(resources)
+                largeClock.events.onColorPaletteChanged(resources)
+            }
+
+            override fun onSeedColorChanged(seedColor: Int?) {
+                assets.setSeedColor(seedColor, design.colorPalette)
+                smallClock.assets.setSeedColor(seedColor, design.colorPalette)
+                largeClock.assets.setSeedColor(seedColor, design.colorPalette)
+
+                smallClock.events.onSeedColorChanged(seedColor)
+                largeClock.events.onSeedColorChanged(seedColor)
+            }
+
+            override fun onWeatherDataChanged(data: WeatherData) {
+                smallClock.events.onWeatherDataChanged(data)
+                largeClock.events.onWeatherDataChanged(data)
+            }
+
+            override fun onAlarmDataChanged(data: AlarmData) {
+                smallClock.events.onAlarmDataChanged(data)
+                largeClock.events.onAlarmDataChanged(data)
+            }
+
+            override fun onZenDataChanged(data: ZenData) {
+                smallClock.events.onZenDataChanged(data)
+                largeClock.events.onZenDataChanged(data)
+            }
+
+            override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {
+                smallClock.events.onReactiveAxesChanged(axes)
+                largeClock.events.onReactiveAxesChanged(axes)
+            }
+        }
+
+    override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
+        events.onColorPaletteChanged(resources)
+        smallClock.animations.doze(dozeFraction)
+        largeClock.animations.doze(dozeFraction)
+        smallClock.animations.fold(foldFraction)
+        largeClock.animations.fold(foldFraction)
+        smallClock.events.onTimeTick()
+        largeClock.events.onTimeTick()
+    }
+
+    override fun dump(pw: PrintWriter) {}
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
new file mode 100644
index 0000000..9067fb0
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.widget.FrameLayout
+import com.android.systemui.customization.R
+import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockEvents
+import com.android.systemui.plugins.clocks.ClockFaceConfig
+import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockFaceLayout
+import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
+import com.android.systemui.plugins.clocks.WeatherData
+import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.view.FlexClockView
+import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
+import java.util.Locale
+import java.util.TimeZone
+import kotlin.math.max
+
+// TODO(b/364680879): Merge w/ ComposedDigitalLayerController
+class FlexClockFaceController(
+    ctx: Context,
+    private val resources: Resources,
+    val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+    face: ClockFace,
+    private val isLargeClock: Boolean,
+    messageBuffer: MessageBuffer,
+) : ClockFaceController {
+    override val view: View
+        get() = layerController.view
+
+    override val config =
+        ClockFaceConfig(
+            hasCustomPositionUpdatedAnimation = false // TODO(b/364673982)
+        )
+
+    private val keyguardLargeClockTopMargin =
+        resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)
+    val layerController: SimpleClockLayerController
+    val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm")
+
+    init {
+        val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+        lp.gravity = Gravity.CENTER
+
+        val layer = face.layers[0]
+
+        layerController =
+            if (isLargeClock) {
+                ComposedDigitalLayerController(
+                    ctx,
+                    resources,
+                    assets,
+                    layer as ComposedDigitalHandLayer,
+                    messageBuffer,
+                )
+            } else {
+                val childView = SimpleDigitalClockTextView(ctx, messageBuffer)
+                SimpleDigitalHandLayerController(
+                    ctx,
+                    resources,
+                    assets,
+                    layer as DigitalHandLayer,
+                    childView,
+                    messageBuffer,
+                )
+            }
+        layerController.view.layoutParams = lp
+    }
+
+    override val layout: ClockFaceLayout =
+        DefaultClockFaceLayout(view).apply {
+            views[0].id =
+                if (isLargeClock) R.id.lockscreen_clock_view_large else R.id.lockscreen_clock_view
+        }
+
+    override val events = FlexClockFaceEvents()
+
+    // TODO(b/364680879): Remove ClockEvents
+    inner class FlexClockFaceEvents : ClockEvents, ClockFaceEvents {
+        override var isReactiveTouchInteractionEnabled = false
+            get() = field
+            set(value) {
+                field = value
+                layerController.events.isReactiveTouchInteractionEnabled = value
+            }
+
+        override fun onTimeTick() {
+            timespecHandler.updateTime()
+            view.contentDescription = timespecHandler.getContentDescription()
+            layerController.faceEvents.onTimeTick()
+        }
+
+        override fun onTimeZoneChanged(timeZone: TimeZone) {
+            timespecHandler.timeZone = timeZone
+            layerController.events.onTimeZoneChanged(timeZone)
+        }
+
+        override fun onTimeFormatChanged(is24Hr: Boolean) {
+            timespecHandler.is24Hr = is24Hr
+            layerController.events.onTimeFormatChanged(is24Hr)
+        }
+
+        override fun onLocaleChanged(locale: Locale) {
+            timespecHandler.updateLocale(locale)
+            layerController.events.onLocaleChanged(locale)
+        }
+
+        override fun onFontSettingChanged(fontSizePx: Float) {
+            layerController.faceEvents.onFontSettingChanged(fontSizePx)
+        }
+
+        override fun onColorPaletteChanged(resources: Resources) {
+            layerController.events.onColorPaletteChanged(resources)
+            layerController.updateColors()
+        }
+
+        override fun onSeedColorChanged(seedColor: Int?) {
+            layerController.events.onSeedColorChanged(seedColor)
+            layerController.updateColors()
+        }
+
+        override fun onRegionDarknessChanged(isRegionDark: Boolean) {
+            layerController.faceEvents.onRegionDarknessChanged(isRegionDark)
+        }
+
+        override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+
+        /**
+         * targetRegion passed to all customized clock applies counter translationY of
+         * KeyguardStatusView and keyguard_large_clock_top_margin from default clock
+         */
+        override fun onTargetRegionChanged(targetRegion: Rect?) {
+            // When a clock needs to be aligned with screen, like weather clock
+            // it needs to offset back the translation of keyguard_large_clock_top_margin
+            if (isLargeClock && (view as FlexClockView).isAlignedWithScreen()) {
+                val topMargin = keyguardLargeClockTopMargin
+                targetRegion?.let {
+                    val (_, yDiff) = computeLayoutDiff(view, it, isLargeClock)
+                    // In LS, we use yDiff to counter translate
+                    // the translation of KeyguardLargeClockTopMargin
+                    // With the targetRegion passed from picker,
+                    // we will have yDiff = 0, no translation is needed for weather clock
+                    if (yDiff.toInt() != 0) view.translationY = yDiff - topMargin / 2
+                }
+                return
+            }
+
+            var maxWidth = 0f
+            var maxHeight = 0f
+
+            layerController.faceEvents.onTargetRegionChanged(targetRegion)
+            maxWidth = max(maxWidth, view.layoutParams.width.toFloat())
+            maxHeight = max(maxHeight, view.layoutParams.height.toFloat())
+
+            val lp =
+                if (maxHeight <= 0 || maxWidth <= 0 || targetRegion == null) {
+                    // No specified width/height. Just match parent size.
+                    FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+                } else {
+                    // Scale to fit in targetRegion based on largest child elements.
+                    val ratio = maxWidth / maxHeight
+                    val targetRatio = targetRegion.width() / targetRegion.height().toFloat()
+                    val scale =
+                        if (ratio > targetRatio) targetRegion.width() / maxWidth
+                        else targetRegion.height() / maxHeight
+
+                    FrameLayout.LayoutParams(
+                        (maxWidth * scale).toInt(),
+                        (maxHeight * scale).toInt(),
+                    )
+                }
+
+            lp.gravity = Gravity.CENTER
+            view.layoutParams = lp
+            targetRegion?.let {
+                val (xDiff, yDiff) = computeLayoutDiff(view, it, isLargeClock)
+                view.translationX = xDiff
+                view.translationY = yDiff
+            }
+        }
+
+        override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {}
+
+        override fun onWeatherDataChanged(data: WeatherData) {
+            layerController.events.onWeatherDataChanged(data)
+        }
+
+        override fun onAlarmDataChanged(data: AlarmData) {
+            layerController.events.onAlarmDataChanged(data)
+        }
+
+        override fun onZenDataChanged(data: ZenData) {
+            layerController.events.onZenDataChanged(data)
+        }
+    }
+
+    override val animations =
+        object : ClockAnimations {
+            override fun enter() {
+                layerController.animations.enter()
+            }
+
+            override fun doze(fraction: Float) {
+                layerController.animations.doze(fraction)
+            }
+
+            override fun fold(fraction: Float) {
+                layerController.animations.fold(fraction)
+            }
+
+            override fun charge() {
+                layerController.animations.charge()
+            }
+
+            override fun onPickerCarouselSwiping(swipingFraction: Float) {
+                face.pickerScale?.let {
+                    view.scaleX = swipingFraction * (1 - it.scaleX) + it.scaleX
+                    view.scaleY = swipingFraction * (1 - it.scaleY) + it.scaleY
+                }
+                if (isLargeClock && !(view as FlexClockView).isAlignedWithScreen()) {
+                    view.translationY = keyguardLargeClockTopMargin / 2F * swipingFraction
+                }
+                layerController.animations.onPickerCarouselSwiping(swipingFraction)
+                view.invalidate()
+            }
+
+            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
+                layerController.animations.onPositionUpdated(fromLeft, direction, fraction)
+            }
+
+            override fun onPositionUpdated(distance: Float, fraction: Float) {
+                layerController.animations.onPositionUpdated(distance, fraction)
+            }
+        }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/LayoutUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/LayoutUtils.kt
new file mode 100644
index 0000000..ef8bee0
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/LayoutUtils.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.graphics.Rect
+import android.view.View
+
+fun computeLayoutDiff(
+    view: View,
+    targetRegion: Rect,
+    isLargeClock: Boolean,
+): Pair<Float, Float> {
+    val parent = view.parent
+    if (parent is View && parent.isLaidOut() && isLargeClock) {
+        return Pair(
+            targetRegion.centerX() - parent.width / 2f,
+            targetRegion.centerY() - parent.height / 2f
+        )
+    }
+    return Pair(0f, 0f)
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
new file mode 100644
index 0000000..5d1a2db
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockEvents
+import com.android.systemui.plugins.clocks.ClockFaceConfig
+import com.android.systemui.plugins.clocks.ClockFaceEvents
+
+interface SimpleClockLayerController {
+    val view: View
+    val events: ClockEvents
+    val animations: ClockAnimations
+    val faceEvents: ClockFaceEvents
+    val config: ClockFaceConfig
+
+    @VisibleForTesting var fakeTimeMills: Long?
+
+    // Called immediately after either onColorPaletteChanged or onSeedColorChanged is called.
+    // Provided for convience to not duplicate color update logic after state updated.
+    fun updateColors() {}
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockRelativeLayout.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockRelativeLayout.kt
new file mode 100644
index 0000000..6e1b9aa
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockRelativeLayout.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.content.Context
+import android.view.View.MeasureSpec.EXACTLY
+import android.widget.RelativeLayout
+import androidx.core.view.children
+import com.android.systemui.shared.clocks.view.SimpleDigitalClockView
+
+class SimpleClockRelativeLayout(context: Context, val faceLayout: DigitalFaceLayout?) :
+    RelativeLayout(context) {
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        // For migrate_clocks_to_blueprint, mode is EXACTLY
+        // when the flag is turned off, we won't execute this codes
+        if (MeasureSpec.getMode(heightMeasureSpec) == EXACTLY) {
+            if (
+                faceLayout == DigitalFaceLayout.TWO_PAIRS_VERTICAL ||
+                    faceLayout == DigitalFaceLayout.FOUR_DIGITS_ALIGN_CENTER
+            ) {
+                val constrainedHeight = MeasureSpec.getSize(heightMeasureSpec) / 2F
+                children.forEach {
+                    // The assumption here is the height of text view is linear to font size
+                    (it as SimpleDigitalClockView).applyTextSize(
+                        constrainedHeight,
+                        constrainedByHeight = true,
+                    )
+                }
+            }
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+    }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
new file mode 100644
index 0000000..ce1eae4
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewGroup
+import android.widget.RelativeLayout
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.customization.R
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockEvents
+import com.android.systemui.plugins.clocks.ClockFaceConfig
+import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.WeatherData
+import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.view.SimpleDigitalClockView
+import java.util.Locale
+import java.util.TimeZone
+
+private val TAG = SimpleDigitalHandLayerController::class.simpleName!!
+
+open class SimpleDigitalHandLayerController<T>(
+    private val ctx: Context,
+    private val resources: Resources,
+    private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources
+    private val layer: DigitalHandLayer,
+    override val view: T,
+    messageBuffer: MessageBuffer,
+) : SimpleClockLayerController where T : View, T : SimpleDigitalClockView {
+    private val logger = Logger(messageBuffer, TAG)
+    val timespec = DigitalTimespecHandler(layer.timespec, layer.dateTimeFormat)
+
+    @VisibleForTesting
+    fun hasLeadingZero() = layer.dateTimeFormat.startsWith("hh") || timespec.is24Hr
+
+    @VisibleForTesting
+    override var fakeTimeMills: Long?
+        get() = timespec.fakeTimeMills
+        set(value) {
+            timespec.fakeTimeMills = value
+        }
+
+    override val config = ClockFaceConfig()
+    var dozeState: DefaultClockController.AnimationState? = null
+    var isRegionDark: Boolean = true
+
+    init {
+        view.layoutParams =
+            RelativeLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+            )
+        if (layer.alignment != null) {
+            layer.alignment.verticalAlignment?.let { view.verticalAlignment = it }
+            layer.alignment.horizontalAlignment?.let { view.horizontalAlignment = it }
+        }
+        view.applyStyles(assets, layer.style, layer.aodStyle)
+        view.id =
+            ctx.resources.getIdentifier(
+                generateDigitalLayerIdString(layer),
+                "id",
+                ctx.getPackageName(),
+            )
+    }
+
+    fun applyLayout(layout: DigitalFaceLayout?) {
+        when (layout) {
+            DigitalFaceLayout.FOUR_DIGITS_ALIGN_CENTER,
+            DigitalFaceLayout.FOUR_DIGITS_HORIZONTAL -> applyFourDigitsLayout(layout)
+            DigitalFaceLayout.TWO_PAIRS_HORIZONTAL,
+            DigitalFaceLayout.TWO_PAIRS_VERTICAL -> applyTwoPairsLayout(layout)
+            else -> {
+                // one view always use FrameLayout
+                // no need to change here
+            }
+        }
+        applyMargin()
+    }
+
+    private fun applyMargin() {
+        if (view.layoutParams is RelativeLayout.LayoutParams) {
+            val lp = view.layoutParams as RelativeLayout.LayoutParams
+            layer.marginRatio?.let {
+                lp.setMargins(
+                    /* left = */ (it.left * view.measuredWidth).toInt(),
+                    /* top = */ (it.top * view.measuredHeight).toInt(),
+                    /* right = */ (it.right * view.measuredWidth).toInt(),
+                    /* bottom = */ (it.bottom * view.measuredHeight).toInt(),
+                )
+            }
+            view.layoutParams = lp
+        }
+    }
+
+    private fun applyTwoPairsLayout(twoPairsLayout: DigitalFaceLayout) {
+        val lp = view.layoutParams as RelativeLayout.LayoutParams
+        lp.addRule(RelativeLayout.TEXT_ALIGNMENT_CENTER)
+        if (twoPairsLayout == DigitalFaceLayout.TWO_PAIRS_HORIZONTAL) {
+            when (view.id) {
+                R.id.HOUR_DIGIT_PAIR -> {
+                    lp.addRule(RelativeLayout.CENTER_VERTICAL)
+                    lp.addRule(RelativeLayout.ALIGN_PARENT_START)
+                }
+                R.id.MINUTE_DIGIT_PAIR -> {
+                    lp.addRule(RelativeLayout.CENTER_VERTICAL)
+                    lp.addRule(RelativeLayout.END_OF, R.id.HOUR_DIGIT_PAIR)
+                }
+                else -> {
+                    throw Exception("cannot apply two pairs layout to view ${view.id}")
+                }
+            }
+        } else {
+            when (view.id) {
+                R.id.HOUR_DIGIT_PAIR -> {
+                    lp.addRule(RelativeLayout.CENTER_HORIZONTAL)
+                    lp.addRule(RelativeLayout.ALIGN_PARENT_TOP)
+                }
+                R.id.MINUTE_DIGIT_PAIR -> {
+                    lp.addRule(RelativeLayout.CENTER_HORIZONTAL)
+                    lp.addRule(RelativeLayout.BELOW, R.id.HOUR_DIGIT_PAIR)
+                }
+                else -> {
+                    throw Exception("cannot apply two pairs layout to view ${view.id}")
+                }
+            }
+        }
+        view.layoutParams = lp
+    }
+
+    private fun applyFourDigitsLayout(fourDigitsfaceLayout: DigitalFaceLayout) {
+        val lp = view.layoutParams as RelativeLayout.LayoutParams
+        when (fourDigitsfaceLayout) {
+            DigitalFaceLayout.FOUR_DIGITS_ALIGN_CENTER -> {
+                when (view.id) {
+                    R.id.HOUR_FIRST_DIGIT -> {
+                        lp.addRule(RelativeLayout.ALIGN_PARENT_START)
+                        lp.addRule(RelativeLayout.ALIGN_PARENT_TOP)
+                    }
+                    R.id.HOUR_SECOND_DIGIT -> {
+                        lp.addRule(RelativeLayout.END_OF, R.id.HOUR_FIRST_DIGIT)
+                        lp.addRule(RelativeLayout.ALIGN_TOP, R.id.HOUR_FIRST_DIGIT)
+                    }
+                    R.id.MINUTE_FIRST_DIGIT -> {
+                        lp.addRule(RelativeLayout.ALIGN_START, R.id.HOUR_FIRST_DIGIT)
+                        lp.addRule(RelativeLayout.BELOW, R.id.HOUR_FIRST_DIGIT)
+                    }
+                    R.id.MINUTE_SECOND_DIGIT -> {
+                        lp.addRule(RelativeLayout.ALIGN_START, R.id.HOUR_SECOND_DIGIT)
+                        lp.addRule(RelativeLayout.BELOW, R.id.HOUR_SECOND_DIGIT)
+                    }
+                    else -> {
+                        throw Exception("cannot apply four digits layout to view ${view.id}")
+                    }
+                }
+            }
+            DigitalFaceLayout.FOUR_DIGITS_HORIZONTAL -> {
+                when (view.id) {
+                    R.id.HOUR_FIRST_DIGIT -> {
+                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
+                        lp.addRule(RelativeLayout.ALIGN_PARENT_START)
+                    }
+                    R.id.HOUR_SECOND_DIGIT -> {
+                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
+                        lp.addRule(RelativeLayout.END_OF, R.id.HOUR_FIRST_DIGIT)
+                    }
+                    R.id.MINUTE_FIRST_DIGIT -> {
+                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
+                        lp.addRule(RelativeLayout.END_OF, R.id.HOUR_SECOND_DIGIT)
+                    }
+                    R.id.MINUTE_SECOND_DIGIT -> {
+                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
+                        lp.addRule(RelativeLayout.END_OF, R.id.MINUTE_FIRST_DIGIT)
+                    }
+                    else -> {
+                        throw Exception("cannot apply FOUR_DIGITS_HORIZONTAL to view ${view.id}")
+                    }
+                }
+            }
+            else -> {
+                throw IllegalArgumentException(
+                    "applyFourDigitsLayout function should not " +
+                        "have parameters as ${layer.faceLayout}"
+                )
+            }
+        }
+        if (lp == view.layoutParams) {
+            return
+        }
+        view.layoutParams = lp
+    }
+
+    fun refreshTime() {
+        timespec.updateTime()
+        val text = timespec.getDigitString()
+        if (view.text != text) {
+            view.text = text
+            view.refreshTime()
+            logger.d({ "refreshTime: new text=$str1" }) { str1 = text }
+        }
+    }
+
+    override val events =
+        object : ClockEvents {
+            override var isReactiveTouchInteractionEnabled = false
+
+            override fun onLocaleChanged(locale: Locale) {
+                timespec.updateLocale(locale)
+                refreshTime()
+            }
+
+            /** Call whenever the text time format changes (12hr vs 24hr) */
+            override fun onTimeFormatChanged(is24Hr: Boolean) {
+                timespec.is24Hr = is24Hr
+                refreshTime()
+            }
+
+            override fun onTimeZoneChanged(timeZone: TimeZone) {
+                timespec.timeZone = timeZone
+                refreshTime()
+            }
+
+            override fun onColorPaletteChanged(resources: Resources) {}
+
+            override fun onSeedColorChanged(seedColor: Int?) {}
+
+            override fun onWeatherDataChanged(data: WeatherData) {}
+
+            override fun onAlarmDataChanged(data: AlarmData) {}
+
+            override fun onZenDataChanged(data: ZenData) {}
+
+            override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+        }
+
+    override fun updateColors() {
+        view.updateColors(assets, isRegionDark)
+        refreshTime()
+    }
+
+    override val animations =
+        object : ClockAnimations {
+            override fun enter() {
+                applyLayout(layer.faceLayout)
+                refreshTime()
+            }
+
+            override fun doze(fraction: Float) {
+                if (dozeState == null) {
+                    dozeState = DefaultClockController.AnimationState(fraction)
+                    view.animateDoze(dozeState!!.isActive, false)
+                } else {
+                    val (hasChanged, hasJumped) = dozeState!!.update(fraction)
+                    if (hasChanged) view.animateDoze(dozeState!!.isActive, !hasJumped)
+                }
+                view.dozeFraction = fraction
+            }
+
+            override fun fold(fraction: Float) {
+                applyLayout(layer.faceLayout)
+                refreshTime()
+            }
+
+            override fun charge() {
+                view.animateCharge()
+            }
+
+            override fun onPickerCarouselSwiping(swipingFraction: Float) {}
+
+            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
+
+            override fun onPositionUpdated(distance: Float, fraction: Float) {}
+        }
+
+    override val faceEvents =
+        object : ClockFaceEvents {
+            override fun onTimeTick() {
+                refreshTime()
+                if (
+                    layer.timespec == DigitalTimespec.TIME_FULL_FORMAT ||
+                        layer.timespec == DigitalTimespec.DATE_FORMAT
+                ) {
+                    view.contentDescription = timespec.getContentDescription()
+                }
+            }
+
+            override fun onFontSettingChanged(fontSizePx: Float) {
+                view.applyTextSize(fontSizePx)
+                applyMargin()
+            }
+
+            override fun onRegionDarknessChanged(isRegionDark: Boolean) {
+                this@SimpleDigitalHandLayerController.isRegionDark = isRegionDark
+                updateColors()
+            }
+
+            override fun onTargetRegionChanged(targetRegion: Rect?) {}
+
+            override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {}
+        }
+
+    companion object {
+        private val DEFAULT_LIGHT_COLOR = "@android:color/system_accent1_100+0"
+        private val DEFAULT_DARK_COLOR = "@android:color/system_accent2_600+0"
+
+        fun getDefaultColor(assets: AssetLoader, isRegionDark: Boolean) =
+            assets.readColor(if (isRegionDark) DEFAULT_LIGHT_COLOR else DEFAULT_DARK_COLOR)
+    }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TimespecHandler.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TimespecHandler.kt
new file mode 100644
index 0000000..ed6a403
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TimespecHandler.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import android.icu.text.DateFormat
+import android.icu.text.SimpleDateFormat
+import android.icu.util.TimeZone as IcuTimeZone
+import android.icu.util.ULocale
+import androidx.annotation.VisibleForTesting
+import java.util.Calendar
+import java.util.Locale
+import java.util.TimeZone
+
+open class TimespecHandler(
+    val cal: Calendar,
+) {
+    var timeZone: TimeZone
+        get() = cal.timeZone
+        set(value) {
+            cal.timeZone = value
+            onTimeZoneChanged()
+        }
+
+    @VisibleForTesting var fakeTimeMills: Long? = null
+
+    fun updateTime() {
+        var timeMs = fakeTimeMills ?: System.currentTimeMillis()
+        cal.timeInMillis = (timeMs * TIME_TRAVEL_SCALE).toLong()
+    }
+
+    protected open fun onTimeZoneChanged() {}
+
+    companion object {
+        // Modifying this will cause the clock to run faster or slower. This is a useful way of
+        // manually checking that clocks are correctly animating through time.
+        private const val TIME_TRAVEL_SCALE = 1.0
+    }
+}
+
+class DigitalTimespecHandler(
+    val timespec: DigitalTimespec,
+    private val timeFormat: String,
+    cal: Calendar = Calendar.getInstance(),
+) : TimespecHandler(cal) {
+    var is24Hr = false
+        set(value) {
+            field = value
+            applyPattern()
+        }
+
+    private var dateFormat = updateSimpleDateFormat(Locale.getDefault())
+    private var contentDescriptionFormat = getContentDescriptionFormat(Locale.getDefault())
+
+    init {
+        applyPattern()
+    }
+
+    override fun onTimeZoneChanged() {
+        dateFormat.timeZone = IcuTimeZone.getTimeZone(cal.timeZone.id)
+        contentDescriptionFormat?.timeZone = IcuTimeZone.getTimeZone(cal.timeZone.id)
+        applyPattern()
+    }
+
+    fun updateLocale(locale: Locale) {
+        dateFormat = updateSimpleDateFormat(locale)
+        contentDescriptionFormat = getContentDescriptionFormat(locale)
+        onTimeZoneChanged()
+    }
+
+    private fun updateSimpleDateFormat(locale: Locale): DateFormat {
+        if (
+            locale.language.equals(Locale.ENGLISH.language) ||
+                timespec != DigitalTimespec.DATE_FORMAT
+        ) {
+            // force date format in English, and time format to use format defined in json
+            return SimpleDateFormat(timeFormat, timeFormat, ULocale.forLocale(locale))
+        } else {
+            return SimpleDateFormat.getInstanceForSkeleton(timeFormat, locale)
+        }
+    }
+
+    private fun getContentDescriptionFormat(locale: Locale): DateFormat? {
+        return when (timespec) {
+            DigitalTimespec.TIME_FULL_FORMAT ->
+                SimpleDateFormat.getInstanceForSkeleton("hh:mm", locale)
+            DigitalTimespec.DATE_FORMAT ->
+                SimpleDateFormat.getInstanceForSkeleton("EEEE MMMM d", locale)
+            else -> {
+                null
+            }
+        }
+    }
+
+    private fun applyPattern() {
+        val timeFormat24Hour = timeFormat.replace("hh", "h").replace("h", "HH")
+        val format = if (is24Hr) timeFormat24Hour else timeFormat
+        if (timespec != DigitalTimespec.DATE_FORMAT) {
+            (dateFormat as SimpleDateFormat).applyPattern(format)
+            (contentDescriptionFormat as? SimpleDateFormat)?.applyPattern(
+                if (is24Hr) CONTENT_DESCRIPTION_TIME_FORMAT_24_HOUR
+                else CONTENT_DESCRIPTION_TIME_FORMAT_12_HOUR
+            )
+        }
+    }
+
+    private fun getSingleDigit(): String {
+        val isFirstDigit = timespec == DigitalTimespec.FIRST_DIGIT
+        val text = dateFormat.format(cal.time).toString()
+        return text.substring(
+            if (isFirstDigit) 0 else text.length - 1,
+            if (isFirstDigit) text.length - 1 else text.length
+        )
+    }
+
+    fun getDigitString(): String {
+        return when (timespec) {
+            DigitalTimespec.FIRST_DIGIT,
+            DigitalTimespec.SECOND_DIGIT -> getSingleDigit()
+            DigitalTimespec.DIGIT_PAIR -> {
+                dateFormat.format(cal.time).toString()
+            }
+            DigitalTimespec.TIME_FULL_FORMAT -> {
+                dateFormat.format(cal.time).toString()
+            }
+            DigitalTimespec.DATE_FORMAT -> {
+                dateFormat.format(cal.time).toString().uppercase()
+            }
+        }
+    }
+
+    fun getContentDescription(): String? {
+        return when (timespec) {
+            DigitalTimespec.TIME_FULL_FORMAT,
+            DigitalTimespec.DATE_FORMAT -> {
+                contentDescriptionFormat?.format(cal.time).toString()
+            }
+            else -> {
+                return null
+            }
+        }
+    }
+
+    companion object {
+        const val CONTENT_DESCRIPTION_TIME_FORMAT_12_HOUR = "hh:mm"
+        const val CONTENT_DESCRIPTION_TIME_FORMAT_24_HOUR = "HH:mm"
+    }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
index eb72346..81efcb9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.shared.clocks.LogUtil
 import java.util.Locale
 
+// TODO(b/364680879): Merge w/ only subclass FlexClockView
 abstract class DigitalClockFaceView(ctx: Context, messageBuffer: MessageBuffer) : FrameLayout(ctx) {
     protected val logger = Logger(messageBuffer, this::class.simpleName!!)
         get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
@@ -140,7 +141,6 @@
     open val useCustomClockScene
         get() = false
 
-    // TODO: implement ClockEventUnion?
     open fun onLocaleChanged(locale: Locale) {}
 
     open fun onWeatherDataChanged(data: WeatherData) {}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index c29c8da..25b2ad7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -35,7 +35,7 @@
 
 fun clamp(value: Float, minVal: Float, maxVal: Float): Float = max(min(value, maxVal), minVal)
 
-class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffer: MessageBuffer) :
+class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: MessageBuffer) :
     DigitalClockFaceView(context, messageBuffer) {
     override var digitalClockTextViewMap = mutableMapOf<Int, SimpleDigitalClockTextView>()
     val digitLeftTopMap = mutableMapOf<Int, Point>()
@@ -57,11 +57,9 @@
     private var prevY = 0f
     private var isDown = false
 
-    // TODO(b/340253296): Genericize; json spec
     private var wght = 603f
     private var wdth = 100f
 
-    // TODO(b/340253296): Json spec
     private val MAX_WGHT = 950f
     private val MIN_WGHT = 50f
     private val WGHT_SCALE = 0.5f
@@ -71,7 +69,6 @@
     private val WDTH_SCALE = 0.2f
 
     override fun onTouchEvent(evt: MotionEvent): Boolean {
-        // TODO(b/340253296): implement on DigitalClockFaceView?
         if (!isReactiveTouchInteractionEnabled) {
             return super.onTouchEvent(evt)
         }
@@ -94,12 +91,11 @@
                 prevX = evt.x
                 prevY = evt.y
 
-                // TODO(b/340253296): Genericize; json spec
                 val fvar = "'wght' $wght, 'wdth' $wdth, 'opsz' 144, 'ROND' 100"
                 digitalClockTextViewMap.forEach { (_, view) ->
                     val textStyle = view.textStyle as FontTextStyle
                     textStyle.fontVariation = fvar
-                    view.applyStyles(assetLoader, textStyle, view.aodStyle)
+                    view.applyStyles(assets, textStyle, view.aodStyle)
                 }
 
                 requestLayout()
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
index 234c7a0..bf15b4f 100644
--- a/packages/SystemUI/docs/scene.md
+++ b/packages/SystemUI/docs/scene.md
@@ -63,7 +63,7 @@
 NOTE: in case these instructions become stale and don't actually enable the
 framework, please make sure `SceneContainerFlag.isEnabled` in the
 [`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt)
-file evalutes to `true`.
+file evaluates to `true`.
 
 1.  Set a collection of **aconfig flags** to `true` by running the following
     commands:
@@ -73,7 +73,6 @@
     $ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true
     $ adb shell device_config override systemui com.android.systemui.notification_avalanche_throttle_hun true
     $ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true
-    $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true
     $ adb shell device_config override systemui com.android.systemui.scene_container true
     ```
 2.  **Restart** System UI by issuing the following command:
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 2bb9e68..ce57fe2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -18,8 +18,6 @@
 
 import static android.view.View.INVISIBLE;
 
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
@@ -156,8 +154,12 @@
         when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility))
                 .thenReturn(INVISIBLE);
 
-        when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame);
-        when(mView.findViewById(R.id.lockscreen_clock_view)).thenReturn(mSmallClockFrame);
+        when(mView
+                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large))
+                .thenReturn(mLargeClockFrame);
+        when(mView
+                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view))
+                .thenReturn(mSmallClockFrame);
         when(mSmallClockView.getContext()).thenReturn(getContext());
         when(mLargeClockView.getContext()).thenReturn(getContext());
 
@@ -167,7 +169,6 @@
         when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
         mExecutor = new FakeExecutor(new FakeSystemClock());
         mFakeFeatureFlags = new FakeFeatureFlags();
-        mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
         mController = new KeyguardClockSwitchController(
                 mView,
                 mStatusBarStateController,
@@ -184,7 +185,6 @@
                 mLogBuffer,
                 KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(),
                 mKeyguardClockInteractor,
-                mFakeFeatureFlags,
                 mock(InWindowLauncherUnlockAnimationManager.class)
         );
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
deleted file mode 100644
index c2c0f57..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard
-
-import android.view.View
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class KeyguardClockSwitchControllerWithCoroutinesTest : KeyguardClockSwitchControllerBaseTest() {
-
-    @Test
-    fun testStatusAreaVisibility_onLockscreenHostedDreamStateChanged() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN starting state for the keyguard clock and wallpaper dream enabled
-            mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true)
-            init()
-
-            // WHEN dreaming starts
-            mController.mIsActiveDreamLockscreenHostedCallback.accept(
-                true /* isActiveDreamLockscreenHosted */
-            )
-
-            // THEN the status area is hidden
-            mExecutor.runAllReady()
-            assertEquals(View.INVISIBLE, mStatusArea.visibility)
-
-            // WHEN dreaming stops
-            mController.mIsActiveDreamLockscreenHostedCallback.accept(
-                false /* isActiveDreamLockscreenHosted */
-            )
-            mExecutor.runAllReady()
-
-            // THEN status area view is visible
-            assertEquals(View.VISIBLE, mStatusArea.visibility)
-        }
-
-    companion object {
-        private val IMMEDIATE = Dispatchers.Main.immediate
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 0bf9d12..4ed5fd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -113,8 +113,10 @@
         });
         mKeyguardClockSwitch =
                 (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
-        mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
-        mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large);
+        mSmallClockFrame = mKeyguardClockSwitch
+                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view);
+        mLargeClockFrame = mKeyguardClockSwitch
+                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large);
         mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area);
         mKeyguardClockSwitch.mChildrenAreLaidOut = true;
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 2e41246..245388c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -17,10 +17,10 @@
 package com.android.keyguard
 
 import android.view.View
-import android.view.ViewGroup
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -88,7 +88,7 @@
         underTest.statusViewCentered = true
 
         val view = View(context)
-        whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
             view
         )
 
@@ -110,7 +110,7 @@
         whenever(statusBarStateController.getState()).thenReturn(SHADE)
 
         val view = View(context)
-        whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
             view
         )
 
@@ -134,7 +134,7 @@
         val view = View(context)
         whenever(
             notificationShadeWindowView
-                .findViewById<View>(R.id.lockscreen_clock_view_large)
+                .findViewById<View>(customR.id.lockscreen_clock_view_large)
         ).thenReturn(view)
 
         progressListener.onTransitionStarted()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 662815e..fcb433b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -46,7 +46,9 @@
 import android.provider.Settings;
 import android.testing.TestableLooper;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.LinearLayout;
+import android.widget.Space;
 import android.widget.Spinner;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -226,7 +228,7 @@
         mDialog.show();
 
         LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
-        assertThat(relatedToolsView.getChildCount()).isEqualTo(1);
+        assertThat(countChildWithoutSpace(relatedToolsView)).isEqualTo(1);
     }
 
     @Test
@@ -244,12 +246,13 @@
         when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
         when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable);
         when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
+        when(mDrawable.mutate()).thenReturn(mDrawable);
 
         setUpPairNewDeviceDialog();
         mDialog.show();
 
         LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
-        assertThat(relatedToolsView.getChildCount()).isEqualTo(2);
+        assertThat(countChildWithoutSpace(relatedToolsView)).isEqualTo(2);
     }
 
     @Test
@@ -364,6 +367,16 @@
         return dialog.requireViewById(R.id.preset_spinner);
     }
 
+    private int countChildWithoutSpace(ViewGroup viewGroup) {
+        int spaceCount = 0;
+        for (int i = 0; i < viewGroup.getChildCount(); i++) {
+            if (viewGroup.getChildAt(i) instanceof Space) {
+                spaceCount++;
+            }
+        }
+        return viewGroup.getChildCount() - spaceCount;
+    }
+
     @After
     public void reset() {
         if (mDialogDelegate != null) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
index 17ce1dd..77369f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
@@ -85,7 +85,7 @@
     }
 
     @Test
-    public void parseStringArray_noString_emptyResult() {
+    public void parseStringArray_noToolName_emptyResult() {
         assertThat(HearingDevicesToolItemParser.parseStringArray(mContext, new String[]{},
                 new String[]{})).isEqualTo(emptyList());
     }
@@ -103,14 +103,14 @@
     }
 
     @Test
-    public void parseStringArray_fourToolName_maxThreeToolItem() {
+    public void parseStringArray_threeToolNames_maxTwoToolItems() {
         String componentNameString = TEST_PKG + "/" + TEST_CLS;
-        String[] fourToolName =
-                new String[]{componentNameString, componentNameString, componentNameString,
-                        componentNameString};
+        String[] threeToolNames =
+                new String[]{componentNameString, componentNameString, componentNameString};
 
         List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
-                fourToolName, new String[]{});
+                threeToolNames, new String[]{});
+
         assertThat(toolItemList.size()).isEqualTo(HearingDevicesToolItemParser.MAX_NUM);
     }
 
@@ -120,6 +120,7 @@
 
         List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
                 wrongFormatToolName, new String[]{});
+
         assertThat(toolItemList.size()).isEqualTo(0);
     }
 
@@ -129,6 +130,7 @@
 
         List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
                 notExistToolName, new String[]{});
+
         assertThat(toolItemList.size()).isEqualTo(0);
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
deleted file mode 100644
index 13306be..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import org.junit.Assert.assertFalse
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-class UdfpsBpViewControllerTest : SysuiTestCase() {
-
-    @JvmField @Rule var rule = MockitoJUnit.rule()
-
-    @Mock lateinit var udfpsBpView: UdfpsBpView
-    @Mock lateinit var statusBarStateController: StatusBarStateController
-    @Mock lateinit var shadeInteractor: ShadeInteractor
-    @Mock lateinit var systemUIDialogManager: SystemUIDialogManager
-    @Mock lateinit var dumpManager: DumpManager
-    @Mock lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
-
-    private lateinit var udfpsBpViewController: UdfpsBpViewController
-
-    @Before
-    fun setup() {
-        udfpsBpViewController =
-            UdfpsBpViewController(
-                udfpsBpView,
-                statusBarStateController,
-                shadeInteractor,
-                systemUIDialogManager,
-                dumpManager,
-                udfpsOverlayInteractor,
-            )
-    }
-
-    @TestableLooper.RunWithLooper(setAsMainLooper = true)
-    @Test
-    fun testShouldNeverPauseAuth() {
-        assertFalse(udfpsBpViewController.shouldPauseAuth())
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 437a4b3..21c6583 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,34 +16,17 @@
 
 package com.android.systemui.biometrics;
 
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
-import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.graphics.Rect;
-import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricRequestConstants;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.SensorProperties;
@@ -58,13 +41,9 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.testing.TestableLooper.RunWithLooper;
-import android.util.Pair;
-import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 import android.view.accessibility.AccessibilityManager;
 
@@ -75,15 +54,11 @@
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.biometrics.udfps.InteractionEvent;
-import com.android.systemui.biometrics.udfps.NormalizedTouchData;
 import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
-import com.android.systemui.biometrics.udfps.TouchProcessorResult;
 import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel;
 import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -102,7 +77,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.power.shared.model.WakeSleepReason;
 import com.android.systemui.power.shared.model.WakefulnessState;
-import com.android.systemui.res.R;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.VibratorHelper;
@@ -130,7 +104,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
-import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -204,16 +177,6 @@
     private FeatureFlags mFeatureFlags;
     // Stuff for configuring mocks
     @Mock
-    private UdfpsView mUdfpsView;
-    @Mock
-    private UdfpsBpView mBpView;
-    @Mock
-    private UdfpsFpmEmptyView mFpmEmptyView;
-    @Mock
-    private UdfpsKeyguardViewLegacy mKeyguardView;
-    private final UdfpsAnimationViewController mUdfpsKeyguardViewController =
-            mock(UdfpsKeyguardViewControllerLegacy.class);
-    @Mock
     private SystemUIDialogManager mSystemUIDialogManager;
     @Mock
     private ActivityTransitionAnimator mActivityTransitionAnimator;
@@ -241,8 +204,6 @@
     @Captor
     private ArgumentCaptor<View> mViewCaptor;
     @Captor
-    private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
-    @Captor
     private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
     @Captor
     private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
@@ -287,18 +248,9 @@
         mContext.getOrCreateTestableResources()
                 .addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false);
 
-        when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
-                .thenReturn(mUdfpsView);
-        when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
-                .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
-        when(mLayoutInflater.inflate(R.layout.udfps_bp_view, null))
-                .thenReturn(mBpView);
-        when(mLayoutInflater.inflate(R.layout.udfps_fpm_empty_view, null))
-                .thenReturn(mFpmEmptyView);
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         when(mSessionTracker.getSessionId(anyInt())).thenReturn(
                 (new InstanceIdSequence(1 << 20)).newInstanceId());
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
 
         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
         componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
@@ -327,7 +279,6 @@
         // Create a fake background executor.
         mBiometricExecutor = new FakeExecutor(new FakeSystemClock());
 
-        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         initUdfpsController(mOpticalProps);
     }
 
@@ -349,7 +300,6 @@
                 mFalsingManager,
                 mPowerManager,
                 mAccessibilityManager,
-                mLockscreenShadeTransitionController,
                 mScreenLifecycle,
                 mVibrator,
                 mUdfpsHapticsSimulator,
@@ -390,101 +340,6 @@
     }
 
     @Test
-    public void dozeTimeTick() throws RemoteException {
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        mUdfpsController.dozeTimeTick();
-        verify(mUdfpsView).dozeTimeTick();
-    }
-
-    @Test
-    public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
-        // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN notify keyguard authenticate to dismiss the keyguard
-        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
-    }
-
-    @Test
-    public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
-        onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */);
-    }
-
-    @Test
-    public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale()
-            throws RemoteException {
-        onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */);
-    }
-
-    public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale)
-            throws RemoteException {
-        // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_MOVE is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
-        if (stale) {
-            mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
-            mFgExecutor.runAllReady();
-        }
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN notify keyguard authenticate to dismiss the keyguard
-        verify(mStatusBarKeyguardViewManager, stale ? never() : times(1))
-                .notifyKeyguardAuthenticated(anyBoolean());
-    }
-
-    @Test
-    public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException {
-        // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
-
-        // GIVEN that the overlay is showing
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.second);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN notify keyguard authenticate to dismiss the keyguard
-        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
-    }
-
-    @Test
     public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
         // GIVEN overlay was showing and the udfps bouncer is showing
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
@@ -524,61 +379,6 @@
     }
 
     @Test
-    public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception {
-        final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)};
-        final int[] displayWidth = new int[]{1080, 1440};
-        final int[] displayHeight = new int[]{1920, 2560};
-        final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
-        final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
-        final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
-                sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0],
-                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
-
-        for (int i1 = 0; i1 <= 1; ++i1) {
-            for (int i2 = 0; i2 <= 1; ++i2) {
-                for (int i3 = 0; i3 <= 1; ++i3) {
-                    for (int i4 = 0; i4 <= 1; ++i4) {
-                        for (int i5 = 0; i5 <= 1; ++i5) {
-                            final UdfpsOverlayParams newParams = new UdfpsOverlayParams(
-                                    sensorBounds[i1], sensorBounds[i1], displayWidth[i2],
-                                    displayHeight[i3], scaleFactor[i4], rotation[i5],
-                                    FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
-
-                            if (newParams.equals(oldParams)) {
-                                continue;
-                            }
-
-                            // Initialize the overlay with old parameters.
-                            mUdfpsController.updateOverlayParams(mOpticalProps, oldParams);
-
-                            // Show the overlay.
-                            reset(mWindowManager);
-                            mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID,
-                                    mOpticalProps.sensorId,
-                                    BiometricRequestConstants.REASON_ENROLL_ENROLLING,
-                                    mUdfpsOverlayControllerCallback);
-
-                            mFgExecutor.runAllReady();
-                            verify(mWindowManager).addView(mViewCaptor.capture(), any());
-                            when(mViewCaptor.getValue().getParent())
-                                    .thenReturn(mock(ViewGroup.class));
-
-                            // Update overlay parameters.
-                            reset(mWindowManager);
-                            mUdfpsController.updateOverlayParams(mOpticalProps, newParams);
-                            mFgExecutor.runAllReady();
-
-                            // Ensure the overlay was recreated.
-                            verify(mWindowManager).removeView(any());
-                            verify(mWindowManager).addView(any(), any());
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Test
     public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception {
         final Rect sensorBounds = new Rect(10, 10, 20, 20);
         final int displayWidth = 1080;
@@ -595,7 +395,6 @@
         mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
                 BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
         mFgExecutor.runAllReady();
-        verify(mWindowManager).addView(any(), any());
 
         // Update overlay with the same parameters.
         mUdfpsController.updateOverlayParams(mOpticalProps,
@@ -606,870 +405,4 @@
         // Ensure the overlay was not recreated.
         verify(mWindowManager, never()).removeView(any());
     }
-
-    private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor,
-            float major) {
-        MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
-        pp.id = 1;
-        MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
-        pc.x = x;
-        pc.y = y;
-        pc.touchMinor = minor;
-        pc.touchMajor = major;
-        return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp},
-                new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
-    }
-
-    private static class TestParams {
-        public final FingerprintSensorPropertiesInternal sensorProps;
-
-        TestParams(FingerprintSensorPropertiesInternal sensorProps) {
-            this.sensorProps = sensorProps;
-        }
-    }
-
-    private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) {
-        for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
-                mUltrasonicProps)) {
-            initUdfpsController(sensorProps);
-            testParamsConsumer.accept(new TestParams(sensorProps));
-        }
-    }
-
-    @Test
-    public void onTouch_propagatesTouchInNativeOrientationAndResolution() {
-        runWithAllParams(
-                this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized);
-    }
-
-    private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized(
-            TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
-        final int pointerId = 0;
-        final int displayWidth = 1080;
-        final int displayHeight = 1920;
-        final float scaleFactor = 1f; // This means the native resolution is 1440x2560.
-        final float touchMinor = 10f;
-        final float touchMajor = 20f;
-        final float orientation = 30f;
-
-        // Expecting a touch at the very bottom right corner in native orientation and resolution.
-        final float expectedX = displayWidth / scaleFactor;
-        final float expectedY = displayHeight / scaleFactor;
-        final float expectedMinor = touchMinor / scaleFactor;
-        final float expectedMajor = touchMajor / scaleFactor;
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-        // GIVEN a valid touch on sensor
-        NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth,
-                displayHeight, touchMinor, touchMajor, orientation, 0L, 0L);
-        TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch(
-                InteractionEvent.DOWN, 1, touchData);
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorDownResult);
-
-        // Show the overlay.
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // Test ROTATION_0
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_0,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
-                touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-
-        // Test ROTATION_90
-        reset(mFingerprintManager);
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_90,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-
-        // Test ROTATION_270
-        reset(mFingerprintManager);
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_270,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-
-        // Test ROTATION_180
-        reset(mFingerprintManager);
-        mUdfpsController.updateOverlayParams(testParams.sensorProps,
-                new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
-                        scaleFactor, Surface.ROTATION_180,
-                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
-        // ROTATION_180 is not supported. It should be treated like ROTATION_0.
-        event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
-                eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void fingerDown() {
-        runWithAllParams(this::fingerDownParameterized);
-    }
-
-    private void fingerDownParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView, mFingerprintManager, mLatencyTracker,
-                mKeyguardUpdateMonitor);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
-                InteractionEvent.DOWN, 1 /* pointerId */, touchData);
-
-        initUdfpsController(testParams.sensorProps);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        // WHEN ACTION_DOWN is received
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN the touch provider is notified about onPointerDown.
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
-
-        // AND display configuration begins
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-            verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
-        } else {
-            verify(mLatencyTracker, never()).onActionStart(
-                    eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-            verify(mUdfpsView, never()).configureDisplay(any());
-        }
-        verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // AND onDisplayConfigured notifies FingerprintManager about onUiReady
-            mOnDisplayConfiguredCaptor.getValue().run();
-            mBiometricExecutor.runAllReady();
-            InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
-            inOrder.verify(mFingerprintManager).onUdfpsUiEvent(
-                    eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID),
-                    eq(testParams.sensorProps.sensorId));
-            inOrder.verify(mLatencyTracker).onActionEnd(
-                    eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-        } else {
-            verify(mFingerprintManager, never()).onUdfpsUiEvent(
-                    eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt());
-            verify(mLatencyTracker, never()).onActionEnd(
-                    eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-        }
-    }
-
-    @Test
-    public void aodInterrupt() {
-        runWithAllParams(this::aodInterruptParameterized);
-    }
-
-    private void aodInterruptParameterized(TestParams testParams) throws RemoteException {
-        mUdfpsController.cancelAodSendFingerUpAction();
-        reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor);
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        // GIVEN that the overlay is showing and screen is on and fp is running
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        // WHEN fingerprint is requested because of AOD interrupt
-        mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // THEN display configuration begins
-            // AND onDisplayConfigured notifies FingerprintManager about onUiReady
-            verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
-            mOnDisplayConfiguredCaptor.getValue().run();
-        } else {
-            verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture());
-        }
-        mBiometricExecutor.runAllReady();
-
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
-    }
-
-    @Test
-    public void tryAodSendFingerUp_displayConfigurationChanges() {
-        runWithAllParams(this::cancelAodInterruptParameterized);
-    }
-
-    private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN AOD interrupt
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-            // WHEN it is cancelled
-            mUdfpsController.tryAodSendFingerUp();
-            // THEN the display is unconfigured
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-            // WHEN it is cancelled
-            mUdfpsController.tryAodSendFingerUp();
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void onFingerUp_displayConfigurationChange() {
-        runWithAllParams(this::onFingerUp_displayConfigurationParameterized);
-    }
-
-    private void onFingerUp_displayConfigurationParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // GIVEN AOD interrupt
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-
-            // WHEN up-action received
-            when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                    touchProcessorResult.second);
-            MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
-            mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
-            mBiometricExecutor.runAllReady();
-            upEvent.recycle();
-            mFgExecutor.runAllReady();
-
-            // THEN the display is unconfigured
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-            // WHEN up-action received
-            when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                    touchProcessorResult.second);
-            MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
-            mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
-            mBiometricExecutor.runAllReady();
-            upEvent.recycle();
-            mFgExecutor.runAllReady();
-
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void onAcquiredGood_displayConfigurationChange() {
-        runWithAllParams(this::onAcquiredGood_displayConfigurationParameterized);
-    }
-
-    private void onAcquiredGood_displayConfigurationParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN overlay is showing
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-            // WHEN ACQUIRED_GOOD received
-            mOverlayController.onAcquired(testParams.sensorProps.sensorId,
-                    BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
-            mFgExecutor.runAllReady();
-            // THEN the display is unconfigured
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-            // WHEN ACQUIRED_GOOD received
-            mOverlayController.onAcquired(testParams.sensorProps.sensorId,
-                    BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
-            mFgExecutor.runAllReady();
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void aodInterruptTimeout() {
-        runWithAllParams(this::aodInterruptTimeoutParameterized);
-    }
-
-    private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN AOD interrupt
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        }
-        // WHEN it times out
-        mFgExecutor.advanceClockToNext();
-        mFgExecutor.runAllReady();
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // THEN the display is unconfigured.
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            // THEN the display configuration is unchanged.
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void aodInterruptCancelTimeoutActionOnFingerUp() {
-        runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized);
-    }
-
-    private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-        when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
-        // GIVEN AOD interrupt
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-        mFgExecutor.runAllReady();
-
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // Configure UdfpsView to accept the ACTION_UP event
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        }
-
-        // WHEN ACTION_UP is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.second);
-        MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
-        mBiometricExecutor.runAllReady();
-        upEvent.recycle();
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        mFgExecutor.runAllReady();
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // Configure UdfpsView to accept the finger up event
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        } else {
-            when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-        }
-
-        // WHEN it times out
-        mFgExecutor.advanceClockToNext();
-        mFgExecutor.runAllReady();
-
-        if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
-            // THEN the display should be unconfigured once. If the timeout action is not
-            // cancelled, the display would be unconfigured twice which would cause two
-            // FP attempts.
-            verify(mUdfpsView).unconfigureDisplay();
-        } else {
-            verify(mUdfpsView, never()).unconfigureDisplay();
-        }
-    }
-
-    @Test
-    public void aodInterruptScreenOff() {
-        runWithAllParams(this::aodInterruptScreenOffParameterized);
-    }
-
-    private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN screen off
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOff();
-        mFgExecutor.runAllReady();
-
-        // WHEN aod interrupt is received
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-
-        // THEN display doesn't get configured because it's off
-        verify(mUdfpsView, never()).configureDisplay(any());
-    }
-
-    @Test
-    public void aodInterrupt_fingerprintNotRunning() {
-        runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized);
-    }
-
-    private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams)
-            throws RemoteException {
-        reset(mUdfpsView);
-
-        // GIVEN showing overlay
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mScreenObserver.onScreenTurnedOn();
-        mFgExecutor.runAllReady();
-
-        // WHEN aod interrupt is received when the fingerprint service isn't running
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
-        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-
-        // THEN display doesn't get configured because it's off
-        verify(mUdfpsView, never()).configureDisplay(any());
-    }
-
-    @Test
-    public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true);
-
-        // WHEN ACTION_HOVER is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
-        MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
-        mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
-        enterEvent.recycle();
-
-        // THEN context click haptic is played
-        verify(mVibrator).performHapticFeedback(
-                any(),
-                eq(HapticFeedbackConstants.CONTEXT_CLICK)
-        );
-    }
-
-    @Test
-    public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN NO haptic played
-        verify(mVibrator, never()).performHapticFeedback(any(), anyInt());
-    }
-
-    @Test
-    public void fingerDown_falsingManagerInformed() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN falsing manager is informed of the touch
-        verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
-    }
-
-    private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent(
-            InteractionEvent event1, InteractionEvent event2, boolean a11y)
-            throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
-                event1, 1 /* pointerId */, touchData);
-        final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
-                event2, 1 /* pointerId */, touchData);
-
-        // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
-        initUdfpsController(mOpticalProps);
-
-        // Configure UdfpsView to accept the ACTION_DOWN event
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
-        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        if (a11y) {
-            verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
-        } else {
-            verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-        }
-
-        return new Pair<>(processorResultDown, processorResultUp);
-    }
-
-    @Test
-    public void onTouch_forwardToKeyguard() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
-                InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData);
-
-        // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-
-        // THEN the touch is forwarded to Keyguard
-        verify(mStatusBarKeyguardViewManager).onTouch(downEvent);
-    }
-
-    @Test
-    public void onTouch_pilferPointer() throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
-
-        // WHEN ACTION_DOWN is received
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-
-        // WHEN ACTION_MOVE is received after
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.second);
-        event.setAction(ACTION_MOVE);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
-        mBiometricExecutor.runAllReady();
-        event.recycle();
-
-        // THEN only pilfer once on the initial down
-        verify(mInputManager).pilferPointers(any());
-    }
-
-    @Test
-    public void onTouch_doNotPilferPointer() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultUnchanged =
-                new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
-                        -1 /* pointerId */, touchData);
-
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN ACTION_DOWN is received and touch is not within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultUnchanged);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN the touch is NOT pilfered
-        verify(mInputManager, never()).pilferPointers(any());
-    }
-
-    @Test
-    public void onDownTouchReceivedWithoutPreviousUp() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown =
-                new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
-                        -1 /* pointerId */, touchData);
-
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN ACTION_DOWN is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
-        mBiometricExecutor.runAllReady();
-        firstDownEvent.recycle();
-
-        // And another ACTION_DOWN is received without an ACTION_UP before
-        MotionEvent secondDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, secondDownEvent);
-        mBiometricExecutor.runAllReady();
-        secondDownEvent.recycle();
-
-        // THEN the touch is still processed
-        verify(mFingerprintManager, times(2)).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void onAodDownAndDownTouchReceived() throws RemoteException {
-        final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
-                0L);
-        final TouchProcessorResult processorResultDown =
-                new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
-                        -1 /* pointerId */, touchData);
-
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
-        // WHEN fingerprint is requested because of AOD interrupt
-        // GIVEN there's been an AoD interrupt
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-        mScreenObserver.onScreenTurnedOn();
-        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-        mFgExecutor.runAllReady();
-
-        // and an ACTION_DOWN is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                processorResultDown);
-        MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
-        mBiometricExecutor.runAllReady();
-        firstDownEvent.recycle();
-
-        // THEN the touch is only processed once
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void onTouch_pilferPointerWhenAltBouncerShowing()
-            throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
-
-        // WHEN alternate bouncer is showing
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // WHEN ACTION_DOWN is received and touch is not within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-        mBiometricExecutor.runAllReady();
-        downEvent.recycle();
-
-        // THEN the touch is pilfered
-        verify(mInputManager).pilferPointers(any());
-    }
-
-    @Test
-    public void onTouch_doNotProcessTouchWhenPullingUpBouncer()
-            throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
-
-        // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS
-        when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true);
-        when(mLockscreenShadeTransitionController.getFractionToShade()).thenReturn(1f);
-
-        // WHEN ACTION_MOVE is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN the touch is NOT processed
-        verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-
-    @Test
-    public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible()
-            throws RemoteException {
-        final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
-                givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        // GIVEN swipe down for QS
-        when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false);
-        when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f);
-
-        // WHEN ACTION_MOVE is received and touch is within sensor
-        when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
-                touchProcessorResult.first);
-        MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
-        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
-        mBiometricExecutor.runAllReady();
-        moveEvent.recycle();
-
-        // THEN the touch is still processed
-        verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
-                anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
-                anyBoolean());
-    }
-
-    @Test
-    public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
-        // GIVEN UDFPS overlay is showing
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        // GIVEN there's been an AoD interrupt
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-        mScreenObserver.onScreenTurnedOn();
-        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-
-        // THEN finger is considered down
-        assertTrue(mUdfpsController.isFingerDown());
-
-        // WHEN udfps receives an ACQUIRED_GOOD after the display is configured
-        when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-        verify(mFingerprintManager).setUdfpsOverlayController(
-                mUdfpsOverlayControllerCaptor.capture());
-        mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD);
-        mFgExecutor.runAllReady();
-
-        // THEN is fingerDown should be FALSE
-        assertFalse(mUdfpsController.isFingerDown());
-    }
-
-    @Test
-    public void playHaptic_onAodInterrupt_onAcquiredBad_performHapticFeedback()
-            throws RemoteException {
-        // GIVEN UDFPS overlay is showing
-        mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-
-        // GIVEN there's been an AoD interrupt
-        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
-        mScreenObserver.onScreenTurnedOn();
-        mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-
-        // THEN vibrate is used
-        verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
-    }
-
-    @Test
-    public void onAcquiredCalbacks() {
-        runWithAllParams(
-                this::ultrasonicCallbackOnAcquired);
-    }
-
-    public void ultrasonicCallbackOnAcquired(TestParams testParams) throws RemoteException{
-        if (testParams.sensorProps.sensorType
-                == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC) {
-            reset(mUdfpsView);
-
-            UdfpsController.Callback callbackMock = mock(UdfpsController.Callback.class);
-            mUdfpsController.addCallback(callbackMock);
-
-            // GIVEN UDFPS overlay is showing
-            mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
-                    BiometricRequestConstants.REASON_AUTH_KEYGUARD,
-                    mUdfpsOverlayControllerCallback);
-            mFgExecutor.runAllReady();
-
-            verify(mFingerprintManager).setUdfpsOverlayController(
-                    mUdfpsOverlayControllerCaptor.capture());
-            mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_START);
-            mFgExecutor.runAllReady();
-
-            verify(callbackMock).onFingerDown();
-
-            mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD);
-            mFgExecutor.runAllReady();
-
-            verify(callbackMock).onFingerUp();
-        }
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
deleted file mode 100644
index 7986051..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityTransitionAnimator;
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import org.junit.Before;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
-    // Dependencies
-    protected @Mock UdfpsKeyguardViewLegacy mView;
-    protected @Mock Context mResourceContext;
-    protected @Mock StatusBarStateController mStatusBarStateController;
-    protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
-    protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    protected @Mock DumpManager mDumpManager;
-    protected @Mock DelayableExecutor mExecutor;
-    protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    protected @Mock KeyguardStateController mKeyguardStateController;
-    protected @Mock KeyguardViewMediator mKeyguardViewMediator;
-    protected @Mock ConfigurationController mConfigurationController;
-    protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
-    protected @Mock SystemUIDialogManager mDialogManager;
-    protected @Mock UdfpsController mUdfpsController;
-    protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator;
-    protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-    protected @Mock ShadeInteractor mShadeInteractor;
-    protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
-    protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
-    protected @Mock SelectedUserInteractor mSelectedUserInteractor;
-    protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor;
-    protected @Mock UdfpsOverlayInteractor mUdfpsOverlayInteractor;
-
-    protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
-
-    protected UdfpsKeyguardViewControllerLegacy mController;
-
-    // Capture listeners so that they can be used to send events
-    private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
-    protected StatusBarStateController.StateListener mStatusBarStateListener;
-
-    private @Captor ArgumentCaptor<KeyguardStateController.Callback>
-            mKeyguardStateControllerCallbackCaptor;
-    protected KeyguardStateController.Callback mKeyguardStateControllerCallback;
-
-    private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.KeyguardViewManagerCallback>
-            mKeyguardViewManagerCallbackArgumentCaptor;
-    protected StatusBarKeyguardViewManager.KeyguardViewManagerCallback mKeyguardViewManagerCallback;
-
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
-        when(mView.getContext()).thenReturn(mResourceContext);
-        when(mResourceContext.getString(anyInt())).thenReturn("test string");
-        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
-        when(mView.getUnpausedAlpha()).thenReturn(255);
-        when(mShadeExpansionStateManager.addExpansionListener(any())).thenReturn(
-                new ShadeExpansionChangeEvent(0, false, false));
-        mController = createUdfpsKeyguardViewController();
-    }
-
-    protected void sendStatusBarStateChanged(int statusBarState) {
-        mStatusBarStateListener.onStateChanged(statusBarState);
-    }
-
-    protected void captureStatusBarStateListeners() {
-        verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
-        mStatusBarStateListener = mStateListenerCaptor.getValue();
-    }
-
-    protected void captureKeyguardStateControllerCallback() {
-        verify(mKeyguardStateController).addCallback(
-                mKeyguardStateControllerCallbackCaptor.capture());
-        mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue();
-    }
-
-    public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
-        return createUdfpsKeyguardViewController(false);
-    }
-
-    public void captureKeyGuardViewManagerCallback() {
-        verify(mStatusBarKeyguardViewManager).addCallback(
-                mKeyguardViewManagerCallbackArgumentCaptor.capture());
-        mKeyguardViewManagerCallback = mKeyguardViewManagerCallbackArgumentCaptor.getValue();
-    }
-
-    protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController(
-            boolean useModernBouncer) {
-        UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy(
-                mView,
-                mStatusBarStateController,
-                mStatusBarKeyguardViewManager,
-                mKeyguardUpdateMonitor,
-                mDumpManager,
-                mLockscreenShadeTransitionController,
-                mConfigurationController,
-                mKeyguardStateController,
-                mUnlockedScreenOffAnimationController,
-                mDialogManager,
-                mUdfpsController,
-                mActivityTransitionAnimator,
-                mPrimaryBouncerInteractor,
-                mAlternateBouncerInteractor,
-                mUdfpsKeyguardAccessibilityDelegate,
-                mSelectedUserInteractor,
-                mKeyguardTransitionInteractor,
-                mShadeInteractor,
-                mUdfpsOverlayInteractor);
-        return controller;
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
deleted file mode 100644
index 98d8b05..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.TestableLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class UdfpsKeyguardViewLegacyControllerTest extends
-        UdfpsKeyguardViewLegacyControllerBaseTest {
-    @Override
-    public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
-        return createUdfpsKeyguardViewController(/* useModernBouncer */ false);
-    }
-
-    @Test
-    public void testShouldPauseAuth_bouncerShowing() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
-        when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
-        when(mView.getUnpausedAlpha()).thenReturn(0);
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testRegistersStatusBarStateListenersOnAttached() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-    }
-
-    @Test
-    public void testViewControllerQueriesSBStateOnAttached() {
-        mController.onViewAttached();
-        verify(mStatusBarStateController).getState();
-
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
-        captureStatusBarStateListeners();
-
-        mController.onViewAttached();
-        verify(mView, atLeast(1)).setPauseAuth(true);
-    }
-
-    @Test
-    public void testListenersUnregisteredOnDetached() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        captureKeyguardStateControllerCallback();
-        mController.onViewDetached();
-
-        verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
-        verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback);
-    }
-
-    @Test
-    public void testShouldPauseAuthUnpausedAlpha0() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        when(mView.getUnpausedAlpha()).thenReturn(0);
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldNotPauseAuthOnKeyguard() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        assertFalse(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void onBiometricAuthenticated_pauseAuth() {
-        // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        // WHEN biometric is authenticated
-        captureKeyguardStateControllerCallback();
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardStateControllerCallback.onUnlockedChanged();
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthIsLaunchTransitionFadingAway() {
-        // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
-        // WHEN isLaunchTransitionFadingAway=true
-        captureKeyguardStateControllerCallback();
-        when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true);
-        mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged();
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthOnShadeLocked() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
-
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthOnShade() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        // WHEN not on keyguard yet (shade = home)
-        sendStatusBarStateChanged(StatusBarState.SHADE);
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testShouldPauseAuthAnimatingScreenOffFromShade() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        // WHEN transitioning from home/shade => keyguard + animating screen off
-        mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD);
-        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
-
-        // THEN pause auth
-        assertTrue(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testDoNotPauseAuthAnimatingScreenOffFromLS() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        // WHEN animating screen off transition from LS => AOD
-        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
-
-        // THEN don't pause auth
-        assertFalse(mController.shouldPauseAuth());
-    }
-
-    @Test
-    public void testOverrideShouldPauseAuthOnShadeLocked() {
-        mController.onViewAttached();
-        captureStatusBarStateListeners();
-
-        sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
-        assertTrue(mController.shouldPauseAuth());
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
deleted file mode 100644
index 29a6e56..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
-import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
-import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
-import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-@TestableLooper.RunWithLooper
-@kotlinx.coroutines.ExperimentalCoroutinesApi
-class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
-    UdfpsKeyguardViewLegacyControllerBaseTest() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private val keyguardBouncerRepository = kosmos.fakeKeyguardBouncerRepository
-    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
-
-    @Before
-    override fun setUp() {
-        allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
-        MockitoAnnotations.initMocks(this)
-        super.setUp()
-    }
-
-    override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy {
-        mPrimaryBouncerInteractor = kosmos.primaryBouncerInteractor
-        mAlternateBouncerInteractor = kosmos.alternateBouncerInteractor
-        mKeyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
-        mUdfpsOverlayInteractor = kosmos.udfpsOverlayInteractor
-        return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
-    }
-
-    @Test
-    fun bouncerExpansionChange_fadeIn() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            captureKeyguardStateControllerCallback()
-            Mockito.reset(mView)
-
-            // WHEN status bar expansion is 0
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN alpha is 0
-            verify(mView).unpausedAlpha = 0
-
-            job.cancel()
-        }
-
-    @Test
-    fun bouncerExpansionChange_pauseAuth() =
-        testScope.runTest {
-            // GIVEN view is attached + on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            Mockito.reset(mView)
-
-            // WHEN panelViewExpansion changes to hide
-            whenever(mView.unpausedAlpha).thenReturn(0)
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN pause auth is updated to PAUSE
-            verify(mView, Mockito.atLeastOnce()).setPauseAuth(true)
-
-            job.cancel()
-        }
-
-    @Test
-    fun bouncerExpansionChange_unpauseAuth() =
-        testScope.runTest {
-            // GIVEN view is attached + on the keyguard + panel expansion is 0f
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            Mockito.reset(mView)
-
-            // WHEN panelViewExpansion changes to expanded
-            whenever(mView.unpausedAlpha).thenReturn(255)
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
-            runCurrent()
-
-            // THEN pause auth is updated to NOT pause
-            verify(mView, Mockito.atLeastOnce()).setPauseAuth(false)
-
-            job.cancel()
-        }
-
-    @Test
-    fun shadeLocked_showAlternateBouncer_unpauseAuth() =
-        testScope.runTest {
-            // GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing)
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED)
-
-            // WHEN alternate bouncer is requested
-            val job = mController.listenForAlternateBouncerVisibility(this)
-            keyguardBouncerRepository.setAlternateVisible(true)
-            runCurrent()
-
-            // THEN udfps view will animate in & pause auth is updated to NOT pause
-            verify(mView).animateInUdfpsBouncer(any())
-            assertFalse(mController.shouldPauseAuth())
-
-            job.cancel()
-        }
-
-    /** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */
-    @Test
-    fun shouldPauseAuthBouncerShowing() =
-        testScope.runTest {
-            // GIVEN view attached and we're on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-
-            // WHEN the bouncer expansion is VISIBLE
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN UDFPS shouldPauseAuth == true
-            assertTrue(mController.shouldPauseAuth())
-
-            job.cancel()
-        }
-
-    @Test
-    fun shouldHandleTouchesChange() =
-        testScope.runTest {
-            val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
-
-            // GIVEN view is attached + on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            whenever(mView.setPauseAuth(true)).thenReturn(true)
-            whenever(mView.unpausedAlpha).thenReturn(0)
-
-            // WHEN panelViewExpansion changes to expanded
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            // THEN UDFPS auth is paused and should not handle touches
-            assertThat(mController.shouldPauseAuth()).isTrue()
-            assertThat(shouldHandleTouches!!).isFalse()
-
-            job.cancel()
-        }
-
-    @Test
-    fun shouldHandleTouchesOnDetach() =
-        testScope.runTest {
-            val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
-
-            // GIVEN view is attached + on the keyguard
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-            whenever(mView.setPauseAuth(true)).thenReturn(true)
-            whenever(mView.unpausedAlpha).thenReturn(0)
-
-            // WHEN panelViewExpansion changes to expanded
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
-            runCurrent()
-
-            mController.onViewDetached()
-
-            // THEN UDFPS auth is paused and should not handle touches
-            assertThat(mController.shouldPauseAuth()).isTrue()
-            assertThat(shouldHandleTouches!!).isFalse()
-
-            job.cancel()
-        }
-
-    @Test
-    fun fadeFromDialogSuggestedAlpha() =
-        testScope.runTest {
-            // GIVEN view is attached and status bar expansion is 1f
-            mController.onViewAttached()
-            captureStatusBarStateListeners()
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
-            runCurrent()
-            Mockito.reset(mView)
-
-            // WHEN dialog suggested alpha is .6f
-            whenever(mView.dialogSuggestedAlpha).thenReturn(.6f)
-            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-
-            // THEN alpha is updated based on dialog suggested alpha
-            verify(mView).unpausedAlpha = (.6f * 255).toInt()
-
-            job.cancel()
-        }
-
-    @Test
-    fun transitionToFullShadeProgress() =
-        testScope.runTest {
-            // GIVEN view is attached and status bar expansion is 1f
-            mController.onViewAttached()
-            val job = mController.listenForBouncerExpansion(this)
-            keyguardBouncerRepository.setPrimaryShow(true)
-            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
-            runCurrent()
-            Mockito.reset(mView)
-            whenever(mView.dialogSuggestedAlpha).thenReturn(1f)
-
-            // WHEN we're transitioning to the full shade
-            val transitionProgress = .6f
-            mController.setTransitionToFullShadeProgress(transitionProgress)
-
-            // THEN alpha is between 0 and 255
-            verify(mView).unpausedAlpha = ((1f - transitionProgress) * 255).toInt()
-
-            job.cancel()
-        }
-
-    @Test
-    fun aodToLockscreen_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForLockscreenAodTransitions(this)
-
-            // WHEN transitioning from lockscreen to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun lockscreenToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForLockscreenAodTransitions(this)
-
-            // WHEN transitioning from lockscreen to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun goneToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForGoneToAodTransition(this)
-
-            // WHEN transitioning from lockscreen to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun aodToOccluded_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForAodToOccludedTransitions(this)
-
-            // WHEN transitioning from aod to occluded
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.OCCLUDED,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(eq(.7f), eq(.7f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.OCCLUDED,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
-
-            job.cancel()
-        }
-
-    @Test
-    fun occludedToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForOccludedToAodTransition(this)
-
-            // WHEN transitioning from occluded to aod
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(.3f),
-                    eq(.3f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.OCCLUDED,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.FINISHED
-                )
-            )
-            runCurrent()
-            // THEN doze amount is updated
-            verify(mView)
-                .onDozeAmountChanged(
-                    eq(1f),
-                    eq(1f),
-                    eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
-                )
-
-            job.cancel()
-        }
-
-    @Test
-    fun cancelledAodToLockscreen_dozeAmountChangedToZero() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            val job = mController.listenForLockscreenAodTransitions(this)
-            runCurrent()
-            Mockito.reset(mView)
-
-            // WHEN aod to lockscreen transition is cancelled
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.LOCKSCREEN,
-                    value = 1f,
-                    transitionState = TransitionState.CANCELED
-                )
-            )
-            runCurrent()
-            // ... and WHEN the next transition is from lockscreen => occluded
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.OCCLUDED,
-                    value = .4f,
-                    transitionState = TransitionState.STARTED
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to zero
-            verify(mView)
-                .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
-            job.cancel()
-        }
-
-    @Test
-    fun cancelledLockscreenToAod_dozeAmountNotUpdatedToZero() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            val job = mController.listenForLockscreenAodTransitions(this)
-            runCurrent()
-            Mockito.reset(mView)
-
-            // WHEN lockscreen to aod transition is cancelled
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.AOD,
-                    value = 1f,
-                    transitionState = TransitionState.CANCELED
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is NOT updated to zero
-            verify(mView, never()).onDozeAmountChanged(eq(0f), eq(0f), anyInt())
-            job.cancel()
-        }
-
-    @Test
-    fun dreamingToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForDreamingToAodTransitions(this)
-            // WHEN dreaming to aod transition in progress
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to
-            verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF))
-            job.cancel()
-        }
-
-    @Test
-    fun alternateBouncerToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForAlternateBouncerToAodTransitions(this)
-            // WHEN alternate bouncer to aod transition in progress
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to
-            verify(mView)
-                .onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
-            job.cancel()
-        }
-
-    @Test
-    fun bouncerToAod_dozeAmountChanged() =
-        testScope.runTest {
-            // GIVEN view is attached
-            mController.onViewAttached()
-            Mockito.reset(mView)
-
-            val job = mController.listenForPrimaryBouncerToAodTransitions(this)
-            // WHEN alternate bouncer to aod transition in progress
-            transitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
-                    to = KeyguardState.AOD,
-                    value = .3f,
-                    transitionState = TransitionState.RUNNING
-                )
-            )
-            runCurrent()
-
-            // THEN doze amount is updated to
-            verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF))
-            job.cancel()
-        }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
deleted file mode 100644
index 9fbe096..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.hardware.biometrics.SensorLocationInternal
-import android.testing.TestableLooper
-import android.testing.ViewUtils
-import android.view.LayoutInflater
-import android.view.Surface
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.nullable
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-
-private const val SENSOR_X = 50
-private const val SENSOR_Y = 250
-private const val SENSOR_RADIUS = 10
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-class UdfpsViewTest : SysuiTestCase() {
-
-    @JvmField @Rule
-    var rule = MockitoJUnit.rule()
-
-    @Mock
-    lateinit var hbmProvider: UdfpsDisplayModeProvider
-    @Mock
-    lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
-
-    private lateinit var view: UdfpsView
-
-    @Before
-    fun setup() {
-        context.setTheme(androidx.appcompat.R.style.Theme_AppCompat)
-        view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
-        view.animationViewController = animationViewController
-        val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
-        view.overlayParams = UdfpsOverlayParams(sensorBounds, sensorBounds, 1920,
-            1080, 1f, Surface.ROTATION_0)
-        view.setUdfpsDisplayModeProvider(hbmProvider)
-        ViewUtils.attachView(view)
-    }
-
-    @After
-    fun cleanup() {
-        ViewUtils.detachView(view)
-    }
-
-    // TODO: Add test to verify view is size of screen
-
-    @Test
-    fun startAndStopIllumination() {
-        val onDone: Runnable = mock()
-        view.configureDisplay(onDone)
-
-        val illuminator = withArgCaptor<Runnable> {
-            verify(hbmProvider).enable(capture())
-        }
-
-        assertThat(view.isDisplayConfigured).isTrue()
-        verify(animationViewController).onDisplayConfiguring()
-        verify(animationViewController, never()).onDisplayUnconfigured()
-        verify(onDone, never()).run()
-
-        // fake illumination event
-        illuminator.run()
-        waitForLooper()
-        verify(onDone).run()
-        verify(hbmProvider, never()).disable(any())
-
-        view.unconfigureDisplay()
-        assertThat(view.isDisplayConfigured).isFalse()
-        verify(animationViewController).onDisplayUnconfigured()
-        verify(hbmProvider).disable(nullable(Runnable::class.java))
-    }
-
-    private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 68cfa28..82ff617 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -16,12 +16,9 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.keyguardUpdateMonitor
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
@@ -61,12 +58,6 @@
         underTest = kosmos.alternateBouncerInteractor
     }
 
-    @Test(expected = IllegalStateException::class)
-    @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() {
-        underTest.show()
-    }
-
     @Test
     @DisableSceneContainer
     fun canShowAlternateBouncer_false_dueToTransitionState() =
@@ -101,21 +92,6 @@
         }
 
     @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun canShowAlternateBouncerForFingerprint_givenCanShow() {
-        givenCanShowAlternateBouncer()
-        assertTrue(underTest.canShowAlternateBouncerForFingerprint())
-    }
-
-    @Test
-    fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() {
-        givenCanShowAlternateBouncer()
-        kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false)
-
-        assertFalse(underTest.canShowAlternateBouncerForFingerprint())
-    }
-
-    @Test
     fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() {
         givenCanShowAlternateBouncer()
         kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
@@ -140,15 +116,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun show_whenCanShow() {
-        givenCanShowAlternateBouncer()
-
-        assertTrue(underTest.show())
-        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
-    }
-
-    @Test
     fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() {
         givenCanShowAlternateBouncer()
         whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true)
@@ -165,15 +132,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun show_whenCannotShow() {
-        givenCannotShowAlternateBouncer()
-
-        assertFalse(underTest.show())
-        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
-    }
-
-    @Test
     fun hide_wasPreviouslyShowing() {
         kosmos.keyguardBouncerRepository.setAlternateVisible(true)
 
@@ -190,7 +148,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun canShowAlternateBouncerForFingerprint_rearFps() {
         givenCanShowAlternateBouncer()
         kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer
@@ -198,29 +155,6 @@
         assertFalse(underTest.canShowAlternateBouncerForFingerprint())
     }
 
-    @Test
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    fun alternateBouncerUiAvailable_fromMultipleSources() {
-        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
-        // GIVEN there are two different sources indicating the alternate bouncer is available
-        underTest.setAlternateBouncerUIAvailable(true, "source1")
-        underTest.setAlternateBouncerUIAvailable(true, "source2")
-        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
-        // WHEN one of the sources no longer says the UI is available
-        underTest.setAlternateBouncerUIAvailable(false, "source1")
-
-        // THEN alternate bouncer UI is still available (from the other source)
-        assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
-        // WHEN all sources say the UI is not available
-        underTest.setAlternateBouncerUIAvailable(false, "source2")
-
-        // THEN alternate boucer UI is not available
-        assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-    }
-
     private fun givenAlternateBouncerSupported() {
         kosmos.givenAlternateBouncerSupported()
     }
@@ -228,8 +162,4 @@
     private fun givenCanShowAlternateBouncer() {
         kosmos.givenCanShowAlternateBouncer()
     }
-
-    private fun givenCannotShowAlternateBouncer() {
-        kosmos.givenCannotShowAlternateBouncer()
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
index a676c7d..0983105 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
@@ -31,12 +31,11 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.core.FakeLogBuffer
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -78,7 +77,7 @@
                 displayId,
                 displayManager,
                 FakeLogBuffer.Factory.create(),
-                mock<TableLogBuffer>(),
+                logcatTableLogBuffer(kosmos, "screenBrightness"),
                 kosmos.applicationCoroutineScope,
                 kosmos.testDispatcher,
             )
@@ -163,7 +162,7 @@
 
                 changeBrightnessInfoAndNotify(
                     BrightnessInfo(0.5f, 0.1f, 0.7f),
-                    listenerCaptor.value
+                    listenerCaptor.value,
                 )
                 runCurrent()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt
index b6616bf..18e7a7e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt
@@ -27,9 +27,8 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -49,7 +48,7 @@
             ScreenBrightnessInteractor(
                 screenBrightnessRepository,
                 applicationCoroutineScope,
-                mock<TableLogBuffer>()
+                logcatTableLogBuffer(this, "screenBrightness"),
             )
         }
 
@@ -112,7 +111,7 @@
                     BrightnessUtils.convertGammaToLinearFloat(
                         gammaBrightness,
                         min.floatValue,
-                        max.floatValue
+                        max.floatValue,
                     )
                 assertThat(temporaryBrightness!!.floatValue)
                     .isWithin(1e-5f)
@@ -136,7 +135,7 @@
                     BrightnessUtils.convertGammaToLinearFloat(
                         gammaBrightness,
                         min.floatValue,
-                        max.floatValue
+                        max.floatValue,
                     )
                 assertThat(brightness!!.floatValue).isWithin(1e-5f).of(expectedBrightness)
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
index 8402676..18f33e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
@@ -54,6 +55,7 @@
                 screenBrightnessInteractor,
                 brightnessPolicyEnforcementInteractor,
                 applicationCoroutineScope,
+                sliderHapticsViewModelFactory,
             )
         }
 
@@ -61,7 +63,7 @@
     fun setUp() {
         kosmos.fakeScreenBrightnessRepository.setMinMaxBrightness(
             LinearBrightness(minBrightness),
-            LinearBrightness(maxBrightness)
+            LinearBrightness(maxBrightness),
         )
     }
 
@@ -79,7 +81,7 @@
                         BrightnessUtils.convertLinearToGammaFloat(
                             brightness,
                             minBrightness,
-                            maxBrightness
+                            maxBrightness,
                         )
                     )
 
@@ -91,7 +93,7 @@
                         BrightnessUtils.convertLinearToGammaFloat(
                             brightness,
                             minBrightness,
-                            maxBrightness
+                            maxBrightness,
                         )
                     )
             }
@@ -122,7 +124,7 @@
                     BrightnessUtils.convertGammaToLinearFloat(
                         newBrightness,
                         minBrightness,
-                        maxBrightness
+                        maxBrightness,
                     )
                 val drag = Drag.Dragging(GammaBrightness(newBrightness))
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImplTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index ee65fbd..1e86516 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal
 
+import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
@@ -77,7 +78,11 @@
     @Before
     fun setUp() {
         with(kosmos) {
-            fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT)
+            fakeSettings.putIntForUser(
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                SCREEN_TIMEOUT,
+                UserHandle.USER_CURRENT,
+            )
             kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
 
             underTest =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index dd5ad17..2b0928f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.testKosmos
@@ -44,7 +44,6 @@
 class CommunalMediaRepositoryImplTest : SysuiTestCase() {
     private val mediaDataManager = mock<MediaDataManager>()
     private val mediaData = mock<MediaData>()
-    private val tableLogBuffer = mock<TableLogBuffer>()
 
     private lateinit var underTest: CommunalMediaRepositoryImpl
 
@@ -52,14 +51,11 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "CommunalMediaRepositoryImplTest")
 
     @Before
     fun setUp() {
-        underTest =
-            CommunalMediaRepositoryImpl(
-                mediaDataManager,
-                tableLogBuffer,
-            )
+        underTest = CommunalMediaRepositoryImpl(mediaDataManager, tableLogBuffer)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt
deleted file mode 100644
index 643063e7..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.ui.compose
-
-import android.testing.TestableLooper
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
-
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@SmallTest
-class CommunalHubUtilsTest : SysuiTestCase() {
-    @Test
-    fun isPointerWithinEnabledRemoveButton_ensureDisabledStatePriority() {
-        assertThat(
-                isPointerWithinEnabledRemoveButton(false, mock<Offset>(), mock<LayoutCoordinates>())
-            )
-            .isFalse()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
index f0d88ab..22b114c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModelTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -53,10 +52,12 @@
             verticalItemSpacingPx = 10f,
             verticalContentPaddingPx = verticalContentPaddingPx,
             viewportHeightPx = viewportHeightPx,
-            maxItemSpan = 1,
-            minItemSpan = 1,
-            currentSpan = 1,
             currentRow = 0,
+            currentSpan = 1,
+            maxHeightPx = Int.MAX_VALUE,
+            minHeightPx = 0,
+            resizeMultiple = 1,
+            totalSpans = 1,
         )
 
     @Before
@@ -79,7 +80,7 @@
 
     @Test
     fun testSingleSpanGrid() =
-        testScope.runTest(timeout = Duration.INFINITE) {
+        testScope.runTest {
             updateGridLayout(singleSpanGrid)
 
             val topState = underTest.topDragState
@@ -98,8 +99,7 @@
     @Test
     fun testTwoSpanGrid_elementInFirstRow_sizeSingleSpan() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2))
-
+            updateGridLayout(singleSpanGrid.copy(currentRow = 0, totalSpans = 2))
             val topState = underTest.topDragState
             assertThat(topState.currentValue).isEqualTo(0)
             assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
@@ -116,8 +116,7 @@
     @Test
     fun testTwoSpanGrid_elementInSecondRow_sizeSingleSpan() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2, currentRow = 1))
-
+            updateGridLayout(singleSpanGrid.copy(currentRow = 1, totalSpans = 2))
             val topState = underTest.topDragState
             assertThat(topState.currentValue).isEqualTo(0)
             assertThat(topState.anchors.toList()).containsExactly(0 to 0f, -1 to -45f)
@@ -134,15 +133,17 @@
     @Test
     fun testTwoSpanGrid_elementInFirstRow_sizeTwoSpan() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2, currentSpan = 2))
+            val adjustedGridLayout = singleSpanGrid.copy(currentSpan = 2, totalSpans = 2)
+
+            updateGridLayout(adjustedGridLayout)
 
             val topState = underTest.topDragState
-            assertThat(topState.currentValue).isEqualTo(0)
             assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+            assertThat(topState.currentValue).isEqualTo(0)
 
             val bottomState = underTest.bottomDragState
+            assertThat(bottomState.anchors.toList()).containsExactly(-1 to -45f, 0 to 0f)
             assertThat(bottomState.currentValue).isEqualTo(0)
-            assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f, -1 to -45f)
         }
 
     /**
@@ -151,7 +152,10 @@
     @Test
     fun testThreeSpanGrid_elementInMiddleRow_sizeOneSpan() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 3, currentRow = 1))
+            val adjustedGridLayout =
+                singleSpanGrid.copy(currentRow = 1, currentSpan = 1, totalSpans = 3)
+
+            updateGridLayout(adjustedGridLayout)
 
             val topState = underTest.topDragState
             assertThat(topState.currentValue).isEqualTo(0)
@@ -165,7 +169,10 @@
     @Test
     fun testThreeSpanGrid_elementInTopRow_sizeOneSpan() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 3))
+            val adjustedGridLayout =
+                singleSpanGrid.copy(currentRow = 0, currentSpan = 1, totalSpans = 3)
+
+            updateGridLayout(adjustedGridLayout)
 
             val topState = underTest.topDragState
             assertThat(topState.currentValue).isEqualTo(0)
@@ -177,16 +184,17 @@
         }
 
     @Test
-    fun testSixSpanGrid_minSpanThree_itemInThirdRow_sizeThreeSpans() =
+    fun testSixSpanGrid_minSpanThree_itemInFourthRow_sizeThreeSpans() =
         testScope.runTest {
-            updateGridLayout(
+            val adjustedGridLayout =
                 singleSpanGrid.copy(
-                    maxItemSpan = 6,
                     currentRow = 3,
                     currentSpan = 3,
-                    minItemSpan = 3,
+                    resizeMultiple = 3,
+                    totalSpans = 6,
                 )
-            )
+
+            updateGridLayout(adjustedGridLayout)
 
             val topState = underTest.topDragState
             assertThat(topState.currentValue).isEqualTo(0)
@@ -200,7 +208,14 @@
     @Test
     fun testTwoSpanGrid_elementMovesFromFirstRowToSecondRow() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2))
+            val firstRowLayout =
+                singleSpanGrid.copy(
+                    currentRow = 0,
+                    currentSpan = 1,
+                    resizeMultiple = 1,
+                    totalSpans = 2,
+                )
+            updateGridLayout(firstRowLayout)
 
             val topState = underTest.topDragState
             val bottomState = underTest.bottomDragState
@@ -208,7 +223,8 @@
             assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
             assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f, 1 to 45f)
 
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2, currentRow = 1))
+            val secondRowLayout = firstRowLayout.copy(currentRow = 1)
+            updateGridLayout(secondRowLayout)
 
             assertThat(topState.anchors.toList()).containsExactly(0 to 0f, -1 to -45f)
             assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f)
@@ -217,19 +233,21 @@
     @Test
     fun testTwoSpanGrid_expandElementFromBottom() = runTestWithSnapshots {
         val resizeInfo by collectLastValue(underTest.resizeInfo)
-        updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2))
 
-        assertThat(resizeInfo).isNull()
+        val adjustedGridLayout = singleSpanGrid.copy(resizeMultiple = 1, totalSpans = 2)
+
+        updateGridLayout(adjustedGridLayout)
+
         underTest.bottomDragState.anchoredDrag { dragTo(45f) }
+
         assertThat(resizeInfo).isEqualTo(ResizeInfo(1, DragHandle.BOTTOM))
     }
 
     @Test
     fun testThreeSpanGrid_expandMiddleElementUpwards() = runTestWithSnapshots {
         val resizeInfo by collectLastValue(underTest.resizeInfo)
-        updateGridLayout(singleSpanGrid.copy(maxItemSpan = 3, currentRow = 1))
+        updateGridLayout(singleSpanGrid.copy(currentRow = 1, totalSpans = 3))
 
-        assertThat(resizeInfo).isNull()
         underTest.topDragState.anchoredDrag { dragTo(-30f) }
         assertThat(resizeInfo).isEqualTo(ResizeInfo(1, DragHandle.TOP))
     }
@@ -237,7 +255,7 @@
     @Test
     fun testThreeSpanGrid_expandTopElementDownBy2Spans() = runTestWithSnapshots {
         val resizeInfo by collectLastValue(underTest.resizeInfo)
-        updateGridLayout(singleSpanGrid.copy(maxItemSpan = 3))
+        updateGridLayout(singleSpanGrid.copy(totalSpans = 3))
 
         assertThat(resizeInfo).isNull()
         underTest.bottomDragState.anchoredDrag { dragTo(60f) }
@@ -247,7 +265,7 @@
     @Test
     fun testTwoSpanGrid_shrinkElementFromBottom() = runTestWithSnapshots {
         val resizeInfo by collectLastValue(underTest.resizeInfo)
-        updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2, currentSpan = 2))
+        updateGridLayout(singleSpanGrid.copy(totalSpans = 2, currentSpan = 2))
 
         assertThat(resizeInfo).isNull()
         underTest.bottomDragState.anchoredDrag { dragTo(-45f) }
@@ -257,7 +275,7 @@
     @Test
     fun testRowInfoBecomesNull_revertsBackToDefault() =
         testScope.runTest {
-            val gridLayout = singleSpanGrid.copy(maxItemSpan = 3, currentRow = 1)
+            val gridLayout = singleSpanGrid.copy(currentRow = 1, resizeMultiple = 1, totalSpans = 3)
             updateGridLayout(gridLayout)
 
             val topState = underTest.topDragState
@@ -266,44 +284,113 @@
             val bottomState = underTest.bottomDragState
             assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f, 1 to 30f)
 
+            // Set currentRow to null to simulate the row info becoming null
             updateGridLayout(gridLayout.copy(currentRow = null))
             assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
             assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f)
         }
 
-    @Test(expected = IllegalArgumentException::class)
-    fun testIllegalState_maxSpanSmallerThanMinSpan() =
+    @Test
+    fun testEqualMaxAndMinHeight_cannotResize() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2, minItemSpan = 3))
+            val heightPx = 20
+            updateGridLayout(
+                singleSpanGrid.copy(maxHeightPx = heightPx, minHeightPx = heightPx, totalSpans = 2)
+            )
+
+            val topState = underTest.topDragState
+            val bottomState = underTest.bottomDragState
+
+            assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+            assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f)
+        }
+
+    @Test
+    fun testMinHeightTwoRows_canExpandButNotShrink() =
+        testScope.runTest {
+            val threeRowGrid =
+                singleSpanGrid.copy(
+                    maxHeightPx = 80,
+                    minHeightPx = 50,
+                    totalSpans = 3,
+                    currentSpan = 2,
+                    currentRow = 0,
+                )
+
+            updateGridLayout(threeRowGrid)
+
+            val topState = underTest.topDragState
+            val bottomState = underTest.bottomDragState
+            assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+            assertThat(bottomState.anchors.toList()).containsAtLeast(0 to 0f, 1 to 30f)
+        }
+
+    @Test
+    fun testMaxHeightTwoRows_canShrinkButNotExpand() =
+        testScope.runTest {
+            val threeRowGrid =
+                singleSpanGrid.copy(
+                    maxHeightPx = 50,
+                    minHeightPx = 20,
+                    totalSpans = 3,
+                    currentSpan = 2,
+                    currentRow = 0,
+                )
+
+            updateGridLayout(threeRowGrid)
+
+            val topState = underTest.topDragState
+            val bottomState = underTest.bottomDragState
+
+            assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f, -1 to -30f)
+
+            assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+        }
+
+    @Test
+    fun testMinHeightEqualToAvailableSpan_cannotResize() =
+        testScope.runTest {
+            val twoRowGrid =
+                singleSpanGrid.copy(
+                    minHeightPx = (viewportHeightPx - verticalContentPaddingPx.toInt()),
+                    totalSpans = 2,
+                    currentSpan = 2,
+                )
+
+            updateGridLayout(twoRowGrid)
+
+            val topState = underTest.topDragState
+            val bottomState = underTest.bottomDragState
+
+            assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+            assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f)
         }
 
     @Test(expected = IllegalArgumentException::class)
-    fun testIllegalState_minSpanOfZero() =
+    fun testIllegalState_maxHeightLessThanMinHeight() =
         testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 2, minItemSpan = 0))
+            updateGridLayout(singleSpanGrid.copy(maxHeightPx = 50, minHeightPx = 100))
         }
 
     @Test(expected = IllegalArgumentException::class)
-    fun testIllegalState_maxSpanOfZero() =
-        testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 0, minItemSpan = 0))
-        }
+    fun testIllegalState_currentSpanExceedsTotalSpans() =
+        testScope.runTest { updateGridLayout(singleSpanGrid.copy(currentSpan = 3, totalSpans = 2)) }
 
     @Test(expected = IllegalArgumentException::class)
-    fun testIllegalState_currentRowNotMultipleOfMinSpan() =
-        testScope.runTest {
-            updateGridLayout(singleSpanGrid.copy(maxItemSpan = 6, minItemSpan = 3, currentSpan = 2))
-        }
+    fun testIllegalState_resizeMultipleZeroOrNegative() =
+        testScope.runTest { updateGridLayout(singleSpanGrid.copy(resizeMultiple = 0)) }
 
     private fun TestScope.updateGridLayout(gridLayout: GridLayout) {
         underTest.setGridLayoutInfo(
-            gridLayout.verticalItemSpacingPx,
-            gridLayout.verticalContentPaddingPx,
-            gridLayout.viewportHeightPx,
-            gridLayout.maxItemSpan,
-            gridLayout.minItemSpan,
-            gridLayout.currentRow,
-            gridLayout.currentSpan,
+            verticalItemSpacingPx = gridLayout.verticalItemSpacingPx,
+            currentRow = gridLayout.currentRow,
+            maxHeightPx = gridLayout.maxHeightPx,
+            minHeightPx = gridLayout.minHeightPx,
+            currentSpan = gridLayout.currentSpan,
+            resizeMultiple = gridLayout.resizeMultiple,
+            totalSpans = gridLayout.totalSpans,
+            viewportHeightPx = gridLayout.viewportHeightPx,
+            verticalContentPaddingPx = gridLayout.verticalContentPaddingPx,
         )
         runCurrent()
     }
@@ -332,9 +419,11 @@
         val verticalItemSpacingPx: Float,
         val verticalContentPaddingPx: Float,
         val viewportHeightPx: Int,
-        val maxItemSpan: Int,
-        val minItemSpan: Int,
         val currentRow: Int?,
-        val currentSpan: Int?,
+        val currentSpan: Int,
+        val maxHeightPx: Int,
+        val minHeightPx: Int,
+        val resizeMultiple: Int,
+        val totalSpans: Int,
     )
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 179ba22..cecc11e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -19,7 +19,6 @@
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ActivityNotFoundException
 import android.content.ComponentName
-import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.UserInfo
 import android.provider.Settings
@@ -27,7 +26,6 @@
 import android.view.accessibility.AccessibilityManager
 import android.view.accessibility.accessibilityManager
 import android.widget.RemoteViews
-import androidx.activity.result.ActivityResultLauncher
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
@@ -88,7 +86,6 @@
     @Mock private lateinit var mediaHost: MediaHost
     @Mock private lateinit var uiEventLogger: UiEventLogger
     @Mock private lateinit var packageManager: PackageManager
-    @Mock private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
     @Mock private lateinit var metricsLogger: CommunalMetricsLogger
 
     private val kosmos = testKosmos()
@@ -117,10 +114,7 @@
         communalSceneInteractor = kosmos.communalSceneInteractor
         communalInteractor = spy(kosmos.communalInteractor)
         kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
-        kosmos.fakeUserTracker.set(
-            userInfos = listOf(MAIN_USER_INFO),
-            selectedUserIndex = 0,
-        )
+        kosmos.fakeUserTracker.set(userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0)
         kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
         accessibilityManager = kosmos.accessibilityManager
 
@@ -257,10 +251,13 @@
     @Test
     fun onOpenWidgetPicker_launchesWidgetPickerActivity() {
         testScope.runTest {
+            var activityStarted = false
             val success =
-                underTest.onOpenWidgetPicker(testableResources.resources, activityResultLauncher)
+                underTest.onOpenWidgetPicker(testableResources.resources) { _ ->
+                    run { activityStarted = true }
+                }
 
-            verify(activityResultLauncher).launch(any())
+            assertTrue(activityStarted)
             assertTrue(success)
         }
     }
@@ -268,14 +265,10 @@
     @Test
     fun onOpenWidgetPicker_activityLaunchThrowsException_failure() {
         testScope.runTest {
-            whenever(activityResultLauncher.launch(any()))
-                .thenThrow(ActivityNotFoundException::class.java)
-
             val success =
-                underTest.onOpenWidgetPicker(
-                    testableResources.resources,
-                    activityResultLauncher,
-                )
+                underTest.onOpenWidgetPicker(testableResources.resources) { _ ->
+                    run { throw ActivityNotFoundException() }
+                }
 
             assertFalse(success)
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
index 3f5b9a3..bec8a30 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
@@ -16,6 +16,14 @@
 
 package com.android.systemui.display.data.repository
 
+import android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN
 import android.hardware.devicestate.DeviceStateManager
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -40,6 +48,8 @@
 import org.junit.runner.RunWith
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.whenever
+import android.hardware.devicestate.DeviceState as PlatformDeviceState
 
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
@@ -59,15 +69,33 @@
     @Before
     fun setup() {
         mContext.orCreateTestableResources.apply {
-            addOverride(R.array.config_foldedDeviceStates, listOf(TEST_FOLDED).toIntArray())
-            addOverride(R.array.config_halfFoldedDeviceStates, TEST_HALF_FOLDED.toIntArray())
-            addOverride(R.array.config_openDeviceStates, TEST_UNFOLDED.toIntArray())
-            addOverride(R.array.config_rearDisplayDeviceStates, TEST_REAR_DISPLAY.toIntArray())
+            addOverride(
+                R.array.config_foldedDeviceStates,
+                listOf(TEST_FOLDED.identifier).toIntArray()
+            )
+            addOverride(
+                R.array.config_halfFoldedDeviceStates,
+                TEST_HALF_FOLDED.identifier.toIntArray()
+            )
+            addOverride(R.array.config_openDeviceStates, TEST_UNFOLDED.identifier.toIntArray())
+            addOverride(
+                R.array.config_rearDisplayDeviceStates,
+                TEST_REAR_DISPLAY.identifier.toIntArray()
+            )
             addOverride(
                 R.array.config_concurrentDisplayDeviceStates,
-                TEST_CONCURRENT_DISPLAY.toIntArray()
+                TEST_CONCURRENT_DISPLAY.identifier.toIntArray()
             )
         }
+        whenever(deviceStateManager.supportedDeviceStates).thenReturn(
+            listOf(
+                TEST_FOLDED,
+                TEST_HALF_FOLDED,
+                TEST_UNFOLDED,
+                TEST_REAR_DISPLAY,
+                TEST_CONCURRENT_DISPLAY
+            )
+        )
         deviceStateRepository =
             DeviceStateRepositoryImpl(
                 mContext,
@@ -85,9 +113,7 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onDeviceStateChanged(
-                getDeviceStateForIdentifier(TEST_FOLDED)
-            )
+            deviceStateManagerListener.value.onDeviceStateChanged(TEST_FOLDED)
 
             assertThat(state()).isEqualTo(DeviceState.FOLDED)
         }
@@ -97,9 +123,7 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onDeviceStateChanged(
-                getDeviceStateForIdentifier(TEST_HALF_FOLDED)
-            )
+            deviceStateManagerListener.value.onDeviceStateChanged(TEST_HALF_FOLDED)
 
             assertThat(state()).isEqualTo(DeviceState.HALF_FOLDED)
         }
@@ -109,9 +133,7 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onDeviceStateChanged(
-                getDeviceStateForIdentifier(TEST_UNFOLDED)
-            )
+            deviceStateManagerListener.value.onDeviceStateChanged(TEST_UNFOLDED)
 
             assertThat(state()).isEqualTo(DeviceState.UNFOLDED)
         }
@@ -121,9 +143,7 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onDeviceStateChanged(
-                getDeviceStateForIdentifier(TEST_REAR_DISPLAY)
-            )
+            deviceStateManagerListener.value.onDeviceStateChanged(TEST_REAR_DISPLAY)
 
             assertThat(state()).isEqualTo(DeviceState.REAR_DISPLAY)
         }
@@ -133,9 +153,7 @@
         testScope.runTest {
             val state = displayState()
 
-            deviceStateManagerListener.value.onDeviceStateChanged(
-                getDeviceStateForIdentifier(TEST_CONCURRENT_DISPLAY)
-            )
+            deviceStateManagerListener.value.onDeviceStateChanged(TEST_CONCURRENT_DISPLAY)
 
             assertThat(state()).isEqualTo(DeviceState.CONCURRENT_DISPLAY)
         }
@@ -164,9 +182,9 @@
 
     private fun Int.toIntArray() = listOf(this).toIntArray()
 
-    private fun getDeviceStateForIdentifier(id: Int): android.hardware.devicestate.DeviceState {
-        return android.hardware.devicestate.DeviceState(
-            android.hardware.devicestate.DeviceState.Configuration.Builder(id, /* name= */ "")
+    private fun getDeviceStateForIdentifier(id: Int): PlatformDeviceState {
+        return PlatformDeviceState(
+            PlatformDeviceState.Configuration.Builder(id, /* name= */ "")
                 .build()
         )
     }
@@ -174,10 +192,68 @@
     private companion object {
         // Used to fake the ids in the test. Note that there is no guarantees different devices will
         // have the same ids (that's why the ones in this test start from 41)
-        const val TEST_FOLDED = 41
-        const val TEST_HALF_FOLDED = 42
-        const val TEST_UNFOLDED = 43
-        const val TEST_REAR_DISPLAY = 44
-        const val TEST_CONCURRENT_DISPLAY = 45
+        val TEST_FOLDED =
+            PlatformDeviceState(
+                PlatformDeviceState.Configuration.Builder(41, "FOLDED")
+                    .setSystemProperties(
+                        setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+                    )
+                    .build()
+            )
+        val TEST_HALF_FOLDED =
+            PlatformDeviceState(
+                PlatformDeviceState.Configuration.Builder(42, "HALF_FOLDED")
+                    .setSystemProperties(
+                        setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)
+                    )
+                    .build()
+            )
+        val TEST_UNFOLDED =
+            PlatformDeviceState(
+                PlatformDeviceState.Configuration.Builder(43, "UNFOLDED")
+                    .setSystemProperties(
+                        setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)
+                    )
+                    .build()
+            )
+        val TEST_REAR_DISPLAY =
+            PlatformDeviceState(
+                PlatformDeviceState.Configuration.Builder(44, "REAR_DISPLAY")
+                    .setSystemProperties(
+                        setOf(
+                            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                            PROPERTY_FEATURE_REAR_DISPLAY,
+                            PROPERTY_EMULATED_ONLY
+                        )
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)
+                    )
+                    .build()
+            )
+        val TEST_CONCURRENT_DISPLAY =
+            PlatformDeviceState(
+                PlatformDeviceState.Configuration.Builder(45, "CONCURRENT_DISPLAY")
+                    .setSystemProperties(
+                        setOf(
+                            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+                            PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT,
+                            PROPERTY_EMULATED_ONLY
+                        )
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)
+                    )
+                    .build()
+            )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 6c6de61..cd8b2e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -440,6 +440,28 @@
         }
 
     @Test
+    fun displayAdditionEvent_emptyByDefault() =
+        testScope.runTest {
+            setDisplays(1, 2, 3)
+
+            val lastAddedDisplay by lastDisplayAdditionEvent()
+
+            assertThat(lastAddedDisplay).isNull()
+        }
+
+    @Test
+    fun displayAdditionEvent_displaysAdded_doesNotReplayEventsToNewSubscribers() =
+        testScope.runTest {
+            val priorDisplayAdded by lastDisplayAdditionEvent()
+            setDisplays(1)
+            sendOnDisplayAdded(1)
+            assertThat(priorDisplayAdded?.displayId).isEqualTo(1)
+
+            val lastAddedDisplay by collectLastValue(displayRepository.displayAdditionEvent)
+            assertThat(lastAddedDisplay).isNull()
+        }
+
+    @Test
     fun defaultDisplayOff_changes() =
         testScope.runTest {
             val defaultDisplayOff by latestDefaultDisplayOffFlowValue()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
new file mode 100644
index 0000000..1dd8ca9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PerDisplayStoreImplTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val testScope = kosmos.testScope
+    private val fakeDisplayRepository = kosmos.displayRepository
+
+    private val store = kosmos.fakePerDisplayStore
+
+    @Before
+    fun start() {
+        store.start()
+    }
+
+    @Before
+    fun addDisplays() = runBlocking {
+        fakeDisplayRepository.addDisplay(createDisplay(DEFAULT_DISPLAY_ID))
+        fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID))
+    }
+
+    @Test
+    fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() =
+        testScope.runTest {
+            val instance = store.defaultDisplay
+
+            assertThat(store.defaultDisplay).isSameInstanceAs(instance)
+        }
+
+    @Test
+    fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() =
+        testScope.runTest {
+            val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+            assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(instance)
+        }
+
+    @Test
+    fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() =
+        testScope.runTest {
+            val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+            fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
+            fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID))
+
+            assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(instance)
+        }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun forDisplay_nonExistingDisplayId_throws() =
+        testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) }
+
+    @Test
+    fun forDisplay_afterDisplayRemoved_onDisplayRemovalActionInvoked() =
+        testScope.runTest {
+            val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+            fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
+
+            assertThat(store.removalActions).containsExactly(instance)
+        }
+
+    @Test
+    fun forDisplay_withoutDisplayRemoval_onDisplayRemovalActionIsNotInvoked() =
+        testScope.runTest {
+            store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+            assertThat(store.removalActions).isEmpty()
+        }
+
+    private fun createDisplay(displayId: Int): Display = mock {
+        on { getDisplayId() } doReturn displayId
+    }
+
+    companion object {
+        private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
+        private const val NON_DEFAULT_DISPLAY_ID = DEFAULT_DISPLAY_ID + 1
+        private const val NON_EXISTING_DISPLAY_ID = DEFAULT_DISPLAY_ID + 2
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
index fad52e0..ba43a23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -38,9 +38,9 @@
 
 import android.app.ActivityManager;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.UiThreadTest;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
index d9dcfdc..9c6fd4b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
@@ -56,6 +56,7 @@
 
         // Adding twice should not invoke twice
         reset(callback)
+        underTest.onStartDream()
         underTest.addCallback(callback)
         underTest.onWakeUp()
         verify(callback, times(1)).onWakeUp()
@@ -68,6 +69,19 @@
     }
 
     @Test
+    fun onWakeUp_multipleCalls() {
+        underTest.onStartDream()
+        assertThat(underTest.isDreaming).isEqualTo(true)
+
+        underTest.addCallback(callback)
+        underTest.onWakeUp()
+        underTest.onWakeUp()
+        underTest.onWakeUp()
+        verify(callback, times(1)).onWakeUp()
+        assertThat(underTest.isDreaming).isEqualTo(false)
+    }
+
+    @Test
     fun onStartDreamInvokesCallback() {
         underTest.addCallback(callback)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index a3314e8..f5d2d42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -712,6 +712,9 @@
 
         // Verify DreamOverlayContainerViewController is destroyed.
         verify(mDreamOverlayContainerViewController).destroy()
+
+        // DreamOverlay callback receives onWakeUp.
+        verify(mDreamOverlayCallbackController).onWakeUp()
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index 8201bbe..e7d2ef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -30,8 +30,11 @@
 import com.android.systemui.education.data.repository.contextualEducationRepository
 import com.android.systemui.education.data.repository.fakeEduClock
 import com.android.systemui.education.shared.model.EducationUiType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
 import com.android.systemui.keyboard.data.repository.keyboardRepository
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
 import com.android.systemui.testKosmos
 import com.android.systemui.touchpad.data.repository.touchpadRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
@@ -42,10 +45,13 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.verify
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
@@ -56,14 +62,19 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val contextualEduInteractor = kosmos.contextualEducationInteractor
+    private val repository = kosmos.contextualEducationRepository
     private val touchpadRepository = kosmos.touchpadRepository
     private val keyboardRepository = kosmos.keyboardRepository
+    private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
     private val userRepository = kosmos.fakeUserRepository
+    private val overviewProxyService = kosmos.mockOverviewProxyService
 
     private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
     private val eduClock = kosmos.fakeEduClock
     private val minDurationForNextEdu =
         KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
+    private val initialDelayElapsedDuration =
+        KeyboardTouchpadEduInteractor.initialDelayDuration + 1.seconds
 
     @Before
     fun setup() {
@@ -271,6 +282,131 @@
             assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant())
         }
 
+    @Test
+    fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
+        testScope.runTest {
+            assumeTrue(gestureType != ALL_APPS)
+            setUpForInitialDelayElapse()
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            val originalValue = model!!.signalCount
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+            assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+        }
+
+    @Test
+    fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
+        testScope.runTest {
+            setUpForInitialDelayElapse()
+            touchpadRepository.setIsAnyTouchpadConnected(false)
+
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            val originalValue = model!!.signalCount
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+            assertThat(model?.signalCount).isEqualTo(originalValue)
+        }
+
+    @Test
+    fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
+        testScope.runTest {
+            assumeTrue(gestureType == ALL_APPS)
+            setUpForInitialDelayElapse()
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            val originalValue = model!!.signalCount
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+            assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+        }
+
+    @Test
+    fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
+        testScope.runTest {
+            setUpForInitialDelayElapse()
+            keyboardRepository.setIsAnyKeyboardConnected(false)
+
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            val originalValue = model!!.signalCount
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+            assertThat(model?.signalCount).isEqualTo(originalValue)
+        }
+
+    @Test
+    fun dataAddedOnUpdateShortcutTriggerTime() =
+        testScope.runTest {
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            assertThat(model?.lastShortcutTriggeredTime).isNull()
+
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ true, gestureType)
+
+            assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
+        }
+
+    @Test
+    fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
+        testScope.runTest {
+            setUpForDeviceConnection()
+            tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            val originalValue = model!!.signalCount
+            eduClock.offset(initialDelayElapsedDuration)
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+            assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+        }
+
+    @Test
+    fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
+        testScope.runTest {
+            setUpForDeviceConnection()
+            tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            val originalValue = model!!.signalCount
+            // No offset to the clock to simulate update before initial delay
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+            assertThat(model?.signalCount).isEqualTo(originalValue)
+        }
+
+    @Test
+    fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() =
+        testScope.runTest {
+            // No update to OOBE launch time to simulate no OOBE is launched yet
+            setUpForDeviceConnection()
+
+            val model by collectLastValue(repository.readGestureEduModelFlow(gestureType))
+            val originalValue = model!!.signalCount
+            val listener = getOverviewProxyListener()
+            listener.updateContextualEduStats(/* isTrackpadGesture= */ false, gestureType)
+
+            assertThat(model?.signalCount).isEqualTo(originalValue)
+        }
+
+    private suspend fun setUpForInitialDelayElapse() {
+        tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, eduClock.instant())
+        tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, eduClock.instant())
+        eduClock.offset(initialDelayElapsedDuration)
+    }
+
+    @After
+    fun clear() {
+        testScope.launch { tutorialSchedulerRepository.clearDataStore() }
+    }
+
     private suspend fun triggerMaxEducationSignals(gestureType: GestureType) {
         // Increment max number of signal to try triggering education
         for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
@@ -288,9 +424,15 @@
         runCurrent()
     }
 
-    private suspend fun setUpForDeviceConnection() {
-        contextualEduInteractor.updateKeyboardFirstConnectionTime()
-        contextualEduInteractor.updateTouchpadFirstConnectionTime()
+    private fun setUpForDeviceConnection() {
+        touchpadRepository.setIsAnyTouchpadConnected(true)
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+    }
+
+    private fun getOverviewProxyListener(): OverviewProxyListener {
+        val listenerCaptor = argumentCaptor<OverviewProxyListener>()
+        verify(overviewProxyService).addCallback(listenerCaptor.capture())
+        return listenerCaptor.firstValue
     }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
deleted file mode 100644
index 98e0947..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.education.domain.interactor
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.contextualeducation.GestureType.ALL_APPS
-import com.android.systemui.contextualeducation.GestureType.BACK
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.education.data.repository.contextualEducationRepository
-import com.android.systemui.education.data.repository.fakeEduClock
-import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
-import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository
-import com.android.systemui.keyboard.data.repository.keyboardRepository
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.data.repository.touchpadRepository
-import com.google.common.truth.Truth.assertThat
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.runTest
-import org.junit.After
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val underTest = kosmos.keyboardTouchpadEduStatsInteractor
-    private val keyboardRepository = kosmos.keyboardRepository
-    private val touchpadRepository = kosmos.touchpadRepository
-    private val repository = kosmos.contextualEducationRepository
-    private val fakeClock = kosmos.fakeEduClock
-    private val tutorialSchedulerRepository = kosmos.tutorialSchedulerRepository
-    private val initialDelayElapsedDuration =
-        KeyboardTouchpadEduStatsInteractorImpl.initialDelayDuration + 1.seconds
-
-    @Test
-    fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
-        testScope.runTest {
-            setUpForInitialDelayElapse()
-            touchpadRepository.setIsAnyTouchpadConnected(true)
-
-            val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
-            val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(BACK)
-
-            assertThat(model?.signalCount).isEqualTo(originalValue + 1)
-        }
-
-    @Test
-    fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
-        testScope.runTest {
-            setUpForInitialDelayElapse()
-            touchpadRepository.setIsAnyTouchpadConnected(false)
-
-            val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
-            val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(BACK)
-
-            assertThat(model?.signalCount).isEqualTo(originalValue)
-        }
-
-    @Test
-    fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
-        testScope.runTest {
-            setUpForInitialDelayElapse()
-            keyboardRepository.setIsAnyKeyboardConnected(true)
-
-            val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS))
-            val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(ALL_APPS)
-
-            assertThat(model?.signalCount).isEqualTo(originalValue + 1)
-        }
-
-    @Test
-    fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
-        testScope.runTest {
-            setUpForInitialDelayElapse()
-            keyboardRepository.setIsAnyKeyboardConnected(false)
-
-            val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS))
-            val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(ALL_APPS)
-
-            assertThat(model?.signalCount).isEqualTo(originalValue)
-        }
-
-    @Test
-    fun dataAddedOnUpdateShortcutTriggerTime() =
-        testScope.runTest {
-            val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
-            assertThat(model?.lastShortcutTriggeredTime).isNull()
-            underTest.updateShortcutTriggerTime(BACK)
-            assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
-        }
-
-    @Test
-    fun dataUpdatedOnIncrementSignalCountAfterInitialDelay() =
-        testScope.runTest {
-            setUpForDeviceConnection()
-            tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant())
-
-            fakeClock.offset(initialDelayElapsedDuration)
-            val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
-            val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(BACK)
-
-            assertThat(model?.signalCount).isEqualTo(originalValue + 1)
-        }
-
-    @Test
-    fun dataUnchangedOnIncrementSignalCountBeforeInitialDelay() =
-        testScope.runTest {
-            setUpForDeviceConnection()
-            tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant())
-
-            // No offset to the clock to simulate update before initial delay
-            val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
-            val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(BACK)
-
-            assertThat(model?.signalCount).isEqualTo(originalValue)
-        }
-
-    @Test
-    fun dataUnchangedOnIncrementSignalCountWithoutOobeLaunchTime() =
-        testScope.runTest {
-            // No update to OOBE launch time to simulate no OOBE is launched yet
-            setUpForDeviceConnection()
-
-            val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
-            val originalValue = model!!.signalCount
-            underTest.incrementSignalCount(BACK)
-
-            assertThat(model?.signalCount).isEqualTo(originalValue)
-        }
-
-    private suspend fun setUpForInitialDelayElapse() {
-        tutorialSchedulerRepository.updateLaunchTime(DeviceType.TOUCHPAD, fakeClock.instant())
-        tutorialSchedulerRepository.updateLaunchTime(DeviceType.KEYBOARD, fakeClock.instant())
-        fakeClock.offset(initialDelayElapsedDuration)
-    }
-
-    private fun setUpForDeviceConnection() {
-        touchpadRepository.setIsAnyTouchpadConnected(true)
-        keyboardRepository.setIsAnyKeyboardConnected(true)
-    }
-
-    @After
-    fun clear() {
-        testScope.launch { tutorialSchedulerRepository.clearDataStore() }
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt
new file mode 100644
index 0000000..90727b2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduDialogTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.education.domain.ui.view
+
+import android.testing.TestableLooper
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.EmptyTestActivity
+import com.android.systemui.education.ui.view.ContextualEduDialog
+import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel
+import kotlin.test.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.firstValue
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class ContextualEduDialogTest : SysuiTestCase() {
+    @Rule
+    @JvmField
+    val activityRule: ActivityScenarioRule<EmptyTestActivity> =
+        ActivityScenarioRule(EmptyTestActivity::class.java)
+    @get:Rule val mockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var accessibilityManager: AccessibilityManager
+    private lateinit var underTest: ContextualEduDialog
+
+    @Before
+    fun setUp() {
+        whenever(accessibilityManager.isEnabled).thenReturn(true)
+    }
+
+    @Test
+    fun sendAccessibilityInfo() {
+        val message = "Testing message"
+        val viewModel = ContextualEduToastViewModel(message, icon = 0, userId = 0)
+        activityRule.scenario.onActivity {
+            underTest = ContextualEduDialog(context, viewModel, accessibilityManager)
+            underTest.show()
+        }
+
+        val eventCaptor = ArgumentCaptor.forClass(AccessibilityEvent::class.java)
+        verify(accessibilityManager).sendAccessibilityEvent(eventCaptor.capture())
+        assertEquals(message, eventCaptor.firstValue.text[0])
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index 4a80d72..28f88fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -17,13 +17,11 @@
 package com.android.systemui.haptics.slider
 
 import android.os.VibrationEffect
-import android.view.VelocityTracker
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.haptics.fakeVibratorHelper
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.fakeSystemClock
 import kotlin.math.max
 import kotlin.test.assertEquals
@@ -31,19 +29,17 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SliderHapticFeedbackProviderTest : SysuiTestCase() {
 
-    @Mock private lateinit var velocityTracker: VelocityTracker
-
     private val kosmos = testKosmos()
 
     private val config = SliderHapticFeedbackConfig()
 
+    private val dragVelocityProvider = SliderDragVelocityProvider { config.maxVelocityToScale }
+
     private val lowTickDuration = 12 // Mocked duration of a low tick
     private val dragTextureThresholdMillis =
         lowTickDuration * config.numberOfLowTicks + config.deltaMillisForDragInterval
@@ -52,17 +48,13 @@
 
     @Before
     fun setup() {
-        MockitoAnnotations.initMocks(this)
-        whenever(velocityTracker.isAxisSupported(config.velocityAxis)).thenReturn(true)
-        whenever(velocityTracker.getAxisVelocity(config.velocityAxis))
-            .thenReturn(config.maxVelocityToScale)
 
         vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] =
             lowTickDuration
         sliderHapticFeedbackProvider =
             SliderHapticFeedbackProvider(
                 vibratorHelper,
-                velocityTracker,
+                dragVelocityProvider,
                 config,
                 kosmos.fakeSystemClock,
             )
@@ -75,9 +67,7 @@
                 VibrationEffect.startComposition()
                     .addPrimitive(
                         VibrationEffect.Composition.PRIMITIVE_CLICK,
-                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(
-                            config.maxVelocityToScale
-                        ),
+                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale),
                     )
                     .compose()
 
@@ -93,7 +83,7 @@
                 VibrationEffect.startComposition()
                     .addPrimitive(
                         VibrationEffect.Composition.PRIMITIVE_CLICK,
-                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale)
+                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale),
                     )
                     .compose()
 
@@ -110,9 +100,7 @@
                 VibrationEffect.startComposition()
                     .addPrimitive(
                         VibrationEffect.Composition.PRIMITIVE_CLICK,
-                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(
-                            config.maxVelocityToScale
-                        ),
+                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale),
                     )
                     .compose()
 
@@ -128,9 +116,7 @@
                 VibrationEffect.startComposition()
                     .addPrimitive(
                         VibrationEffect.Composition.PRIMITIVE_CLICK,
-                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(
-                            config.maxVelocityToScale
-                        ),
+                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale),
                     )
                     .compose()
 
@@ -146,10 +132,7 @@
             // GIVEN max velocity and slider progress
             val progress = 1f
             val expectedScale =
-                sliderHapticFeedbackProvider.scaleOnDragTexture(
-                    config.maxVelocityToScale,
-                    progress,
-                )
+                sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress)
             val ticks = VibrationEffect.startComposition()
             repeat(config.numberOfLowTicks) {
                 ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
@@ -222,10 +205,7 @@
             // GIVEN max velocity and slider progress
             val progress = 1f
             val expectedScale =
-                sliderHapticFeedbackProvider.scaleOnDragTexture(
-                    config.maxVelocityToScale,
-                    progress,
-                )
+                sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress)
             val ticks = VibrationEffect.startComposition()
             repeat(config.numberOfLowTicks) {
                 ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
@@ -234,9 +214,7 @@
                 VibrationEffect.startComposition()
                     .addPrimitive(
                         VibrationEffect.Composition.PRIMITIVE_CLICK,
-                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(
-                            config.maxVelocityToScale
-                        ),
+                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale),
                     )
                     .compose()
 
@@ -250,7 +228,7 @@
             // THEN there are two bookend vibrations
             assertEquals(
                 /* expected= */ 2,
-                vibratorHelper.timesVibratedWithEffect(bookendVibration)
+                vibratorHelper.timesVibratedWithEffect(bookendVibration),
             )
         }
 
@@ -260,10 +238,7 @@
             // GIVEN max velocity and slider progress
             val progress = 1f
             val expectedScale =
-                sliderHapticFeedbackProvider.scaleOnDragTexture(
-                    config.maxVelocityToScale,
-                    progress,
-                )
+                sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress)
             val ticks = VibrationEffect.startComposition()
             repeat(config.numberOfLowTicks) {
                 ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
@@ -272,9 +247,7 @@
                 VibrationEffect.startComposition()
                     .addPrimitive(
                         VibrationEffect.Composition.PRIMITIVE_CLICK,
-                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(
-                            config.maxVelocityToScale
-                        ),
+                        sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale),
                     )
                     .compose()
 
@@ -288,7 +261,7 @@
             // THEN there are two bookend vibrations
             assertEquals(
                 /* expected= */ 2,
-                vibratorHelper.timesVibratedWithEffect(bookendVibration)
+                vibratorHelper.timesVibratedWithEffect(bookendVibration),
             )
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModelTest.kt
new file mode 100644
index 0000000..8693f6b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModelTest.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider.compose.ui
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.DragInteraction
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
+import com.android.systemui.haptics.slider.SliderEventType
+import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SliderHapticsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val interactionSource = DragInteractionSourceTest()
+    private val underTest =
+        kosmos.sliderHapticsViewModelFactory.create(
+            interactionSource,
+            0f..1f,
+            Orientation.Horizontal,
+            SliderHapticFeedbackConfig(),
+            SeekableSliderTrackerConfig(),
+        )
+
+    @Before
+    fun setUp() {
+        underTest.activateIn(testScope)
+    }
+
+    @Test
+    fun onActivated_startsRunning() =
+        testScope.runTest {
+            // WHEN the view-model is activated
+            testScope.runCurrent()
+
+            // THEN the view-model starts running
+            assertThat(underTest.isRunning).isTrue()
+        }
+
+    @Test
+    fun onDragStart_goesToUserStartedDragging() =
+        testScope.runTest {
+            // WHEN a drag interaction starts
+            interactionSource.setDragInteraction(DragInteraction.Start())
+            runCurrent()
+
+            // THEN the current slider event type shows that the user started dragging
+            assertThat(underTest.currentSliderEventType)
+                .isEqualTo(SliderEventType.STARTED_TRACKING_TOUCH)
+        }
+
+    @Test
+    fun onValueChange_whileUserStartedDragging_goesToUserDragging() =
+        testScope.runTest {
+            // WHEN a drag interaction starts
+            interactionSource.setDragInteraction(DragInteraction.Start())
+            runCurrent()
+
+            // WHEN a value changes in the slider
+            underTest.onValueChange(0.5f)
+
+            // THEN the current slider event type shows that the user is dragging
+            assertThat(underTest.currentSliderEventType)
+                .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_USER)
+        }
+
+    @Test
+    fun onValueChange_whileUserDragging_staysInUserDragging() =
+        testScope.runTest {
+            // WHEN a drag interaction starts and the user keeps dragging
+            interactionSource.setDragInteraction(DragInteraction.Start())
+            runCurrent()
+            underTest.onValueChange(0.5f)
+
+            // WHEN value changes continue to occur due to dragging
+            underTest.onValueChange(0.6f)
+
+            // THEN the current slider event type reflects that the user continues to drag
+            assertThat(underTest.currentSliderEventType)
+                .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_USER)
+        }
+
+    @Test
+    fun onValueChange_whileNOTHING_goesToProgramStartedDragging() =
+        testScope.runTest {
+            // WHEN a value change occurs without a drag interaction
+            underTest.onValueChange(0.5f)
+
+            // THEN the current slider event type shows that the program started dragging
+            assertThat(underTest.currentSliderEventType)
+                .isEqualTo(SliderEventType.STARTED_TRACKING_PROGRAM)
+        }
+
+    @Test
+    fun onValueChange_whileProgramStartedDragging_goesToProgramDragging() =
+        testScope.runTest {
+            // WHEN the program starts dragging
+            underTest.onValueChange(0.5f)
+
+            // WHEN the program continues to make value changes
+            underTest.onValueChange(0.6f)
+
+            // THEN the current slider event type shows that program is dragging
+            assertThat(underTest.currentSliderEventType)
+                .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM)
+        }
+
+    @Test
+    fun onValueChange_whileProgramDragging_staysInProgramDragging() =
+        testScope.runTest {
+            // WHEN the program starts and continues to drag
+            underTest.onValueChange(0.5f)
+            underTest.onValueChange(0.6f)
+
+            // WHEN value changes continue to occur
+            underTest.onValueChange(0.7f)
+
+            // THEN the current slider event type shows that the program is dragging the slider
+            assertThat(underTest.currentSliderEventType)
+                .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM)
+        }
+
+    @Test
+    fun onValueChangeEnded_goesToNOTHING() =
+        testScope.runTest {
+            // WHEN changes end in the slider
+            underTest.onValueChangeEnded()
+
+            // THEN the current slider event type always resets to NOTHING
+            assertThat(underTest.currentSliderEventType).isEqualTo(SliderEventType.NOTHING)
+        }
+
+    private class DragInteractionSourceTest : InteractionSource {
+        private val _interactions = MutableStateFlow<DragInteraction>(IdleDrag)
+        override val interactions = _interactions.asStateFlow()
+
+        fun setDragInteraction(interaction: DragInteraction) {
+            _interactions.value = interaction
+        }
+    }
+
+    private object IdleDrag : DragInteraction
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index e251ab5..baf3b5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -20,7 +20,10 @@
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.defaultDeviceState
+import com.android.systemui.deviceStateManager
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -28,7 +31,6 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
-import com.android.systemui.util.mockito.whenever
 import java.util.function.Predicate
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
@@ -47,6 +49,7 @@
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
@@ -65,6 +68,8 @@
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
     @Mock private lateinit var powerManager: PowerManager
     @Mock private lateinit var wallpaperManager: WallpaperManager
+    private val kosmos = Kosmos()
+    private val deviceStateManager = kosmos.deviceStateManager
 
     @Mock
     private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub
@@ -173,7 +178,8 @@
                     statusBarStateController,
                     notificationShadeWindowController,
                     powerManager,
-                    wallpaperManager
+                    wallpaperManager,
+                    deviceStateManager
                 ) {
                 override fun shouldPerformSmartspaceTransition(): Boolean =
                     shouldPerformSmartspaceTransition
@@ -185,6 +191,8 @@
 
         whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
         whenever(powerManager.isInteractive).thenReturn(true)
+        whenever(deviceStateManager.supportedDeviceStates)
+            .thenReturn(listOf(kosmos.defaultDeviceState))
 
         // All of these fields are final, so we can't mock them, but are needed so that the surface
         // appear amount setter doesn't short circuit.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 2a2a82d..b5ea305 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -100,18 +99,14 @@
                 FakeTrustRepository(),
                 testScope.backgroundScope,
                 mSelectedUserInteractor,
-                faceAuthInteractor
+                faceAuthInteractor,
             )
 
         alternateBouncerInteractor =
             AlternateBouncerInteractor(
-                mock(StatusBarStateController::class.java),
-                mock(KeyguardStateController::class.java),
                 bouncerRepository,
                 FakeFingerprintPropertyRepository(),
-                biometricSettingsRepository,
                 FakeSystemClock(),
-                keyguardUpdateMonitor,
                 { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
                 { mock(KeyguardInteractor::class.java) },
                 { mock(KeyguardTransitionInteractor::class.java) },
@@ -121,13 +116,12 @@
 
         underTest =
             DeviceEntrySideFpsOverlayInteractor(
-                testScope.backgroundScope,
                 mContext,
                 deviceEntryFingerprintAuthRepository,
                 kosmos.sceneInteractor,
                 primaryBouncerInteractor,
                 alternateBouncerInteractor,
-                keyguardUpdateMonitor
+                keyguardUpdateMonitor,
             )
     }
 
@@ -142,7 +136,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isTrue()
         }
@@ -158,7 +152,7 @@
                 isShowing = false,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -169,13 +163,12 @@
         testScope.runTest {
             underTest =
                 DeviceEntrySideFpsOverlayInteractor(
-                    testScope.backgroundScope,
                     mContext,
                     deviceEntryFingerprintAuthRepository,
                     kosmos.sceneInteractor,
                     primaryBouncerInteractor,
                     alternateBouncerInteractor,
-                    keyguardUpdateMonitor
+                    keyguardUpdateMonitor,
                 )
 
             val showIndicatorForDeviceEntry by
@@ -185,7 +178,7 @@
             updateBouncerScene(
                 isActive = true,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isTrue()
         }
@@ -196,13 +189,12 @@
         testScope.runTest {
             underTest =
                 DeviceEntrySideFpsOverlayInteractor(
-                    testScope.backgroundScope,
                     mContext,
                     deviceEntryFingerprintAuthRepository,
                     kosmos.sceneInteractor,
                     primaryBouncerInteractor,
                     alternateBouncerInteractor,
-                    keyguardUpdateMonitor
+                    keyguardUpdateMonitor,
                 )
 
             val showIndicatorForDeviceEntry by
@@ -212,7 +204,7 @@
             updateBouncerScene(
                 isActive = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -228,7 +220,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = false,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -245,7 +237,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = false
+                isUnlockingWithFpAllowed = false,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -261,7 +253,7 @@
             updateBouncerScene(
                 isActive = true,
                 fpsDetectionRunning = false,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -277,7 +269,7 @@
             updateBouncerScene(
                 isActive = true,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = false
+                isUnlockingWithFpAllowed = false,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -294,7 +286,7 @@
                 isShowing = true,
                 isAnimatingAway = true,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
             assertThat(showIndicatorForDeviceEntry).isFalse()
         }
@@ -325,7 +317,7 @@
                 isShowing = true,
                 isAnimatingAway = false,
                 fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
+                isUnlockingWithFpAllowed = true,
             )
 
             // Another request to show indicator for deviceEntryFingerprintAuthRepository update
@@ -355,7 +347,7 @@
             .thenReturn(isUnlockingWithFpAllowed)
         mContext.orCreateTestableResources.addOverride(
             R.bool.config_show_sidefps_hint_on_bouncer,
-            true
+            true,
         )
     }
 
@@ -366,7 +358,7 @@
     ) {
         kosmos.sceneInteractor.changeScene(
             if (isActive) Scenes.Bouncer else Scenes.Lockscreen,
-            "reason"
+            "reason",
         )
 
         whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
@@ -375,7 +367,7 @@
             .thenReturn(isUnlockingWithFpAllowed)
         mContext.orCreateTestableResources.addOverride(
             R.bool.config_show_sidefps_hint_on_bouncer,
-            true
+            true,
         )
         runCurrent()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 3fb3eea..b3417b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -150,6 +150,53 @@
             assertThat(secureCameraActive()).isFalse()
         }
 
+    /** Regression test for b/373700726. */
+    @Test
+    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun testSecureCameraStillFalseAfterDeviceUnlocked() =
+        testScope.runTest {
+            val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+            runCurrent()
+
+            // Launch camera
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+            assertThat(secureCameraActive()).isTrue()
+
+            // Go back to keyguard
+            repository.setKeyguardShowing(true)
+            repository.setKeyguardOccluded(false)
+            assertThat(secureCameraActive()).isFalse()
+
+            // WHEN device is unlocked (and therefore keyguard is no longer showing)
+            repository.setKeyguardShowing(false)
+
+            // THEN we still show secure camera as *not* active
+            assertThat(secureCameraActive()).isFalse()
+        }
+
+    /** Regression test for b/373700726. */
+    @Test
+    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun testSecureCameraStillFalseAfterBouncerDismissed() =
+        testScope.runTest {
+            val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+            runCurrent()
+
+            // Launch camera
+            underTest.onCameraLaunchDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+            assertThat(secureCameraActive()).isTrue()
+
+            // Show bouncer
+            bouncerRepository.setPrimaryShow(true)
+            assertThat(secureCameraActive()).isFalse()
+
+            // WHEN device is unlocked (and therefore the bouncer is no longer showing)
+            bouncerRepository.setPrimaryShow(false)
+
+            // THEN we still show secure camera as *not* active
+            assertThat(secureCameraActive()).isFalse()
+        }
+
     @Test
     @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
@@ -182,11 +229,7 @@
             val dismissAlpha by collectLastValue(underTest.dismissAlpha)
             assertThat(dismissAlpha).isEqualTo(1f)
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = AOD,
-                to = LOCKSCREEN,
-                testScope,
-            )
+            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
 
             repository.setStatusBarState(StatusBarState.KEYGUARD)
             // User begins to swipe up
@@ -208,11 +251,7 @@
             assertThat(dismissAlpha[0]).isEqualTo(1f)
             assertThat(dismissAlpha.size).isEqualTo(1)
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = AOD,
-                to = LOCKSCREEN,
-                testScope,
-            )
+            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
 
             // User begins to swipe up
             repository.setStatusBarState(StatusBarState.KEYGUARD)
@@ -328,11 +367,7 @@
 
             shadeRepository.setLegacyShadeExpansion(0f)
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = AOD,
-                to = LOCKSCREEN,
-                testScope,
-            )
+            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
 
             assertThat(keyguardTranslationY).isEqualTo(0f)
         }
@@ -350,11 +385,7 @@
 
             shadeRepository.setLegacyShadeExpansion(1f)
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = AOD,
-                to = LOCKSCREEN,
-                testScope,
-            )
+            keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
 
             assertThat(keyguardTranslationY).isEqualTo(0f)
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index 13f30f5..945e44a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -46,6 +46,7 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.isNull
 
 @ExperimentalCoroutinesApi
 @SmallTest
@@ -96,7 +97,7 @@
             .sendVolumeKeyEvent(
                 eq(actionDownVolumeDownKeyEvent),
                 eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
-                eq(true)
+                eq(true),
             )
 
         assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isTrue()
@@ -104,7 +105,7 @@
             .sendVolumeKeyEvent(
                 eq(actionDownVolumeUpKeyEvent),
                 eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
-                eq(true)
+                eq(true),
             )
     }
 
@@ -117,7 +118,7 @@
             .sendVolumeKeyEvent(
                 eq(actionDownVolumeDownKeyEvent),
                 eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
-                eq(true)
+                eq(true),
             )
 
         assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isFalse()
@@ -125,7 +126,7 @@
             .sendVolumeKeyEvent(
                 eq(actionDownVolumeUpKeyEvent),
                 eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
-                eq(true)
+                eq(true),
             )
     }
 
@@ -135,7 +136,9 @@
         whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(statusBarKeyguardViewManager).dismissWithAction(any(), isNull(), eq(false))
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 12039c1..e079619 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -118,11 +118,11 @@
                             LOCKSCREEN,
                             0f,
                             STARTED,
-                            ownerName = "KeyguardTransitionRepository(boot)"
+                            ownerName = "KeyguardTransitionRepository(boot)",
                         ),
                         steps[0],
                         steps[3],
-                        steps[6]
+                        steps[6],
                     )
                 )
         }
@@ -253,51 +253,20 @@
                     true, // The repo is seeded with a transition from OFF to LOCKSCREEN.
                     false,
                 ),
-                inTransition
+                inTransition,
             )
 
-            sendSteps(
-                TransitionStep(LOCKSCREEN, GONE, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(LOCKSCREEN, GONE, 0f, STARTED))
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true), inTransition)
 
-            sendSteps(
-                TransitionStep(LOCKSCREEN, GONE, 0.5f, RUNNING),
-            )
+            sendSteps(TransitionStep(LOCKSCREEN, GONE, 0.5f, RUNNING))
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true), inTransition)
 
-            sendSteps(
-                TransitionStep(LOCKSCREEN, GONE, 1f, FINISHED),
-            )
+            sendSteps(TransitionStep(LOCKSCREEN, GONE, 1f, FINISHED))
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                    false,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true, false), inTransition)
         }
 
     @Test
@@ -312,33 +281,16 @@
                     true, // The repo is seeded with a transition from OFF to LOCKSCREEN.
                     false,
                 ),
-                inTransition
+                inTransition,
             )
 
             kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer))
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true), inTransition)
 
             kosmos.setSceneTransition(Idle(Scenes.Bouncer))
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                    false,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true, false), inTransition)
         }
 
     @Test
@@ -346,14 +298,7 @@
         testScope.runTest {
             val inTransition by collectValues(underTest.isInTransition)
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false), inTransition)
 
             // Start FINISHED in GONE.
             sendSteps(
@@ -362,32 +307,11 @@
                 TransitionStep(LOCKSCREEN, GONE, 1f, FINISHED),
             )
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                    false,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true, false), inTransition)
 
-            sendSteps(
-                TransitionStep(GONE, DOZING, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(GONE, DOZING, 0f, STARTED))
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                    false,
-                    true,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true, false, true), inTransition)
 
             sendSteps(
                 TransitionStep(GONE, DOZING, 0.5f, RUNNING),
@@ -410,7 +334,7 @@
                     // transitioning to GONE, the state we're also FINISHED in.
                     true,
                 ),
-                inTransition
+                inTransition,
             )
 
             sendSteps(
@@ -418,18 +342,7 @@
                 TransitionStep(LOCKSCREEN, GONE, 1f, FINISHED),
             )
 
-            assertEquals(
-                listOf(
-                    false,
-                    true,
-                    false,
-                    true,
-                    false,
-                    true,
-                    false,
-                ),
-                inTransition
-            )
+            assertEquals(listOf(false, true, false, true, false, true, false), inTransition)
         }
 
     @Test
@@ -440,7 +353,7 @@
                 collectValues(
                     underTest.isInTransition(
                         edge = Edge.create(OFF, OFF),
-                        edgeWithoutSceneContainer = Edge.create(to = LOCKSCREEN)
+                        edgeWithoutSceneContainer = Edge.create(to = LOCKSCREEN),
                     )
                 )
 
@@ -450,49 +363,19 @@
                 TransitionStep(AOD, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
-            sendSteps(
-                TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
-            )
+            sendSteps(TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED),
-            )
+            sendSteps(TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED),
@@ -500,29 +383,14 @@
                 TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
                 TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false, true))
         }
 
     @Test
@@ -535,33 +403,15 @@
             kosmos.setSceneTransition(Transition(from = Scenes.Gone, to = Scenes.Lockscreen))
             kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
             kosmos.setSceneTransition(Transition(from = Scenes.Lockscreen, to = Scenes.Shade))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             kosmos.setSceneTransition(Idle(Scenes.Shade))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
         }
 
     @Test
@@ -575,14 +425,7 @@
             kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
             kosmos.setSceneTransition(Transition(from = Scenes.Lockscreen, to = Scenes.Gone))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
         }
 
     @Test
@@ -602,14 +445,7 @@
 
             kosmos.setSceneTransition(Idle(Scenes.Gone))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
         }
 
     @Test
@@ -623,49 +459,19 @@
                 TransitionStep(AOD, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
-            sendSteps(
-                TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
-            )
+            sendSteps(TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED),
-            )
+            sendSteps(TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED),
@@ -673,29 +479,14 @@
                 TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
                 TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false, true))
         }
 
     @Test
@@ -715,49 +506,19 @@
                 TransitionStep(AOD, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
-            sendSteps(
-                TransitionStep(DOZING, GONE, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, GONE, 0f, RUNNING),
-            )
+            sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, GONE, 0f, FINISHED),
-            )
+            sendSteps(TransitionStep(DOZING, GONE, 0f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(GONE, DOZING, 0f, STARTED),
@@ -765,29 +526,14 @@
                 TransitionStep(GONE, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(DOZING, GONE, 0f, STARTED),
                 TransitionStep(DOZING, GONE, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false, true))
         }
 
     @Test
@@ -807,48 +553,19 @@
                 TransitionStep(AOD, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
-            sendSteps(
-                TransitionStep(DOZING, GONE, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, GONE, 0f, RUNNING),
-            )
+            sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
-            sendSteps(
-                TransitionStep(DOZING, GONE, 0f, CANCELED),
-            )
+            sendSteps(TransitionStep(DOZING, GONE, 0f, CANCELED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             sendSteps(
                 TransitionStep(GONE, DOZING, 0f, STARTED),
@@ -856,29 +573,14 @@
                 TransitionStep(GONE, DOZING, 1f, FINISHED),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(DOZING, GONE, 0f, STARTED),
                 TransitionStep(DOZING, GONE, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false, true))
         }
 
     @Test
@@ -895,87 +597,43 @@
             assertThat(results)
                 .isEqualTo(
                     listOf(
-                        false, // Finished in DOZING, not GONE.
+                        false // Finished in DOZING, not GONE.
                     )
                 )
 
             sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
             sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
             sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             sendSteps(
                 TransitionStep(GONE, DOZING, 0f, STARTED),
                 TransitionStep(GONE, DOZING, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             sendSteps(TransitionStep(GONE, DOZING, 1f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(DOZING, GONE, 0f, STARTED),
                 TransitionStep(DOZING, GONE, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false, true))
         }
 
     @Test
@@ -993,87 +651,43 @@
             assertThat(results)
                 .isEqualTo(
                     listOf(
-                        false, // Finished in DOZING, not GONE.
+                        false // Finished in DOZING, not GONE.
                     )
                 )
 
             sendSteps(TransitionStep(DOZING, GONE, 0f, STARTED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
             sendSteps(TransitionStep(DOZING, GONE, 0f, RUNNING))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
             sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             sendSteps(
                 TransitionStep(GONE, DOZING, 0f, STARTED),
                 TransitionStep(GONE, DOZING, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             sendSteps(TransitionStep(GONE, DOZING, 1f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(
                 TransitionStep(DOZING, GONE, 0f, STARTED),
                 TransitionStep(DOZING, GONE, 0f, RUNNING),
             )
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             sendSteps(TransitionStep(DOZING, GONE, 1f, FINISHED))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false, true))
         }
 
     @Test
@@ -1091,72 +705,33 @@
             assertThat(results)
                 .isEqualTo(
                     listOf(
-                        false, // Finished in DOZING, not GONE.
+                        false // Finished in DOZING, not GONE.
                     )
                 )
 
             kosmos.setSceneTransition(Transition(from = Scenes.Lockscreen, to = Scenes.Gone))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false))
 
             kosmos.setSceneTransition(Idle(Scenes.Gone))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             kosmos.setSceneTransition(Transition(from = Scenes.Gone, to = Scenes.Lockscreen))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true))
 
             kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             kosmos.setSceneTransition(Transition(from = Scenes.Lockscreen, to = Scenes.Gone))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false))
 
             kosmos.setSceneTransition(Idle(Scenes.Gone))
 
-            assertThat(results)
-                .isEqualTo(
-                    listOf(
-                        false,
-                        true,
-                        false,
-                        true,
-                    )
-                )
+            assertThat(results).isEqualTo(listOf(false, true, false, true))
         }
 
     @Test
@@ -1165,68 +740,29 @@
             val currentStates by collectValues(underTest.currentKeyguardState)
 
             // We init the repo with a transition from OFF -> LOCKSCREEN.
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN), currentStates)
 
-            sendSteps(
-                TransitionStep(LOCKSCREEN, AOD, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
 
             // The current state should continue to be LOCKSCREEN as we transition to AOD.
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN), currentStates)
 
-            sendSteps(
-                TransitionStep(LOCKSCREEN, AOD, 0.5f, RUNNING),
-            )
+            sendSteps(TransitionStep(LOCKSCREEN, AOD, 0.5f, RUNNING))
 
             // The current state should continue to be LOCKSCREEN as we transition to AOD.
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN), currentStates)
 
-            sendSteps(
-                TransitionStep(LOCKSCREEN, AOD, 0.6f, CANCELED),
-            )
+            sendSteps(TransitionStep(LOCKSCREEN, AOD, 0.6f, CANCELED))
 
             // Once CANCELED, we're still currently in LOCKSCREEN...
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN), currentStates)
 
-            sendSteps(
-                TransitionStep(AOD, LOCKSCREEN, 0.6f, STARTED),
-            )
+            sendSteps(TransitionStep(AOD, LOCKSCREEN, 0.6f, STARTED))
 
             // ...until STARTING back to LOCKSCREEN, at which point the "current" state should be
             // the
             // one we're transitioning from, despite never FINISHING in that state.
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                    AOD,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN, AOD), currentStates)
 
             sendSteps(
                 TransitionStep(AOD, LOCKSCREEN, 0.8f, RUNNING),
@@ -1234,15 +770,7 @@
             )
 
             // FINSHING in LOCKSCREEN should update the current state to LOCKSCREEN.
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                    AOD,
-                    LOCKSCREEN,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN, AOD, LOCKSCREEN), currentStates)
         }
 
     @Test
@@ -1251,13 +779,7 @@
             val currentStates by collectValues(underTest.currentKeyguardState)
 
             // We init the repo with a transition from OFF -> LOCKSCREEN.
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN), currentStates)
 
             sendSteps(
                 TransitionStep(LOCKSCREEN, GONE, 0f, STARTED),
@@ -1273,7 +795,7 @@
                     // Transitioned to GONE
                     GONE,
                 ),
-                currentStates
+                currentStates,
             )
 
             sendSteps(
@@ -1290,12 +812,10 @@
                     // Current state should not be DOZING until the post-cancelation transition is
                     // STARTED
                 ),
-                currentStates
+                currentStates,
             )
 
-            sendSteps(
-                TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED))
 
             assertEquals(
                 listOf(
@@ -1305,7 +825,7 @@
                     // DOZING -> LS STARTED, DOZING is now the current state.
                     DOZING,
                 ),
-                currentStates
+                currentStates,
             )
 
             sendSteps(
@@ -1313,19 +833,9 @@
                 TransitionStep(DOZING, LOCKSCREEN, 0.6f, CANCELED),
             )
 
-            assertEquals(
-                listOf(
-                    OFF,
-                    LOCKSCREEN,
-                    GONE,
-                    DOZING,
-                ),
-                currentStates
-            )
+            assertEquals(listOf(OFF, LOCKSCREEN, GONE, DOZING), currentStates)
 
-            sendSteps(
-                TransitionStep(LOCKSCREEN, GONE, 0f, STARTED),
-            )
+            sendSteps(TransitionStep(LOCKSCREEN, GONE, 0f, STARTED))
 
             assertEquals(
                 listOf(
@@ -1336,7 +846,7 @@
                     // LS -> GONE STARTED, LS is now the current state.
                     LOCKSCREEN,
                 ),
-                currentStates
+                currentStates,
             )
 
             sendSteps(
@@ -1354,7 +864,7 @@
                     // FINISHED in GONE, GONE is now the current state.
                     GONE,
                 ),
-                currentStates
+                currentStates,
             )
         }
 
@@ -1504,6 +1014,126 @@
             }
         }
 
+    @Test
+    @EnableSceneContainer
+    fun simulateTransitionStepsForSceneTransitions_emits_correct_values_for_wildcard_from_edge() =
+        testScope.runTest {
+            val sceneToSceneSteps by
+                collectValues(underTest.transition(Edge.create(from = Scenes.Gone)))
+            val progress = MutableSharedFlow<Float>()
+
+            kosmos.setSceneTransition(
+                Transition(Scenes.Gone, Scenes.Lockscreen, progress = progress)
+            )
+
+            progress.emit(0.2f)
+            runCurrent()
+            progress.emit(0.6f)
+            runCurrent()
+
+            kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer, progress = progress))
+
+            progress.emit(0.1f)
+            runCurrent()
+
+            kosmos.setSceneTransition(
+                Transition(Scenes.Bouncer, Scenes.Lockscreen, progress = progress)
+            )
+
+            progress.emit(0.3f)
+            runCurrent()
+
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+            assertEquals(
+                listOf(
+                    TransitionStep(UNDEFINED, UNDEFINED, 0f, STARTED),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0.2f, RUNNING),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0.6f, RUNNING),
+                    TransitionStep(UNDEFINED, UNDEFINED, 1f, FINISHED),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0f, STARTED),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0.1f, RUNNING),
+                    TransitionStep(UNDEFINED, UNDEFINED, 1f, FINISHED),
+                ),
+                sceneToSceneSteps,
+            )
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun simulateTransitionStepsForSceneTransitions_emits_correct_values_for_wildcard_to_edge() =
+        testScope.runTest {
+            val sceneToSceneSteps by
+                collectValues(underTest.transition(Edge.create(to = Scenes.Gone)))
+            val progress = MutableSharedFlow<Float>()
+
+            kosmos.setSceneTransition(
+                Transition(Scenes.Gone, Scenes.Lockscreen, progress = progress)
+            )
+
+            progress.emit(0.2f)
+            runCurrent()
+
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+            kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer, progress = progress))
+
+            progress.emit(0.1f)
+            runCurrent()
+
+            kosmos.setSceneTransition(Transition(Scenes.Bouncer, Scenes.Gone, progress = progress))
+
+            progress.emit(0.3f)
+            runCurrent()
+
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+            assertEquals(
+                listOf(
+                    TransitionStep(UNDEFINED, UNDEFINED, 0f, STARTED),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0.3f, RUNNING),
+                    TransitionStep(UNDEFINED, UNDEFINED, 1f, FINISHED),
+                ),
+                sceneToSceneSteps,
+            )
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun flatMapLatestWithFinished_emission_of_previous_progress_flow_is_not_interleaving() =
+        testScope.runTest {
+            val sceneToSceneSteps by
+                collectValues(underTest.transition(Edge.create(from = Scenes.Gone)))
+            val progress1 = MutableSharedFlow<Float>()
+            val progress2 = MutableSharedFlow<Float>()
+
+            kosmos.setSceneTransition(
+                Transition(Scenes.Gone, Scenes.Lockscreen, progress = progress1)
+            )
+
+            progress1.emit(0.1f)
+            runCurrent()
+
+            kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer, progress = progress2))
+
+            progress2.emit(0.3f)
+            runCurrent()
+
+            progress1.emit(0.2f)
+            runCurrent()
+
+            assertEquals(
+                listOf(
+                    TransitionStep(UNDEFINED, UNDEFINED, 0f, STARTED),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0.1f, RUNNING),
+                    TransitionStep(UNDEFINED, UNDEFINED, 1f, FINISHED),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0f, STARTED),
+                    TransitionStep(UNDEFINED, UNDEFINED, 0.3f, RUNNING),
+                ),
+                sceneToSceneSteps,
+            )
+        }
+
     private suspend fun sendSteps(vararg steps: TransitionStep) {
         steps.forEach {
             repository.sendTransitionStep(it)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 8f3d549..f4d2ea0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -118,9 +118,6 @@
     private val fromPrimaryBouncerTransitionInteractor by lazy {
         kosmos.fromPrimaryBouncerTransitionInteractor
     }
-    private val fromDreamingLockscreenHostedTransitionInteractor by lazy {
-        kosmos.fromDreamingLockscreenHostedTransitionInteractor
-    }
     private val fromGlanceableHubTransitionInteractor by lazy {
         kosmos.fromGlanceableHubTransitionInteractor
     }
@@ -161,7 +158,6 @@
         fromLockscreenTransitionInteractor.start()
         fromPrimaryBouncerTransitionInteractor.start()
         fromDreamingTransitionInteractor.start()
-        fromDreamingLockscreenHostedTransitionInteractor.start()
         fromAodTransitionInteractor.start()
         fromGoneTransitionInteractor.start()
         fromDozingTransitionInteractor.start()
@@ -275,36 +271,6 @@
         }
 
     @Test
-    @BrokenWithSceneContainer(339465026)
-    fun lockscreenToDreamingLockscreenHosted() =
-        testScope.runTest {
-            // GIVEN a device that is not dreaming or dozing
-            keyguardRepository.setDreamingWithOverlay(false)
-            keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
-            )
-            advanceTimeBy(600L)
-
-            // GIVEN a prior transition has run to LOCKSCREEN
-            runTransitionAndSetWakefulness(KeyguardState.GONE, KeyguardState.LOCKSCREEN)
-
-            // WHEN the device begins to dream and the dream is lockscreen hosted
-            keyguardRepository.setDreamingWithOverlay(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            advanceTimeBy(100L)
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-                    from = KeyguardState.LOCKSCREEN,
-                    ownerName = "FromLockscreenTransitionInteractor",
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
     fun lockscreenToDozing() =
         testScope.runTest {
             // GIVEN a device with AOD not available
@@ -355,157 +321,6 @@
         }
 
     @Test
-    @DisableSceneContainer
-    fun dreamingLockscreenHostedToLockscreen() =
-        testScope.runTest {
-            // GIVEN a device dreaming with the lockscreen hosted dream and not dozing
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
-            )
-            runCurrent()
-
-            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
-            runTransitionAndSetWakefulness(
-                KeyguardState.GONE,
-                KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-            )
-
-            // WHEN the lockscreen hosted dream stops
-            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
-            advanceTimeBy(100L)
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.LOCKSCREEN,
-                    from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-                    ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    fun dreamingLockscreenHostedToGone() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
-            runTransitionAndSetWakefulness(
-                KeyguardState.GONE,
-                KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-            )
-
-            // WHEN biometrics succeeds with wake and unlock from dream mode
-            keyguardRepository.setBiometricUnlockState(
-                BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM
-            )
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.GONE,
-                    from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-                    ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    fun dreamingLockscreenHostedToPrimaryBouncer() =
-        testScope.runTest {
-            // GIVEN a device dreaming with lockscreen hosted dream and not dozing
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            runCurrent()
-
-            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
-            runTransitionAndSetWakefulness(
-                KeyguardState.GONE,
-                KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-            )
-
-            // WHEN the primary bouncer is set to show
-            bouncerRepository.setPrimaryShow(true)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.PRIMARY_BOUNCER,
-                    from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-                    ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    fun dreamingLockscreenHostedToDozing() =
-        testScope.runTest {
-            // GIVEN a device is dreaming with lockscreen hosted dream
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            runCurrent()
-
-            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
-            runTransitionAndSetWakefulness(
-                KeyguardState.GONE,
-                KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-            )
-
-            // WHEN the device begins to sleep
-            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
-            keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(from = DozeStateModel.INITIALIZED, to = DozeStateModel.DOZE)
-            )
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.DOZING,
-                    from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-                    ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
-    fun dreamingLockscreenHostedToOccluded() =
-        testScope.runTest {
-            // GIVEN device is dreaming with lockscreen hosted dream and not occluded
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            runCurrent()
-
-            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
-            runTransitionAndSetWakefulness(
-                KeyguardState.GONE,
-                KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-            )
-
-            // WHEN the keyguard is occluded and the lockscreen hosted dream stops
-            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
-            keyguardRepository.setKeyguardOccluded(true)
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.OCCLUDED,
-                    from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-                    ownerName = "FromDreamingLockscreenHostedTransitionInteractor",
-                    animatorAssertion = { it.isNotNull() },
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
     fun dozingToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index b6ec6a6..e24bb04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -19,6 +19,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository
 import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION
@@ -31,7 +32,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.data.repository.fakePowerRepository
-import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.statusbar.LightRevealEffect
@@ -98,7 +98,7 @@
             fakeKeyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
                     to = KeyguardState.LOCKSCREEN,
-                    transitionState = TransitionState.RUNNING
+                    transitionState = TransitionState.RUNNING,
                 )
             )
             runCurrent()
@@ -110,7 +110,7 @@
             fakeKeyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
                     to = KeyguardState.LOCKSCREEN,
-                    transitionState = TransitionState.STARTED
+                    transitionState = TransitionState.STARTED,
                 )
             )
             runCurrent()
@@ -147,19 +147,29 @@
 
             assertThat(fakeLightRevealScrimRepository.revealAnimatorRequests.last())
                 .isEqualTo(
-                    RevealAnimatorRequest(
-                        reveal = false,
-                        duration = DEFAULT_REVEAL_DURATION
-                    )
+                    RevealAnimatorRequest(reveal = false, duration = DEFAULT_REVEAL_DURATION)
                 )
         }
 
+    @Test
+    fun supportsAmbientMode() =
+        kosmos.testScope.runTest {
+            val maxAlpha by collectLastValue(underTest.maxAlpha)
+            assertThat(maxAlpha).isEqualTo(1f)
+
+            underTest.setWallpaperSupportsAmbientMode(true)
+            assertThat(maxAlpha).isLessThan(1f)
+
+            underTest.setWallpaperSupportsAmbientMode(false)
+            assertThat(maxAlpha).isEqualTo(1f)
+        }
+
     private fun updateWakefulness(goToSleepReason: WakeSleepReason) {
         fakePowerRepository.updateWakefulness(
             rawState = WakefulnessState.STARTING_TO_SLEEP,
             lastWakeReason = WakeSleepReason.POWER_BUTTON,
             lastSleepReason = goToSleepReason,
-            powerButtonLaunchGestureTriggered = false
+            powerButtonLaunchGestureTriggered = false,
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
index c4eabd8..3800608 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard.ui.binder
 
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import android.view.View
 import android.view.layoutInflater
@@ -24,7 +23,6 @@
 import android.view.windowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -63,7 +61,7 @@
                 kosmos.mockedLayoutInflater.inflate(
                     eq(R.layout.alternate_bouncer),
                     isNull(),
-                    anyBoolean()
+                    anyBoolean(),
                 )
             )
             .thenReturn(mockedAltBouncerView)
@@ -71,7 +69,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun addViewToWindowManager() {
         testScope.runTest {
             kosmos.givenCanShowAlternateBouncer()
@@ -85,7 +82,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun viewRemovedImmediatelyIfAlreadyAttachedToWindow() {
         testScope.runTest {
             kosmos.givenCanShowAlternateBouncer()
@@ -107,7 +103,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     fun viewNotRemovedUntilAttachedToWindow() {
         testScope.runTest {
             kosmos.givenCanShowAlternateBouncer()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index 1c99eff..d94c97a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -28,12 +28,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
-import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
 import com.android.systemui.util.mockito.any
@@ -135,7 +135,7 @@
         assertThat(smartspaceConstraints.layout.topToBottom).isEqualTo(dateView.id)
 
         val dateConstraints = constraintSet.getConstraint(dateView.id)
-        assertThat(dateConstraints.layout.topToBottom).isEqualTo(R.id.lockscreen_clock_view)
+        assertThat(dateConstraints.layout.topToBottom).isEqualTo(customR.id.lockscreen_clock_view)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index c1bd378..5d95480 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -19,7 +19,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
@@ -34,7 +33,6 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @ExperimentalCoroutinesApi
 @RunWith(AndroidJUnit4::class)
@@ -49,7 +47,6 @@
     @Test
     fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
             fingerprintPropertyRepository.supportsUdfps()
@@ -64,28 +61,7 @@
             assertThat(alternateBouncerWindowRequired).isTrue()
 
             transitionRepository.sendTransitionSteps(
-                listOf(
-                    stepFromAlternateBouncer(1.0f, TransitionState.FINISHED),
-                ),
-                testScope,
-            )
-            assertThat(alternateBouncerWindowRequired).isFalse()
-        }
-
-    @Test
-    fun deviceEntryUdfpsFlagDisabled_alternateBouncerWindowRequiredFalse() =
-        testScope.runTest {
-            mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-            val alternateBouncerWindowRequired by
-                collectLastValue(underTest.alternateBouncerWindowRequired)
-            fingerprintPropertyRepository.supportsUdfps()
-            transitionRepository.sendTransitionSteps(
-                listOf(
-                    stepFromAlternateBouncer(0f, TransitionState.STARTED),
-                    stepFromAlternateBouncer(.4f),
-                    stepFromAlternateBouncer(.6f),
-                    stepFromAlternateBouncer(1f),
-                ),
+                listOf(stepFromAlternateBouncer(1.0f, TransitionState.FINISHED)),
                 testScope,
             )
             assertThat(alternateBouncerWindowRequired).isFalse()
@@ -94,7 +70,6 @@
     @Test
     fun lockscreenTransition_alternateBouncerWindowRequiredFalse() =
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
             fingerprintPropertyRepository.supportsUdfps()
@@ -113,7 +88,6 @@
     @Test
     fun rearFps_alternateBouncerWindowRequiredFalse() =
         testScope.runTest {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
             val alternateBouncerWindowRequired by
                 collectLastValue(underTest.alternateBouncerWindowRequired)
             fingerprintPropertyRepository.supportsRearFps()
@@ -131,7 +105,7 @@
 
     private fun stepFromAlternateBouncer(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return step(
             from = KeyguardState.ALTERNATE_BOUNCER,
@@ -143,7 +117,7 @@
 
     private fun stepFromDozingToLockscreen(
         value: Float,
-        state: TransitionState = TransitionState.RUNNING
+        state: TransitionState = TransitionState.RUNNING,
     ): TransitionStep {
         return step(
             from = KeyguardState.DOZING,
@@ -157,14 +131,14 @@
         from: KeyguardState,
         to: KeyguardState,
         value: Float,
-        transitionState: TransitionState
+        transitionState: TransitionState,
     ): TransitionStep {
         return TransitionStep(
             from = from,
             to = to,
             value = value,
             transitionState = transitionState,
-            ownerName = "AlternateBouncerViewModelTest"
+            ownerName = "AlternateBouncerViewModelTest",
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/HydratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/lifecycle/HydratorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarButtonTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TileLayoutTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/TileLayoutTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
index 4bbdfa4..d96e664 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/AbstractQSFragmentComposeViewModelTest.kt
@@ -21,10 +21,11 @@
 import androidx.lifecycle.testing.TestLifecycleOwner
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestResult
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.resetMain
@@ -62,7 +63,7 @@
     ): TestResult {
         return runTest {
             lifecycleOwner.setCurrentState(Lifecycle.State.RESUMED)
-            lifecycleOwner.lifecycleScope.launch { underTest.activate() }
+            underTest.activateIn(kosmos.testScope)
             block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) }
         }
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt
index da16640..d16da1c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelForceQSTest.kt
@@ -18,12 +18,13 @@
 
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
-import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.sysuiStatusBarStateController
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import org.junit.Test
 import org.junit.runner.RunWith
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
@@ -32,6 +33,7 @@
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 @RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
 class QSFragmentComposeViewModelForceQSTest(private val testData: TestData) :
     AbstractQSFragmentComposeViewModelTest() {
 
@@ -39,18 +41,18 @@
     fun forceQs_orRealExpansion() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val expansionState by collectLastValue(underTest.expansionState)
-
                 with(testData) {
                     sysuiStatusBarStateController.setState(statusBarState)
-                    underTest.isQSExpanded = expanded
+                    underTest.isQsExpanded = expanded
                     underTest.isStackScrollerOverscrolling = stackScrollerOverScrolling
                     fakeDeviceEntryRepository.setBypassEnabled(bypassEnabled)
                     underTest.isTransitioningToFullShade = transitioningToFullShade
                     underTest.isInSplitShade = inSplitShade
 
-                    underTest.qsExpansionValue = EXPANSION
-                    assertThat(expansionState!!.progress)
+                    underTest.setQsExpansionValue(EXPANSION)
+
+                    runCurrent()
+                    assertThat(underTest.expansionState.progress)
                         .isEqualTo(if (expectedForceQS) 1f else EXPANSION)
                 }
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index c19e4b8..3b00f86 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -19,6 +19,7 @@
 import android.app.StatusBarManager
 import android.content.testableContext
 import android.testing.TestableLooper.RunWithLooper
+import androidx.compose.runtime.snapshots.Snapshot
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
@@ -33,6 +34,7 @@
 import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
 import com.android.systemui.statusbar.sysuiStatusBarStateController
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -40,22 +42,21 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
 class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() {
 
     @Test
     fun qsExpansionValueChanges_correctExpansionState() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val expansionState by collectLastValue(underTest.expansionState)
+                underTest.setQsExpansionValue(0f)
+                assertThat(underTest.expansionState.progress).isEqualTo(0f)
 
-                underTest.qsExpansionValue = 0f
-                assertThat(expansionState!!.progress).isEqualTo(0f)
+                underTest.setQsExpansionValue(0.3f)
+                assertThat(underTest.expansionState.progress).isEqualTo(0.3f)
 
-                underTest.qsExpansionValue = 0.3f
-                assertThat(expansionState!!.progress).isEqualTo(0.3f)
-
-                underTest.qsExpansionValue = 1f
-                assertThat(expansionState!!.progress).isEqualTo(1f)
+                underTest.setQsExpansionValue(1f)
+                assertThat(underTest.expansionState.progress).isEqualTo(1f)
             }
         }
 
@@ -63,13 +64,11 @@
     fun qsExpansionValueChanges_clamped() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val expansionState by collectLastValue(underTest.expansionState)
+                underTest.setQsExpansionValue(-1f)
+                assertThat(underTest.expansionState.progress).isEqualTo(0f)
 
-                underTest.qsExpansionValue = -1f
-                assertThat(expansionState!!.progress).isEqualTo(0f)
-
-                underTest.qsExpansionValue = 2f
-                assertThat(expansionState!!.progress).isEqualTo(1f)
+                underTest.setQsExpansionValue(2f)
+                assertThat(underTest.expansionState.progress).isEqualTo(1f)
             }
         }
 
@@ -77,15 +76,13 @@
     fun qqsHeaderHeight_largeScreenHeader_0() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val qqsHeaderHeight by collectLastValue(underTest.qqsHeaderHeight)
-
                 testableContext.orCreateTestableResources.addOverride(
                     R.bool.config_use_large_screen_shade_header,
                     true,
                 )
                 fakeConfigurationRepository.onConfigurationChange()
 
-                assertThat(qqsHeaderHeight).isEqualTo(0)
+                assertThat(underTest.qqsHeaderHeight).isEqualTo(0)
             }
         }
 
@@ -93,15 +90,13 @@
     fun qqsHeaderHeight_noLargeScreenHeader_providedByHelper() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val qqsHeaderHeight by collectLastValue(underTest.qqsHeaderHeight)
-
                 testableContext.orCreateTestableResources.addOverride(
                     R.bool.config_use_large_screen_shade_header,
                     false,
                 )
                 fakeConfigurationRepository.onConfigurationChange()
 
-                assertThat(qqsHeaderHeight)
+                assertThat(underTest.qqsHeaderHeight)
                     .isEqualTo(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
             }
         }
@@ -120,17 +115,17 @@
     fun statusBarState_followsController() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val statusBarState by collectLastValue(underTest.statusBarState)
-                runCurrent()
-
                 sysuiStatusBarStateController.setState(StatusBarState.SHADE)
-                assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+                runCurrent()
+                assertThat(underTest.statusBarState).isEqualTo(StatusBarState.SHADE)
 
                 sysuiStatusBarStateController.setState(StatusBarState.KEYGUARD)
-                assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+                runCurrent()
+                assertThat(underTest.statusBarState).isEqualTo(StatusBarState.KEYGUARD)
 
                 sysuiStatusBarStateController.setState(StatusBarState.SHADE_LOCKED)
-                assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
+                runCurrent()
+                assertThat(underTest.statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
             }
         }
 
@@ -138,17 +133,18 @@
     fun statusBarState_changesEarlyIfUpcomingStateIsKeyguard() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val statusBarState by collectLastValue(underTest.statusBarState)
-
                 sysuiStatusBarStateController.setState(StatusBarState.SHADE)
                 sysuiStatusBarStateController.setUpcomingState(StatusBarState.SHADE_LOCKED)
-                assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+                runCurrent()
+                assertThat(underTest.statusBarState).isEqualTo(StatusBarState.SHADE)
 
                 sysuiStatusBarStateController.setUpcomingState(StatusBarState.KEYGUARD)
-                assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+                runCurrent()
+                assertThat(underTest.statusBarState).isEqualTo(StatusBarState.KEYGUARD)
 
                 sysuiStatusBarStateController.setUpcomingState(StatusBarState.SHADE)
-                assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+                runCurrent()
+                assertThat(underTest.statusBarState).isEqualTo(StatusBarState.KEYGUARD)
             }
         }
 
@@ -156,16 +152,16 @@
     fun qsEnabled_followsRepository() =
         with(kosmos) {
             testScope.testWithinLifecycle {
-                val qsEnabled by collectLastValue(underTest.qsEnabled)
-
                 fakeDisableFlagsRepository.disableFlags.value =
                     DisableFlagsModel(disable2 = QS_DISABLE_FLAG)
+                runCurrent()
 
-                assertThat(qsEnabled).isFalse()
+                assertThat(underTest.isQsEnabled).isFalse()
 
                 fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel()
+                runCurrent()
 
-                assertThat(qsEnabled).isTrue()
+                assertThat(underTest.isQsEnabled).isTrue()
             }
         }
 
@@ -175,13 +171,16 @@
             testScope.testWithinLifecycle {
                 val squishiness by collectLastValue(tileSquishinessInteractor.squishiness)
 
-                underTest.squishinessFractionValue = 0.3f
+                underTest.squishinessFraction = 0.3f
+                Snapshot.sendApplyNotifications()
                 assertThat(squishiness).isWithin(epsilon).of(0.3f.constrainSquishiness())
 
-                underTest.squishinessFractionValue = 0f
+                underTest.squishinessFraction = 0f
+                Snapshot.sendApplyNotifications()
                 assertThat(squishiness).isWithin(epsilon).of(0f.constrainSquishiness())
 
-                underTest.squishinessFractionValue = 1f
+                underTest.squishinessFraction = 1f
+                Snapshot.sendApplyNotifications()
                 assertThat(squishiness).isWithin(epsilon).of(1f.constrainSquishiness())
             }
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
index 14d6094..e5bdc2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
@@ -54,7 +54,7 @@
     private fun setRowsInConfig(rows: Int) =
         with(kosmos) {
             testCase.context.orCreateTestableResources.addOverride(
-                R.integer.quick_settings_max_rows,
+                R.integer.quick_settings_paginated_grid_num_rows,
                 rows,
             )
             fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
new file mode 100644
index 0000000..ec0773f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QSColumnsRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private lateinit var underTest: QSColumnsRepository
+
+    @Before
+    fun setUp() {
+        underTest = with(kosmos) { qsColumnsRepository }
+    }
+
+    @Test
+    fun configChanges_triggerColumnsUpdate() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.columns)
+
+                setColumnsInConfig(4)
+                assertThat(latest).isEqualTo(4)
+
+                setColumnsInConfig(8)
+                assertThat(latest).isEqualTo(8)
+            }
+        }
+
+    @Test
+    fun withDualShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.dualShadeColumns)
+
+                assertThat(latest).isEqualTo(4)
+            }
+        }
+
+    @Test
+    fun withSplitShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.splitShadeColumns)
+                fakeShadeRepository.setShadeLayoutWide(true)
+
+                assertThat(latest).isEqualTo(4)
+            }
+        }
+
+    private fun setColumnsInConfig(
+        columns: Int,
+        id: Int = R.integer.quick_settings_infinite_grid_num_columns,
+    ) =
+        with(kosmos) {
+            testCase.context.orCreateTestableResources.addOverride(id, columns)
+            fakeConfigurationRepository.onConfigurationChange()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
index ae6f576..cda3d48 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
@@ -54,7 +54,7 @@
     private fun setRowsInConfig(rows: Int) =
         with(kosmos) {
             testCase.context.orCreateTestableResources.addOverride(
-                R.integer.quick_qs_panel_max_rows,
+                R.integer.quick_qs_paginated_grid_num_rows,
                 rows,
             )
             fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt
new file mode 100644
index 0000000..35f7504
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import android.content.res.mainResources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
+import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
+import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QSColumnsInteractorTest : SysuiTestCase() {
+    private val kosmos =
+        testKosmos().apply {
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_settings_infinite_grid_num_columns,
+                1,
+            )
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_settings_dual_shade_num_columns,
+                2,
+            )
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_settings_split_shade_num_columns,
+                3,
+            )
+            qsColumnsRepository = QSColumnsRepository(mainResources, configurationRepository)
+        }
+    private lateinit var underTest: QSColumnsInteractor
+
+    @Before
+    fun setUp() {
+        underTest = with(kosmos) { qsColumnsInteractor }
+    }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun withSingleShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.columns)
+
+                assertThat(latest).isEqualTo(1)
+            }
+        }
+
+    @Test
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun withDualShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.columns)
+
+                assertThat(latest).isEqualTo(2)
+            }
+        }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun withSplitShade_returnsCorrectValue() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.columns)
+
+                fakeShadeRepository.setShadeLayoutWide(true)
+
+                assertThat(latest).isEqualTo(3)
+            }
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
index ef85302..2c894f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
@@ -58,6 +58,11 @@
             qsPreferencesInteractor.setLargeTilesSpecs(
                 tiles.filter { it.spec.startsWith(PREFIX_LARGE) }.toSet()
             )
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_settings_infinite_grid_num_columns,
+                4,
+            )
+            fakeConfigurationRepository.onConfigurationChange()
         }
 
     private val underTest = kosmos.quickQuickSettingsViewModel
@@ -146,7 +151,7 @@
 
     private fun Kosmos.setRows(rows: Int) {
         testCase.context.orCreateTestableResources.addOverride(
-            R.integer.quick_qs_panel_max_rows,
+            R.integer.quick_qs_paginated_grid_num_rows,
             rows,
         )
         fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 7798f46..b23262a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -34,9 +34,9 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.service.quicksettings.Tile;
-import android.testing.UiThreadTest;
 import android.widget.ImageView;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
new file mode 100644
index 0000000..cdf6bda
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles.impl.hearingdevices.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileMapperTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
+    private val mapper by lazy {
+        HearingDevicesTileMapper(
+            context.orCreateTestableResources
+                .apply { addOverride(R.drawable.qs_hearing_devices_icon, TestStubDrawable()) }
+                .resources,
+            context.theme,
+        )
+    }
+
+    @Test
+    fun map_anyActiveHearingDevice_anyPairedHearingDevice_activeState() {
+        val tileState: QSTileState =
+            mapper.map(
+                qsTileConfig,
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = true,
+                    isAnyPairedHearingDevice = true,
+                ),
+            )
+        val expectedState =
+            createHearingDevicesTileState(
+                QSTileState.ActivationState.ACTIVE,
+                context.getString(R.string.quick_settings_hearing_devices_connected),
+            )
+        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun map_noActiveHearingDevice_anyPairedHearingDevice_inactiveState() {
+        val tileState: QSTileState =
+            mapper.map(
+                qsTileConfig,
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = false,
+                    isAnyPairedHearingDevice = true,
+                ),
+            )
+        val expectedState =
+            createHearingDevicesTileState(
+                QSTileState.ActivationState.INACTIVE,
+                context.getString(R.string.quick_settings_hearing_devices_disconnected),
+            )
+        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun map_noActiveHearingDevice_noPairedHearingDevice_inactiveState() {
+        val tileState: QSTileState =
+            mapper.map(
+                qsTileConfig,
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = false,
+                    isAnyPairedHearingDevice = false,
+                ),
+            )
+        val expectedState =
+            createHearingDevicesTileState(QSTileState.ActivationState.INACTIVE, secondaryLabel = "")
+        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+    }
+
+    private fun createHearingDevicesTileState(
+        activationState: QSTileState.ActivationState,
+        secondaryLabel: String,
+    ): QSTileState {
+        val label = context.getString(R.string.quick_settings_hearing_devices_label)
+        val iconRes = R.drawable.qs_hearing_devices_icon
+        return QSTileState(
+            { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+            iconRes,
+            label,
+            activationState,
+            secondaryLabel,
+            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+            label,
+            null,
+            QSTileState.SideViewIcon.Chevron,
+            QSTileState.EnabledState.ENABLED,
+            Switch::class.qualifiedName,
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
new file mode 100644
index 0000000..1dfa2cd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val testUser = UserHandle.of(1)
+
+    private val controller = kosmos.fakeBluetoothController
+    private lateinit var underTest: HearingDevicesTileDataInteractor
+
+    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var checker: HearingDevicesChecker
+
+    @Before
+    fun setup() {
+        underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
+    }
+
+    @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+    @Test
+    fun availability_flagEnabled_returnTrue() =
+        testScope.runTest {
+            val availability by collectLastValue(underTest.availability(testUser))
+
+            assertThat(availability).isTrue()
+        }
+
+    @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+    @Test
+    fun availability_flagDisabled_returnFalse() =
+        testScope.runTest {
+            val availability by collectLastValue(underTest.availability(testUser))
+
+            assertThat(availability).isFalse()
+        }
+
+    @Test
+    fun tileData_bluetoothStateChanged_dataMatchesChecker() =
+        testScope.runTest {
+            val flowValues: List<HearingDevicesTileModel> by
+                collectValues(
+                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.isBluetoothEnabled = false
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.isBluetoothEnabled = true
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(2)
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+            controller.isBluetoothEnabled = true
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(3)
+
+            assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+                .containsExactly(false, true, true)
+                .inOrder()
+            assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+                .containsExactly(false, false, true)
+                .inOrder()
+        }
+
+    @Test
+    fun tileData_bluetoothDeviceChanged_dataMatchesChecker() =
+        testScope.runTest {
+            val flowValues: List<HearingDevicesTileModel> by
+                collectValues(
+                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.onBluetoothDevicesChanged()
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+            controller.onBluetoothDevicesChanged()
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(2)
+
+            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+            whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+            controller.onBluetoothDevicesChanged()
+            runCurrent()
+            assertThat(flowValues.size).isEqualTo(3)
+
+            assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+                .containsExactly(false, true, true)
+                .inOrder()
+            assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+                .containsExactly(false, false, true)
+                .inOrder()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..00ee1c3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.platform.test.annotations.EnabledOnRavenwood
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.verify
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val inputHandler = FakeQSTileIntentUserInputHandler()
+
+    private lateinit var underTest: HearingDevicesTileUserActionInteractor
+
+    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var dialogManager: HearingDevicesDialogManager
+
+    @Before
+    fun setUp() {
+        underTest =
+            HearingDevicesTileUserActionInteractor(
+                testScope.coroutineContext,
+                inputHandler,
+                dialogManager,
+            )
+    }
+
+    @Test
+    fun handleClick_launchDialog() =
+        testScope.runTest {
+            val input =
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = true,
+                    isAnyPairedHearingDevice = true,
+                )
+
+            underTest.handleInput(QSTileInputTestKtx.click(input))
+
+            verify(dialogManager).showDialog(anyOrNull(), eq(LAUNCH_SOURCE_QS_TILE))
+        }
+
+    @Test
+    fun handleLongClick_launchSettings() =
+        testScope.runTest {
+            val input =
+                HearingDevicesTileModel(
+                    isAnyActiveHearingDevice = true,
+                    isAnyPairedHearingDevice = true,
+                )
+
+            underTest.handleInput(QSTileInputTestKtx.longClick(input))
+
+            QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+                assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS)
+            }
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 04ca38f..3e40c5c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -22,6 +22,9 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.defaultDeviceState
+import com.android.systemui.deviceStateManager
+import com.android.systemui.foldedDeviceStateList
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
@@ -30,11 +33,11 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.devicePostureController
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -42,13 +45,17 @@
     private val kosmos = Kosmos()
     private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig
     private val devicePostureController = kosmos.devicePostureController
+    private val deviceStateManager = kosmos.deviceStateManager
 
     private lateinit var mapper: RotationLockTileMapper
 
     @Before
     fun setup() {
+        deviceStateManager
         whenever(devicePostureController.devicePosture)
             .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED)
+        whenever(deviceStateManager.supportedDeviceStates)
+            .thenReturn(listOf(kosmos.defaultDeviceState))
 
         mapper =
             RotationLockTileMapper(
@@ -64,7 +71,8 @@
                     }
                     .resources,
                 context.theme,
-                devicePostureController
+                devicePostureController,
+                deviceStateManager
             )
     }
 
@@ -162,6 +170,7 @@
                 intArrayOf(1, 2, 3)
             )
         }
+        whenever(deviceStateManager.supportedDeviceStates).thenReturn(kosmos.foldedDeviceStateList)
     }
 
     private fun createRotationLockTileState(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 3f550ca..0d5ddae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -18,9 +18,12 @@
 
 import static com.android.systemui.screenrecord.RecordingService.GROUP_KEY_ERROR_SAVING;
 import static com.android.systemui.screenrecord.RecordingService.GROUP_KEY_SAVED;
+import static com.android.systemui.screenrecord.RecordingService.NOTIF_GROUP_ID_ERROR_SAVING;
+import static com.android.systemui.screenrecord.RecordingService.NOTIF_GROUP_ID_SAVED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -235,7 +238,9 @@
 
         // Processing notification
         ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
-        verify(mNotificationManager).notifyAsUser(any(), anyInt(), notifCaptor.capture(), any());
+        ArgumentCaptor<Integer> notifIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mNotificationManager)
+                .notifyAsUser(any(), notifIdCaptor.capture(), notifCaptor.capture(), any());
         assertEquals(GROUP_KEY_SAVED, notifCaptor.getValue().getGroup());
 
         reset(mNotificationManager);
@@ -243,7 +248,7 @@
         mRunnableCaptor.getValue().run();
 
         verify(mNotificationManager, times(2))
-                .notifyAsUser(any(), anyInt(), notifCaptor.capture(), any());
+                .notifyAsUser(any(), notifIdCaptor.capture(), notifCaptor.capture(), any());
         // Saved notification
         Notification saveNotification = notifCaptor.getAllValues().get(0);
         assertFalse(saveNotification.isGroupSummary());
@@ -252,6 +257,10 @@
         Notification groupSummaryNotification = notifCaptor.getAllValues().get(1);
         assertTrue(groupSummaryNotification.isGroupSummary());
         assertEquals(GROUP_KEY_SAVED, groupSummaryNotification.getGroup());
+
+        // Verify the group notification ID and the individual notification ID are different
+        assertNotEquals(NOTIF_GROUP_ID_SAVED, (int) notifIdCaptor.getAllValues().get(0));
+        assertEquals(NOTIF_GROUP_ID_SAVED, (int) notifIdCaptor.getAllValues().get(1));
     }
 
     @Test
@@ -264,9 +273,12 @@
 
         verify(mRecordingService).createErrorSavingNotification(any());
         ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
-        verify(mNotificationManager).notifyAsUser(any(), anyInt(), notifCaptor.capture(), any());
+        ArgumentCaptor<Integer> notifIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mNotificationManager)
+                .notifyAsUser(any(), notifIdCaptor.capture(), notifCaptor.capture(), any());
         assertTrue(notifCaptor.getValue().isGroupSummary());
         assertEquals(GROUP_KEY_ERROR_SAVING, notifCaptor.getValue().getGroup());
+        assertEquals(NOTIF_GROUP_ID_ERROR_SAVING, (int) notifIdCaptor.getValue());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt
index 9331c8d..0bbf47c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt
@@ -16,13 +16,16 @@
 
 package com.android.systemui.screenrecord.data.model
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel.Starting.Companion.toCountdownSeconds
 import com.google.common.truth.Truth.assertThat
+import org.junit.runner.RunWith
 import kotlin.test.Test
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class ScreenRecordModelTest : SysuiTestCase() {
     @Test
     fun countdownSeconds_millis0_is0() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/AnnouncementResolverTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeImageCapture.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/FakeImageCapture.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeImageCapture.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/FakeImageCapture.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
index 254f1e1..4d71dc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/model/DisplayContentScenarios.kt
@@ -21,8 +21,8 @@
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREE_FORM
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FULL_SCREEN
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.PIP
-import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.SPLIT_BOTTOM
-import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.SPLIT_TOP
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Orientation.HORIZONTAL
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Orientation.VERTICAL
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.emptyRootSplit
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.freeForm
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks.fullScreen
@@ -39,16 +39,14 @@
 
     data class TaskSpec(val taskId: Int, val userId: Int, val name: String)
 
+    val emptyDisplayContent = DisplayContentModel(0, SystemUiState(shadeExpanded = false), listOf())
+
     /** Home screen, with only the launcher visible */
     fun launcherOnly(shadeExpanded: Boolean = false) =
         DisplayContentModel(
             displayId = 0,
             systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
-            rootTasks =
-                listOf(
-                    launcher(visible = true),
-                    emptyRootSplit,
-                )
+            rootTasks = listOf(launcher(visible = true), emptyRootSplit),
         )
 
     /** A Full screen activity for the personal (primary) user, with launcher behind it */
@@ -57,48 +55,72 @@
             displayId = 0,
             systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
             rootTasks =
-                listOf(
-                    fullScreen(spec, visible = true),
-                    launcher(visible = false),
-                    emptyRootSplit,
-                )
+                listOf(fullScreen(spec, visible = true), launcher(visible = false), emptyRootSplit),
         )
 
+    enum class Orientation {
+        HORIZONTAL,
+        VERTICAL,
+    }
+
+    internal fun Rect.splitLeft(margin: Int = 0) = Rect(left, top, centerX() - margin, bottom)
+
+    internal fun Rect.splitRight(margin: Int = 0) = Rect(centerX() + margin, top, right, bottom)
+
+    internal fun Rect.splitTop(margin: Int = 0) = Rect(left, top, right, centerY() - margin)
+
+    internal fun Rect.splitBottom(margin: Int = 0) = Rect(left, centerY() + margin, right, bottom)
+
     fun splitScreenApps(
-        top: TaskSpec,
-        bottom: TaskSpec,
+        displayId: Int = 0,
+        parentBounds: Rect = FULL_SCREEN,
+        taskMargin: Int = 0,
+        orientation: Orientation = VERTICAL,
+        first: TaskSpec,
+        second: TaskSpec,
         focusedTaskId: Int,
+        parentTaskId: Int = 2,
         shadeExpanded: Boolean = false,
     ): DisplayContentModel {
-        val topBounds = SPLIT_TOP
-        val bottomBounds = SPLIT_BOTTOM
+
+        val firstBounds =
+            when (orientation) {
+                VERTICAL -> parentBounds.splitTop(taskMargin)
+                HORIZONTAL -> parentBounds.splitLeft(taskMargin)
+            }
+        val secondBounds =
+            when (orientation) {
+                VERTICAL -> parentBounds.splitBottom(taskMargin)
+                HORIZONTAL -> parentBounds.splitRight(taskMargin)
+            }
+
         return DisplayContentModel(
-            displayId = 0,
+            displayId = displayId,
             systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
             rootTasks =
                 listOf(
                     newRootTaskInfo(
-                        taskId = 2,
+                        taskId = parentTaskId,
                         userId = TestUserIds.PERSONAL,
-                        bounds = FULL_SCREEN,
+                        bounds = parentBounds,
                         topActivity =
                             ComponentName.unflattenFromString(
-                                if (top.taskId == focusedTaskId) top.name else bottom.name
+                                if (first.taskId == focusedTaskId) first.name else second.name
                             ),
                     ) {
                         listOf(
                                 newChildTask(
-                                    taskId = top.taskId,
-                                    bounds = topBounds,
-                                    userId = top.userId,
-                                    name = top.name
+                                    taskId = first.taskId,
+                                    bounds = firstBounds,
+                                    userId = first.userId,
+                                    name = first.name,
                                 ),
                                 newChildTask(
-                                    taskId = bottom.taskId,
-                                    bounds = bottomBounds,
-                                    userId = bottom.userId,
-                                    name = bottom.name
-                                )
+                                    taskId = second.taskId,
+                                    bounds = secondBounds,
+                                    userId = second.userId,
+                                    name = second.name,
+                                ),
                             )
                             // Child tasks are ordered bottom-up in RootTaskInfo.
                             // Sort 'focusedTaskId' last.
@@ -106,7 +128,7 @@
                             .sortedBy { it.id == focusedTaskId }
                     },
                     launcher(visible = false),
-                )
+                ),
         )
     }
 
@@ -124,7 +146,7 @@
                     fullScreen?.also { add(fullScreen(it, visible = true)) }
                     add(launcher(visible = (fullScreen == null)))
                     add(emptyRootSplit)
-                }
+                },
         )
     }
 
@@ -142,7 +164,7 @@
         return DisplayContentModel(
             displayId = 0,
             systemUiState = SystemUiState(shadeExpanded = shadeExpanded),
-            rootTasks = freeFormTasks + launcher(visible = true) + emptyRootSplit
+            rootTasks = freeFormTasks + launcher(visible = true) + emptyRootSplit,
         )
     }
 
@@ -153,11 +175,18 @@
      * somewhat sensible in terms of logical position (Re: PIP, SPLIT, etc).
      */
     object Bounds {
+        // "Phone" size
         val FULL_SCREEN = Rect(0, 0, 1080, 2400)
         val PIP = Rect(440, 1458, 1038, 1794)
         val SPLIT_TOP = Rect(0, 0, 1080, 1187)
         val SPLIT_BOTTOM = Rect(0, 1213, 1080, 2400)
         val FREE_FORM = Rect(119, 332, 1000, 1367)
+
+        // "Tablet" size
+        val FREEFORM_FULL_SCREEN = Rect(0, 0, 2560, 1600)
+        val FREEFORM_MAXIMIZED = Rect(0, 48, 2560, 1480)
+        val FREEFORM_SPLIT_LEFT = Rect(0, 0, 1270, 1600)
+        val FREEFORM_SPLIT_RIGHT = Rect(1290, 0, 2560, 1600)
     }
 
     /** A collection of task names used in test scenarios */
@@ -177,6 +206,8 @@
             "com.google.android.youtube/" +
                 "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity"
 
+        const val MESSAGES = "com.google.android.apps.messaging/.ui.ConversationListActivity"
+
         /** The NexusLauncher activity */
         const val LAUNCHER =
             "com.google.android.apps.nexuslauncher/" +
@@ -220,7 +251,7 @@
             }
 
         /** NexusLauncher on the default display. Usually below all other visible tasks */
-        fun launcher(visible: Boolean) =
+        fun launcher(visible: Boolean, bounds: Rect = FULL_SCREEN) =
             newRootTaskInfo(
                 taskId = 1,
                 activityType = ActivityType.Home,
@@ -229,43 +260,63 @@
                 topActivity = ComponentName.unflattenFromString(ActivityNames.LAUNCHER),
                 topActivityType = ActivityType.Home,
             ) {
-                listOf(newChildTask(taskId = 1002, name = ActivityNames.LAUNCHER))
+                listOf(newChildTask(taskId = 1002, name = ActivityNames.LAUNCHER, bounds = bounds))
             }
 
         /** A full screen Activity */
-        fun fullScreen(task: TaskSpec, visible: Boolean) =
+        fun fullScreen(task: TaskSpec, visible: Boolean, bounds: Rect = FULL_SCREEN) =
             newRootTaskInfo(
                 taskId = task.taskId,
                 userId = task.userId,
                 visible = visible,
-                bounds = FULL_SCREEN,
+                bounds = bounds,
                 topActivity = ComponentName.unflattenFromString(task.name),
             ) {
-                listOf(newChildTask(taskId = task.taskId, userId = task.userId, name = task.name))
+                listOf(
+                    newChildTask(
+                        taskId = task.taskId,
+                        userId = task.userId,
+                        name = task.name,
+                        bounds = bounds,
+                    )
+                )
             }
 
         /** An activity in Picture-in-Picture mode */
-        fun pictureInPicture(task: TaskSpec) =
+        fun pictureInPicture(task: TaskSpec, bounds: Rect = PIP) =
             newRootTaskInfo(
                 taskId = task.taskId,
                 userId = task.userId,
-                bounds = PIP,
                 windowingMode = WindowingMode.PictureInPicture,
                 topActivity = ComponentName.unflattenFromString(task.name),
             ) {
-                listOf(newChildTask(taskId = task.taskId, userId = userId, name = task.name))
+                listOf(
+                    newChildTask(
+                        taskId = task.taskId,
+                        userId = userId,
+                        name = task.name,
+                        bounds = bounds,
+                    )
+                )
             }
 
         /** An activity in FreeForm mode */
-        fun freeForm(task: TaskSpec) =
+        fun freeForm(task: TaskSpec, bounds: Rect = FREE_FORM) =
             newRootTaskInfo(
                 taskId = task.taskId,
                 userId = task.userId,
-                bounds = FREE_FORM,
+                bounds = bounds,
                 windowingMode = WindowingMode.Freeform,
                 topActivity = ComponentName.unflattenFromString(task.name),
             ) {
-                listOf(newChildTask(taskId = task.taskId, userId = userId, name = task.name))
+                listOf(
+                    newChildTask(
+                        taskId = task.taskId,
+                        userId = userId,
+                        name = task.name,
+                        bounds = bounds,
+                    )
+                )
             }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/data/repository/ProfileTypeRepositoryKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/repository/ProfileTypeRepositoryKosmos.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/data/repository/ProfileTypeRepositoryKosmos.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/data/repository/ProfileTypeRepositoryKosmos.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
index 6c35b23..cedf0c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/NewRootTaskInfo.kt
@@ -69,7 +69,7 @@
     taskId: Int,
     name: String,
     bounds: Rect? = null,
-    userId: Int? = null
+    userId: Int? = null,
 ): ChildTaskModel {
     return ChildTaskModel(taskId, name, bounds ?: this.bounds, userId ?: this.userId)
 }
@@ -83,7 +83,7 @@
     running: Boolean = true,
     activityType: ActivityType = Standard,
     windowingMode: WindowingMode = FullScreen,
-    bounds: Rect? = null,
+    bounds: Rect = Rect(),
     topActivity: ComponentName? = null,
     topActivityType: ActivityType = Standard,
     numActivities: Int? = null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
index 6e57761..b7f565d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.screenshot.policy
 
 import android.content.ComponentName
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.screenshot.data.model.DisplayContentModel
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
@@ -59,7 +59,7 @@
             policy.check(
                 singleFullScreen(
                     spec = TaskSpec(taskId = 1002, name = YOUTUBE, userId = PRIVATE),
-                    shadeExpanded = true
+                    shadeExpanded = true,
                 )
             )
 
@@ -93,8 +93,8 @@
                     CaptureParameters(
                         type = FullScreen(displayId = 0),
                         component = ComponentName.unflattenFromString(YOUTUBE),
-                        owner = UserHandle.of(PRIVATE)
-                    )
+                        owner = UserHandle.of(PRIVATE),
+                    ),
                 )
             )
     }
@@ -110,25 +110,20 @@
                         listOf(
                             fullScreen(
                                 TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
-                                visible = true
+                                visible = true,
                             ),
                             fullScreen(
                                 TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
-                                visible = false
+                                visible = false,
                             ),
                             launcher(visible = false),
                             emptyRootSplit,
-                        )
+                        ),
                 )
             )
 
         assertThat(result)
-            .isEqualTo(
-                NotMatched(
-                    PrivateProfilePolicy.NAME,
-                    PrivateProfilePolicy.NO_VISIBLE_TASKS,
-                )
-            )
+            .isEqualTo(NotMatched(PrivateProfilePolicy.NAME, PrivateProfilePolicy.NO_VISIBLE_TASKS))
     }
 
     @Test
@@ -136,9 +131,9 @@
         val result =
             policy.check(
                 splitScreenApps(
-                    top = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
-                    bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
-                    focusedTaskId = 1003
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+                    focusedTaskId = 1003,
                 )
             )
 
@@ -150,8 +145,8 @@
                     CaptureParameters(
                         type = FullScreen(displayId = 0),
                         component = ComponentName.unflattenFromString(YOUTUBE),
-                        owner = UserHandle.of(PRIVATE)
-                    )
+                        owner = UserHandle.of(PRIVATE),
+                    ),
                 )
             )
     }
@@ -161,9 +156,9 @@
         val result =
             policy.check(
                 splitScreenApps(
-                    top = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
-                    bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
-                    focusedTaskId = 1002
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+                    focusedTaskId = 1002,
                 )
             )
 
@@ -175,8 +170,8 @@
                     CaptureParameters(
                         type = FullScreen(displayId = 0),
                         component = ComponentName.unflattenFromString(FILES),
-                        owner = UserHandle.of(PRIVATE)
-                    )
+                        owner = UserHandle.of(PRIVATE),
+                    ),
                 )
             )
     }
@@ -196,8 +191,8 @@
                     CaptureParameters(
                         type = FullScreen(displayId = 0),
                         component = ComponentName.unflattenFromString(YOUTUBE_PIP),
-                        owner = UserHandle.of(PRIVATE)
-                    )
+                        owner = UserHandle.of(PRIVATE),
+                    ),
                 )
             )
     }
@@ -220,8 +215,8 @@
                     CaptureParameters(
                         type = FullScreen(displayId = 0),
                         component = ComponentName.unflattenFromString(YOUTUBE_PIP),
-                        owner = UserHandle.of(PRIVATE)
-                    )
+                        owner = UserHandle.of(PRIVATE),
+                    ),
                 )
             )
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt
new file mode 100644
index 0000000..28eb9fc
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/ScreenshotPolicyTest.kt
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.policy
+
+import android.content.ComponentName
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.LAUNCHER
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.MESSAGES
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.YOUTUBE
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREEFORM_FULL_SCREEN
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FULL_SCREEN
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Orientation.VERTICAL
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.TaskSpec
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.freeFormApps
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.pictureInPictureApp
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.splitScreenApps
+import com.android.systemui.screenshot.data.repository.profileTypeRepository
+import com.android.systemui.screenshot.policy.CaptureType.FullScreen
+import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
+import com.android.systemui.screenshot.policy.CaptureType.RootTask
+import com.android.systemui.screenshot.policy.TestUserIds.PERSONAL
+import com.android.systemui.screenshot.policy.TestUserIds.PRIVATE
+import com.android.systemui.screenshot.policy.TestUserIds.WORK
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ScreenshotPolicyTest {
+    private val kosmos = Kosmos()
+
+    private val defaultComponent = ComponentName("default", "default")
+    private val defaultOwner = UserHandle.SYSTEM
+
+    @Test
+    fun fullScreen_work() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                singleFullScreen(TaskSpec(taskId = 1002, name = FILES, userId = WORK)),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = IsolatedTask(taskId = 1002, taskBounds = FULL_SCREEN),
+                    component = ComponentName.unflattenFromString(FILES),
+                    owner = UserHandle.of(WORK),
+                )
+            )
+    }
+
+    @Test
+    fun fullScreen_private() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                singleFullScreen(TaskSpec(taskId = 1002, name = YOUTUBE, userId = PRIVATE)),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(YOUTUBE),
+                    owner = UserHandle.of(PRIVATE),
+                )
+            )
+    }
+
+    @Test
+    fun splitScreen_workAndPersonal() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                splitScreenApps(
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+                    focusedTaskId = 1002,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(YOUTUBE),
+                    owner = UserHandle.of(PERSONAL),
+                )
+            )
+    }
+
+    @Test
+    fun splitScreen_personalAndPrivate() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                splitScreenApps(
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = PERSONAL),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+                    focusedTaskId = 1002,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(YOUTUBE),
+                    owner = UserHandle.of(PRIVATE),
+                )
+            )
+    }
+
+    @Test
+    fun splitScreen_workAndPrivate() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                splitScreenApps(
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+                    focusedTaskId = 1002,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(YOUTUBE),
+                    owner = UserHandle.of(PRIVATE),
+                )
+            )
+    }
+
+    @Test
+    fun splitScreen_twoWorkTasks() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                splitScreenApps(
+                    parentTaskId = 1,
+                    parentBounds = FREEFORM_FULL_SCREEN,
+                    orientation = VERTICAL,
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = WORK),
+                    focusedTaskId = 1002,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type =
+                        RootTask(
+                            parentTaskId = 1,
+                            taskBounds = FREEFORM_FULL_SCREEN,
+                            childTaskIds = listOf(1002, 1003),
+                        ),
+                    component = ComponentName.unflattenFromString(FILES),
+                    owner = UserHandle.of(WORK),
+                )
+            )
+    }
+
+    @Test
+    fun freeform_floatingWindows() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                freeFormApps(
+                    TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+                    focusedTaskId = 1003,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(YOUTUBE),
+                    owner = UserHandle.of(PERSONAL),
+                )
+            )
+    }
+
+    @Test
+    fun freeform_floatingWindows_maximized() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                freeFormApps(
+                    TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+                    focusedTaskId = 1003,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(YOUTUBE),
+                    owner = UserHandle.of(PERSONAL),
+                )
+            )
+    }
+
+    @Test
+    fun freeform_floatingWindows_withPrivate() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                freeFormApps(
+                    TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    TaskSpec(taskId = 1003, name = YOUTUBE, userId = PRIVATE),
+                    TaskSpec(taskId = 1004, name = MESSAGES, userId = PERSONAL),
+                    focusedTaskId = 1004,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(YOUTUBE),
+                    owner = UserHandle.of(PRIVATE),
+                )
+            )
+    }
+
+    @Test
+    fun freeform_floating_workOnly() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                freeFormApps(
+                    TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    focusedTaskId = 1002,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = ComponentName.unflattenFromString(LAUNCHER),
+                    owner = defaultOwner,
+                )
+            )
+    }
+
+    @Test
+    fun fullScreen_shadeExpanded() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                singleFullScreen(
+                    TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    shadeExpanded = true,
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = FullScreen(displayId = 0),
+                    component = defaultComponent,
+                    owner = defaultOwner,
+                )
+            )
+    }
+
+    @Test
+    fun fullScreen_with_PictureInPicture() = runTest {
+        val policy = ScreenshotPolicy(kosmos.profileTypeRepository)
+
+        val result =
+            policy.apply(
+                pictureInPictureApp(
+                    pip = TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
+                    fullScreen = TaskSpec(taskId = 1003, name = FILES, userId = WORK),
+                ),
+                defaultComponent,
+                defaultOwner,
+            )
+
+        assertThat(result)
+            .isEqualTo(
+                CaptureParameters(
+                    type = IsolatedTask(taskId = 1003, taskBounds = FULL_SCREEN),
+                    component = ComponentName.unflattenFromString(FILES),
+                    owner = UserHandle.of(WORK),
+                )
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/TestUserIds.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/TestUserIds.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/TestUserIds.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/TestUserIds.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
similarity index 84%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
index be9fcc2..30a786c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
@@ -31,13 +31,13 @@
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.YOUTUBE
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FREE_FORM
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FULL_SCREEN
-import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.SPLIT_TOP
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.RootTasks
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.TaskSpec
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.freeFormApps
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.pictureInPictureApp
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.splitScreenApps
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.splitTop
 import com.android.systemui.screenshot.data.model.SystemUiState
 import com.android.systemui.screenshot.data.repository.profileTypeRepository
 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult
@@ -69,6 +69,7 @@
     @JvmField @Rule(order = 2) val mockitoRule: MockitoRule = MockitoJUnit.rule()
 
     @Mock lateinit var mContext: Context
+
     @Mock lateinit var mResources: Resources
 
     private val kosmos = Kosmos()
@@ -94,17 +95,11 @@
                 DisplayContentModel(
                     displayId = 0,
                     systemUiState = SystemUiState(shadeExpanded = false),
-                    rootTasks = listOf(RootTasks.emptyWithNoChildTasks)
+                    rootTasks = listOf(RootTasks.emptyWithNoChildTasks),
                 )
             )
 
-        assertThat(result)
-            .isEqualTo(
-                NotMatched(
-                    WorkProfilePolicy.NAME,
-                    WORK_TASK_NOT_TOP,
-                )
-            )
+        assertThat(result).isEqualTo(NotMatched(WorkProfilePolicy.NAME, WORK_TASK_NOT_TOP))
     }
 
     @Test
@@ -114,13 +109,7 @@
                 singleFullScreen(TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL))
             )
 
-        assertThat(result)
-            .isEqualTo(
-                NotMatched(
-                    WorkProfilePolicy.NAME,
-                    WORK_TASK_NOT_TOP,
-                )
-            )
+        assertThat(result).isEqualTo(NotMatched(WorkProfilePolicy.NAME, WORK_TASK_NOT_TOP))
     }
 
     @Test
@@ -129,17 +118,11 @@
             policy.check(
                 singleFullScreen(
                     TaskSpec(taskId = 1002, name = FILES, userId = WORK),
-                    shadeExpanded = true
+                    shadeExpanded = true,
                 )
             )
 
-        assertThat(result)
-            .isEqualTo(
-                NotMatched(
-                    WorkProfilePolicy.NAME,
-                    SHADE_EXPANDED,
-                )
-            )
+        assertThat(result).isEqualTo(NotMatched(WorkProfilePolicy.NAME, SHADE_EXPANDED))
     }
 
     @Test
@@ -156,7 +139,7 @@
                         type = IsolatedTask(taskId = 1002, taskBounds = FULL_SCREEN),
                         component = ComponentName.unflattenFromString(FILES),
                         owner = UserHandle.of(WORK),
-                    )
+                    ),
                 )
             )
     }
@@ -166,9 +149,11 @@
         val result =
             policy.check(
                 splitScreenApps(
-                    top = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
-                    bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
-                    focusedTaskId = 1002
+                    parentBounds = FULL_SCREEN,
+                    taskMargin = 20,
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+                    focusedTaskId = 1002,
                 )
             )
 
@@ -178,10 +163,10 @@
                     policy = WorkProfilePolicy.NAME,
                     reason = WORK_TASK_IS_TOP,
                     CaptureParameters(
-                        type = IsolatedTask(taskId = 1002, taskBounds = SPLIT_TOP),
+                        type = IsolatedTask(taskId = 1002, taskBounds = FULL_SCREEN.splitTop(20)),
                         component = ComponentName.unflattenFromString(FILES),
                         owner = UserHandle.of(WORK),
-                    )
+                    ),
                 )
             )
     }
@@ -191,19 +176,13 @@
         val result =
             policy.check(
                 splitScreenApps(
-                    top = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
-                    bottom = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
-                    focusedTaskId = 1003
+                    first = TaskSpec(taskId = 1002, name = FILES, userId = WORK),
+                    second = TaskSpec(taskId = 1003, name = YOUTUBE, userId = PERSONAL),
+                    focusedTaskId = 1003,
                 )
             )
 
-        assertThat(result)
-            .isEqualTo(
-                NotMatched(
-                    WorkProfilePolicy.NAME,
-                    WORK_TASK_NOT_TOP,
-                )
-            )
+        assertThat(result).isEqualTo(NotMatched(WorkProfilePolicy.NAME, WORK_TASK_NOT_TOP))
     }
 
     @Test
@@ -225,7 +204,7 @@
                         type = IsolatedTask(taskId = 1003, taskBounds = FULL_SCREEN),
                         component = ComponentName.unflattenFromString(FILES),
                         owner = UserHandle.of(WORK),
-                    )
+                    ),
                 )
             )
     }
@@ -238,7 +217,7 @@
                 freeFormApps(
                     TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
                     TaskSpec(taskId = 1003, name = FILES, userId = WORK),
-                    focusedTaskId = 1003
+                    focusedTaskId = 1003,
                 )
             )
 
@@ -251,7 +230,7 @@
                         type = IsolatedTask(taskId = 1003, taskBounds = FREE_FORM),
                         component = ComponentName.unflattenFromString(FILES),
                         owner = UserHandle.of(WORK),
-                    )
+                    ),
                 )
             )
     }
@@ -264,16 +243,10 @@
                 freeFormApps(
                     TaskSpec(taskId = 1002, name = YOUTUBE, userId = PERSONAL),
                     TaskSpec(taskId = 1003, name = FILES, userId = WORK),
-                    focusedTaskId = 1003
+                    focusedTaskId = 1003,
                 )
             )
 
-        assertThat(result)
-            .isEqualTo(
-                NotMatched(
-                    WorkProfilePolicy.NAME,
-                    DESKTOP_MODE_ENABLED,
-                )
-            )
+        assertThat(result).isEqualTo(NotMatched(WorkProfilePolicy.NAME, DESKTOP_MODE_ENABLED))
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/scrim/ScrimViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scrim/ScrimViewTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ConstraintChangeTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ConstraintChangeTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ConstraintChangesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ConstraintChangesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 0e9ef06..d1d3b9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -22,6 +22,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+import static kotlinx.coroutines.flow.SharedFlowKt.MutableSharedFlow;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -36,10 +40,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-import static kotlinx.coroutines.flow.SharedFlowKt.MutableSharedFlow;
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
 import android.animation.Animator;
 import android.annotation.IdRes;
 import android.content.ContentResolver;
@@ -68,13 +68,13 @@
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.EmptyLockIconViewController;
 import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardSliceViewController;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardStatusViewController;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.LegacyLockIconViewController;
 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -108,7 +108,6 @@
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
 import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
@@ -201,6 +200,12 @@
 import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
+import dagger.Lazy;
+
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.channels.BufferOverflow;
+import kotlinx.coroutines.test.TestScope;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -215,11 +220,6 @@
 import java.util.List;
 import java.util.Optional;
 
-import dagger.Lazy;
-import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.channels.BufferOverflow;
-import kotlinx.coroutines.test.TestScope;
-
 public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
 
     protected static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400;
@@ -270,6 +270,7 @@
     @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController;
     @Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent;
     @Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
+    @Mock protected EmptyLockIconViewController mLockIconViewController;
     @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
     @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
     @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController;
@@ -284,7 +285,6 @@
     @Mock protected AmbientState mAmbientState;
     @Mock protected UserManager mUserManager;
     @Mock protected UiEventLogger mUiEventLogger;
-    @Mock protected LegacyLockIconViewController mLockIconViewController;
     @Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
     @Mock protected KeyguardRootView mKeyguardRootView;
     @Mock protected View mKeyguardRootViewChild;
@@ -327,8 +327,6 @@
     @Mock protected LockscreenToOccludedTransitionViewModel
             mLockscreenToOccludedTransitionViewModel;
     @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
-    @Mock protected GoneToDreamingLockscreenHostedTransitionViewModel
-            mGoneToDreamingLockscreenHostedTransitionViewModel;
     @Mock protected PrimaryBouncerToGoneTransitionViewModel
             mPrimaryBouncerToGoneTransitionViewModel;
     @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -396,7 +394,6 @@
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
 
         mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
-        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
 
         mMainDispatcher = getMainDispatcher();
         KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
@@ -461,7 +458,8 @@
                 () -> mKosmos.getSceneInteractor(),
                 () -> mKosmos.getSceneContainerOcclusionInteractor(),
                 () -> mKosmos.getKeyguardClockInteractor(),
-                () -> mKosmos.getSceneBackInteractor());
+                () -> mKosmos.getSceneBackInteractor(),
+                () -> mKosmos.getAlternateBouncerInteractor());
 
         KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
         keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -586,10 +584,6 @@
         when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
                 .thenReturn(emptyFlow());
 
-        // Gone->Dreaming lockscreen hosted
-        when(mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha())
-                .thenReturn(emptyFlow());
-
         // Lockscreen->Occluded
         when(mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha())
                 .thenReturn(emptyFlow());
@@ -622,7 +616,8 @@
                                 () -> mKosmos.getSceneInteractor(),
                                 () -> mKosmos.getSceneContainerOcclusionInteractor(),
                                 () -> mKosmos.getKeyguardClockInteractor(),
-                                () -> mKosmos.getSceneBackInteractor()),
+                                () -> mKosmos.getSceneBackInteractor(),
+                                () -> mKosmos.getAlternateBouncerInteractor()),
                         mKeyguardBypassController,
                         mDozeParameters,
                         mScreenOffAnimationController,
@@ -684,6 +679,9 @@
         when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes);
         when(longPressHandlingViewRes.getString(anyInt())).thenReturn("");
 
+        when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
+        when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
+
         mNotificationPanelViewController = new NotificationPanelViewController(
                 mView,
                 mMainHandler,
@@ -747,7 +745,6 @@
                 mOccludedToLockscreenTransitionViewModel,
                 mLockscreenToDreamingTransitionViewModel,
                 mGoneToDreamingTransitionViewModel,
-                mGoneToDreamingLockscreenHostedTransitionViewModel,
                 mLockscreenToOccludedTransitionViewModel,
                 mPrimaryBouncerToGoneTransitionViewModel,
                 mMainDispatcher,
@@ -849,7 +846,7 @@
             mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel();
             mNotificationPanelViewController.cancelHeightAnimator();
             leakedAnimators = mNotificationPanelViewController.mTestSetOfAnimatorsUsed.stream()
-                    .filter(Animator::isRunning).toList();
+                .filter(Animator::isRunning).toList();
             mNotificationPanelViewController.mTestSetOfAnimatorsUsed.forEach(Animator::cancel);
         }
         if (mMainHandler != null) {
@@ -866,11 +863,7 @@
         when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
         when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom);
         when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
-        when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
-
         when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding));
-        when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
-        when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
 
         when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
                 .thenReturn(indicationPadding);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 43dbb40..ec75972 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -217,7 +217,6 @@
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(5);
 
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(5);
     }
@@ -235,7 +234,6 @@
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
 
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
     }
@@ -253,7 +251,6 @@
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
 
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
     }
@@ -271,7 +268,6 @@
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(2);
 
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(2);
     }
@@ -289,7 +285,6 @@
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
 
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
     }
@@ -389,7 +384,6 @@
     @Test
     @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     public void alternateBouncerVisible_onTouchEvent_notHandled() {
-        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
         // GIVEN alternate bouncer is visible
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
similarity index 86%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 6f2302a..adc336d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -30,7 +30,6 @@
 import android.view.ViewTreeObserver
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityContainerController
-import com.android.keyguard.LegacyLockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.Flags
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
@@ -43,7 +42,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
 import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
@@ -54,7 +52,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.DragDownHelper
@@ -71,7 +68,6 @@
 import com.android.systemui.statusbar.phone.DozeScrimController
 import com.android.systemui.statusbar.phone.DozeServiceHost
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -80,6 +76,7 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.emptyFlow
@@ -98,11 +95,10 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import org.mockito.Mockito.`when` as whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -125,16 +121,12 @@
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var ambientState: AmbientState
     @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
-    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
     @Mock private lateinit var quickSettingsController: QuickSettingsControllerImpl
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
-    @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
     @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
     @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
-    @Mock
-    private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
     @Mock private lateinit var notificationInsetsController: NotificationInsetsController
     @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -144,7 +136,7 @@
     @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
     @Mock
     private lateinit var unfoldTransitionProgressProvider:
-            Optional<UnfoldTransitionProgressProvider>
+        Optional<UnfoldTransitionProgressProvider>
     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
     @Mock lateinit var dragDownHelper: DragDownHelper
     @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -176,20 +168,16 @@
         MockitoAnnotations.initMocks(this)
         whenever(view.bottom).thenReturn(VIEW_BOTTOM)
         whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
-                .thenReturn(mock(ViewGroup::class.java))
+            .thenReturn(mock(ViewGroup::class.java))
         whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
-                .thenReturn(keyguardBouncerComponent)
+            .thenReturn(keyguardBouncerComponent)
         whenever(keyguardBouncerComponent.securityContainerController)
-                .thenReturn(keyguardSecurityContainerController)
+            .thenReturn(keyguardSecurityContainerController)
         whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
-                .thenReturn(emptyFlow<TransitionStep>())
+            .thenReturn(emptyFlow<TransitionStep>())
 
         featureFlagsClassic = FakeFeatureFlagsClassic()
         featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
-        featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
-        if (!SceneContainerFlag.isEnabled) {
-            mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        }
         mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
 
         testScope = TestScope()
@@ -208,9 +196,7 @@
                 panelExpansionInteractor,
                 ShadeExpansionStateManager(),
                 stackScrollLayoutController,
-                statusBarKeyguardViewManager,
                 statusBarWindowStateController,
-                lockIconViewController,
                 centralSurfaces,
                 dozeServiceHost,
                 dozeScrimController,
@@ -223,7 +209,6 @@
                 shadeLogger,
                 dumpManager,
                 pulsingGestureListener,
-                mLockscreenHostedDreamGestureListener,
                 keyguardTransitionInteractor,
                 mGlanceableHubContainerController,
                 notificationLaunchAnimationInteractor,
@@ -233,7 +218,7 @@
                 quickSettingsController,
                 primaryBouncerInteractor,
                 alternateBouncerInteractor,
-                mock(BouncerViewBinder::class.java)
+                mock(BouncerViewBinder::class.java),
             )
         underTest.setupExpandedStatusBar()
         underTest.setDragDownHelper(dragDownHelper)
@@ -294,7 +279,7 @@
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
             whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true)
 
             val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -309,7 +294,7 @@
             underTest.setStatusBarViewController(phoneStatusBarViewController)
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
             // Item we're testing
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(false)
 
@@ -327,7 +312,7 @@
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             // Item we're testing
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(false)
+                .thenReturn(false)
 
             val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
 
@@ -341,7 +326,7 @@
             underTest.setStatusBarViewController(phoneStatusBarViewController)
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
             // Item we're testing
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
 
@@ -358,7 +343,7 @@
             whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
             whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
             whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
-                    .thenReturn(true)
+                .thenReturn(true)
 
             // Down event first
             interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -379,7 +364,7 @@
             // GIVEN touch dispatcher in a state that returns true
             underTest.setStatusBarViewController(phoneStatusBarViewController)
             whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
-                    .thenReturn(true)
+                .thenReturn(true)
             assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
 
             // WHEN launch animation is running for 2 seconds
@@ -432,47 +417,13 @@
     }
 
     @Test
-    fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
-        // down event should be intercepted by keyguardViewManager
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(true)
-
-        // Then touch should not be intercepted
-        val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
-        assertThat(shouldIntercept).isTrue()
-    }
-
-    @Test
-    @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-    fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() {
-        // GIVEN dozing
-        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
-        // AND alternate bouncer doesn't want the touch
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND quick settings controller doesn't want it
-        whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(false)
-        // AND the lock icon wants the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true)
-
-        // THEN touch should NOT be intercepted by NotificationShade
-        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
-    }
-
-    @Test
     @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
         // GIVEN dozing
         whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
-        // AND alternate bouncer doesn't want the touch
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND the lock icon does NOT want the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
         // AND quick settings controller doesn't want it
         whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(false)
+            .thenReturn(false)
 
         // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -483,14 +434,9 @@
     fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() {
         // GIVEN dozing
         whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
-        // AND alternate bouncer doesn't want the touch
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND the lock icon does NOT want the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
         // AND quick settings controller DOES want it
         whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(true)
+            .thenReturn(true)
 
         // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -503,20 +449,13 @@
         whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
         // AND pulsing
         whenever(dozeServiceHost.isPulsing()).thenReturn(true)
-        // AND status bar doesn't want it
-        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
-                .thenReturn(false)
-        // AND shade is not fully expanded (mock is false by default)
-        // AND the lock icon does NOT want the touch
-        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
         // AND quick settings controller DOES want it
         whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
-                .thenReturn(true)
+            .thenReturn(true)
         // AND bouncer is not showing
         whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
         // AND panel view controller wants it
-        whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT))
-                .thenReturn(true)
+        whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)).thenReturn(true)
 
         // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -589,12 +528,10 @@
         underTest.setupCommunalHubLayout()
 
         // Simluate attaching the view so flow collection starts.
-        val onAttachStateChangeListenerArgumentCaptor = ArgumentCaptor.forClass(
-            View.OnAttachStateChangeListener::class.java
-        )
-        verify(view, atLeast(1)).addOnAttachStateChangeListener(
-            onAttachStateChangeListenerArgumentCaptor.capture()
-        )
+        val onAttachStateChangeListenerArgumentCaptor =
+            ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+        verify(view, atLeast(1))
+            .addOnAttachStateChangeListener(onAttachStateChangeListenerArgumentCaptor.capture())
         for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) {
             listener.onViewAttachedToWindow(view)
         }
@@ -608,7 +545,7 @@
     @RequiresFlagsDisabled(Flags.FLAG_COMMUNAL_HUB)
     fun doesNotSetupCommunalHubLayout_whenFlagDisabled() {
         whenever(mGlanceableHubContainerController.communalAvailable())
-                .thenReturn(MutableStateFlow(false))
+            .thenReturn(MutableStateFlow(false))
 
         val mockCommunalPlaceholder = mock(View::class.java)
         val fakeViewIndex = 20
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
similarity index 83%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ca29dd9..d61320e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityContainerController
-import com.android.keyguard.LegacyLockIconViewController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
@@ -57,7 +56,6 @@
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.DozeScrimController
 import com.android.systemui.statusbar.phone.DozeServiceHost
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -104,19 +102,15 @@
     @Mock
     private lateinit var notificationStackScrollLayoutController:
         NotificationStackScrollLayoutController
-    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
-    @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
     @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
     @Mock private lateinit var ambientState: AmbientState
     @Mock private lateinit var shadeLogger: ShadeLogger
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
     @Mock private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent
-    @Mock
-    private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
     @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
     @Mock private lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
     @Mock
@@ -160,8 +154,6 @@
 
         val featureFlags = FakeFeatureFlags()
         featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
-        featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         testScope = TestScope()
         controller =
@@ -176,9 +168,7 @@
                 panelExpansionInteractor,
                 ShadeExpansionStateManager(),
                 notificationStackScrollLayoutController,
-                statusBarKeyguardViewManager,
                 statusBarWindowStateController,
-                lockIconViewController,
                 centralSurfaces,
                 dozeServiceHost,
                 dozeScrimController,
@@ -191,7 +181,6 @@
                 shadeLogger,
                 dumpManager,
                 pulsingGestureListener,
-                mLockscreenHostedDreamGestureListener,
                 keyguardTransitionInteractor,
                 mGlanceableHubContainerController,
                 NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()),
@@ -221,48 +210,18 @@
         }
 
     @Test
-    fun testInterceptTouchWhenShowingAltAuth() =
-        testScope.runTest {
-            captureInteractionEventHandler()
-
-            // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
-            whenever(statusBarStateController.isDozing).thenReturn(false)
-            whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true)
-            whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
-
-            // THEN we should intercept touch
-            assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue()
-        }
-
-    @Test
     fun testNoInterceptTouch() =
         testScope.runTest {
             captureInteractionEventHandler()
 
-            // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
+            // WHEN not dozing, drag down helper doesn't want to intercept
             whenever(statusBarStateController.isDozing).thenReturn(false)
-            whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any()))
-                .thenReturn(false)
             whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
 
             // THEN we shouldn't intercept touch
             assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse()
         }
 
-    @Test
-    fun testHandleTouchEventWhenShowingAltAuth() =
-        testScope.runTest {
-            captureInteractionEventHandler()
-
-            // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
-            whenever(statusBarStateController.isDozing).thenReturn(false)
-            whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true)
-            whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
-
-            // THEN we should handle the touch
-            assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue()
-        }
-
     private fun captureInteractionEventHandler() {
         verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
         interactionEventHandler = interactionEventHandlerCaptor.value
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 905301e..943fb62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -74,6 +75,7 @@
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock private lateinit var statusBarWindowController: StatusBarWindowController
+    @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
     @Mock private lateinit var windowManager: WindowManager
@@ -105,6 +107,8 @@
         MockitoAnnotations.initMocks(this)
         whenever(windowManager.defaultDisplay).thenReturn(display)
         whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
+        whenever(statusBarWindowControllerStore.defaultDisplay)
+            .thenReturn(statusBarWindowController)
         shadeController =
             ShadeControllerImpl(
                 commandQueue,
@@ -113,7 +117,7 @@
                 keyguardStateController,
                 statusBarStateController,
                 statusBarKeyguardViewManager,
-                statusBarWindowController,
+                statusBarWindowControllerStore,
                 deviceProvisionedController,
                 notificationShadeWindowController,
                 0,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/QuickStepContractTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/system/QuickStepContractTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/system/QuickStepContractTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/system/QuickStepContractTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index 59678a2..4478252 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -19,7 +19,6 @@
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
 
 import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
 import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
 
@@ -273,7 +272,6 @@
 
         mFlags = new FakeFeatureFlags();
         mFlags.set(KEYGUARD_TALKBACK_FIX, true);
-        mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
         mController = new KeyguardIndicationController(
                 mContext,
                 mTestableLooper.getLooper(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationListenerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationListenerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index db274cc..f8720b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -28,6 +28,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.flags.DisableSceneContainer
@@ -83,8 +85,9 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+    private val alternateBouncerInteractor by lazy { kosmos.alternateBouncerInteractor }
     private val mockDarkAnimator = mock<ObjectAnimator>()
 
     private lateinit var underTest: StatusBarStateControllerImpl
@@ -121,6 +124,7 @@
                     { kosmos.sceneContainerOcclusionInteractor },
                     { kosmos.keyguardClockInteractor },
                     { kosmos.sceneBackInteractor },
+                    { kosmos.alternateBouncerInteractor },
                 ) {
                 override fun createDarkAnimator(): ObjectAnimator {
                     return mockDarkAnimator
@@ -299,6 +303,52 @@
 
     @Test
     @EnableSceneContainer
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun start_hydratesStatusBarState_withAlternateBouncer() =
+        testScope.runTest {
+            var statusBarState = underTest.state
+            val listener =
+                object : StatusBarStateController.StateListener {
+                    override fun onStateChanged(newState: Int) {
+                        statusBarState = newState
+                    }
+                }
+            underTest.addCallback(listener)
+
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val deviceUnlockStatus by
+                collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus)
+            val alternateBouncerIsVisible by collectLastValue(alternateBouncerInteractor.isVisible)
+
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Password
+            )
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+            assertThat(deviceUnlockStatus!!.isUnlocked).isTrue()
+
+            sceneInteractor.changeScene(toScene = Scenes.Lockscreen, loggingReason = "reason")
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+            kosmos.givenCanShowAlternateBouncer()
+            alternateBouncerInteractor.forceShow()
+            runCurrent()
+            assertThat(alternateBouncerIsVisible).isTrue()
+
+            // Call start to begin hydrating based on the scene framework:
+            underTest.start()
+
+            sceneInteractor.changeScene(toScene = Scenes.Gone, loggingReason = "reason")
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+        }
+
+    @Test
+    @EnableSceneContainer
     @EnableFlags(DualShade.FLAG_NAME)
     fun start_hydratesStatusBarState_dualShade_whileLocked() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
similarity index 74%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
index 118dea6..69a7627 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
 
 import android.content.packageManager
 import android.graphics.drawable.BitmapDrawable
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -40,13 +40,13 @@
 import org.mockito.kotlin.whenever
 
 @SmallTest
-class DemoRonChipViewModelTest : SysuiTestCase() {
+class DemoNotifChipViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val commandRegistry = kosmos.commandRegistry
     private val pw = PrintWriter(StringWriter())
 
-    private val underTest = kosmos.demoRonChipViewModel
+    private val underTest = kosmos.demoNotifChipViewModel
 
     @Before
     fun setUp() {
@@ -56,61 +56,61 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_flagOff_hidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            addDemoRonChip()
+            addDemoNotifChip()
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_noPackage_hidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron"))
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif"))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_hasPackage_shown() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_hasText_shownWithText() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
             commandRegistry.onShellCommand(
                 pw,
-                arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test")
+                arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"),
             )
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_supportsColor() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
             commandRegistry.onShellCommand(
                 pw,
-                arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343")
+                arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"),
             )
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
@@ -119,28 +119,28 @@
         }
 
     @Test
-    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun chip_hasHideArg_hidden() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
             // First, show a chip
-            addDemoRonChip()
+            addDemoNotifChip()
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
 
             // Then, hide the chip
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide"))
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide"))
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
-    private fun addDemoRonChip() {
-        Companion.addDemoRonChip(commandRegistry, pw)
+    private fun addDemoNotifChip() {
+        addDemoNotifChip(commandRegistry, pw)
     }
 
     companion object {
-        fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
-            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+        fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
+            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
         }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
new file mode 100644
index 0000000..eb5d931
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.notification.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+class NotifChipsViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
+
+    private val underTest = kosmos.notifChipsViewModel
+
+    @Test
+    fun chips_noNotifs_empty() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            setNotifs(emptyList())
+
+            assertThat(latest).isEmpty()
+        }
+
+    @Test
+    fun chips_notifMissingStatusBarChipIconView_empty() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null)))
+
+            assertThat(latest).isEmpty()
+        }
+
+    @Test
+    fun chips_oneNotif_statusBarIconViewMatches() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val icon = mock<StatusBarIconView>()
+            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+
+            assertThat(latest).hasSize(1)
+            val chip = latest!![0]
+            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+            assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
+        }
+
+    @Test
+    fun chips_twoNotifs_twoChips() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val firstIcon = mock<StatusBarIconView>()
+            val secondIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon),
+                )
+            )
+
+            assertThat(latest).hasSize(2)
+            assertIsNotifChip(latest!![0], firstIcon)
+            assertIsNotifChip(latest!![1], secondIcon)
+        }
+
+    private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+        activeNotificationListRepository.activeNotifications.value =
+            ActiveNotificationsStore.Builder()
+                .apply { notifs.forEach { addIndividualNotif(it) } }
+                .build()
+        testScope.runCurrent()
+    }
+
+    companion object {
+        fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
+            assertThat(latest)
+                .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 26ce7b9..e96def6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -25,7 +25,6 @@
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
@@ -40,7 +39,8 @@
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -66,13 +66,11 @@
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
-/**
- * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled.
- */
+/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
-@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+@DisableFlags(StatusBarNotifChips.FLAG_NAME)
 class OngoingActivityChipsViewModelTest : SysuiTestCase() {
     private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
@@ -99,11 +97,11 @@
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
-        kosmos.demoRonChipViewModel.start()
+        kosmos.demoNotifChipViewModel.start()
         val icon =
             BitmapDrawable(
                 context.resources,
-                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888),
             )
         whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
     }
@@ -325,7 +323,7 @@
             latest: OngoingActivityChipModel?,
             chipView: View,
             dialog: SystemUIDialog,
-            kosmos: Kosmos
+            kosmos: Kosmos,
         ): DialogInterface.OnClickListener {
             // Capture the action that would get invoked when the user clicks "Stop" on the dialog
             lateinit var dialogStopAction: DialogInterface.OnClickListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
similarity index 77%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 631120b..b12d7c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -24,7 +24,6 @@
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
@@ -34,10 +33,13 @@
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
 import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -46,6 +48,10 @@
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
 import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
@@ -67,14 +73,12 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
-/**
- * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled.
- */
+/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
-class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val systemClock = kosmos.fakeSystemClock
@@ -83,6 +87,7 @@
     private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
     private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
     private val callRepo = kosmos.ongoingCallRepository
+    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
 
     private val pw = PrintWriter(StringWriter())
 
@@ -103,16 +108,16 @@
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
-        kosmos.demoRonChipViewModel.start()
+        kosmos.demoNotifChipViewModel.start()
         val icon =
             BitmapDrawable(
                 context.resources,
-                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+                Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888),
             )
         whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
     }
 
-    // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the
+    // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the
     // flow has the right behavior to verify that we don't break any existing functionality.
 
     @Test
@@ -249,13 +254,13 @@
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
 
             val latest by collectLastValue(underTest.chips)
 
             assertIsScreenRecordChip(latest!!.primary)
             assertIsCallChip(latest!!.secondary)
-            // Demo RON chip is dropped
+            // Demo notif chip is dropped
         }
 
     @Test
@@ -288,10 +293,101 @@
         }
 
     @Test
+    fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val icon = mock<StatusBarIconView>()
+            setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+
+            assertIsNotifChip(latest!!.primary, icon)
+            assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val firstIcon = mock<StatusBarIconView>()
+            val secondIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
+                )
+            )
+
+            assertIsNotifChip(latest!!.primary, firstIcon)
+            assertIsNotifChip(latest!!.secondary, secondIcon)
+        }
+
+    @Test
+    fun chips_threeNotifChips_topTwoShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            val firstIcon = mock<StatusBarIconView>()
+            val secondIcon = mock<StatusBarIconView>()
+            val thirdIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
+                    activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon),
+                )
+            )
+
+            assertIsNotifChip(latest!!.primary, firstIcon)
+            assertIsNotifChip(latest!!.secondary, secondIcon)
+        }
+
+    @Test
+    fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            val firstIcon = mock<StatusBarIconView>()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+                    activeNotificationModel(
+                        key = "secondNotif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                    ),
+                )
+            )
+
+            assertIsCallChip(latest!!.primary)
+            assertIsNotifChip(latest!!.secondary, firstIcon)
+        }
+
+    @Test
+    fun chips_screenRecordAndCallAndNotifs_notifsNotShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            screenRecordState.value = ScreenRecordModel.Recording
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = mock<StatusBarIconView>(),
+                    )
+                )
+            )
+
+            assertIsScreenRecordChip(latest!!.primary)
+            assertIsCallChip(latest!!.secondary)
+        }
+
+    @Test
     fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
         testScope.runTest {
             // Start with just the lowest priority chip shown
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
             // And everything else hidden
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
             mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -299,7 +395,7 @@
 
             val latest by collectLastValue(underTest.primaryChip)
 
-            assertIsDemoRonChip(latest)
+            assertIsDemoNotifChip(latest)
 
             // WHEN the higher priority call chip is added
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
@@ -333,7 +429,7 @@
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -355,15 +451,15 @@
             // WHEN the higher priority call is removed
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            // THEN the lower priority demo RON is used
-            assertIsDemoRonChip(latest)
+            // THEN the lower priority demo notif is used
+            assertIsDemoNotifChip(latest)
         }
 
     @Test
     fun chips_movesChipsAroundAccordingToPriority() =
         testScope.runTest {
             // Start with just the lowest priority chip shown
-            addDemoRonChip(commandRegistry, pw)
+            addDemoNotifChip(commandRegistry, pw)
             // And everything else hidden
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
             mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -371,16 +467,16 @@
 
             val latest by collectLastValue(underTest.chips)
 
-            assertIsDemoRonChip(latest!!.primary)
+            assertIsDemoNotifChip(latest!!.primary)
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
 
             // WHEN the higher priority call chip is added
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
-            // THEN the higher priority call chip is used as primary and demo ron is demoted to
+            // THEN the higher priority call chip is used as primary and demo notif is demoted to
             // secondary
             assertIsCallChip(latest!!.primary)
-            assertIsDemoRonChip(latest!!.secondary)
+            assertIsDemoNotifChip(latest!!.secondary)
 
             // WHEN the higher priority media projection chip is added
             mediaProjectionState.value =
@@ -391,7 +487,7 @@
                 )
 
             // THEN the higher priority media projection chip is used as primary and call is demoted
-            // to secondary (and demo RON is dropped altogether)
+            // to secondary (and demo notif is dropped altogether)
             assertIsShareToAppChip(latest!!.primary)
             assertIsCallChip(latest!!.secondary)
 
@@ -405,15 +501,15 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            // THEN media projection and demo RON remain
+            // THEN media projection and demo notif remain
             assertIsShareToAppChip(latest!!.primary)
-            assertIsDemoRonChip(latest!!.secondary)
+            assertIsDemoNotifChip(latest!!.secondary)
 
             // WHEN media projection is dropped
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            // THEN demo RON is promoted to primary
-            assertIsDemoRonChip(latest!!.primary)
+            // THEN demo notif is promoted to primary
+            assertIsDemoNotifChip(latest!!.primary)
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
@@ -526,9 +622,17 @@
             assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false))
         }
 
-    private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
+    private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) {
         assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
         assertThat((latest as OngoingActivityChipModel.Shown).icon)
             .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
     }
+
+    private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+        activeNotificationListRepository.activeNotifications.value =
+            ActiveNotificationsStore.Builder()
+                .apply { notifs.forEach { addIndividualNotif(it) } }
+                .build()
+        testScope.runCurrent()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/MobileIconCarrierIdOverridesFake.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
new file mode 100644
index 0000000..8dcc444
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Expect
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
+    @get:Rule val expect: Expect = Expect.create()
+
+    private val kosmos =
+        testKosmos().also {
+            it.statusBarOrchestratorFactory = it.fakeStatusBarOrchestratorFactory
+            it.statusBarInitializerStore = it.fakeStatusBarInitializerStore
+        }
+    private val testScope = kosmos.testScope
+    private val fakeDisplayRepository = kosmos.displayRepository
+    private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory
+    private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore
+
+    // Lazy, so that @EnableFlags is set before initializer is instantiated.
+    private val underTest by lazy { kosmos.multiDisplayStatusBarStarter }
+
+    @Test
+    fun start_startsInitializersForCurrentDisplays() =
+        testScope.runTest {
+            fakeDisplayRepository.addDisplay(displayId = 1)
+            fakeDisplayRepository.addDisplay(displayId = 2)
+
+            underTest.start()
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 1).startedByCoreStartable)
+                .isTrue()
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 2).startedByCoreStartable)
+                .isTrue()
+        }
+
+    @Test
+    fun start_startsOrchestratorForCurrentDisplays() =
+        testScope.runTest {
+            fakeDisplayRepository.addDisplay(displayId = 1)
+            fakeDisplayRepository.addDisplay(displayId = 2)
+
+            underTest.start()
+            runCurrent()
+
+            verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 1)!!).start()
+            verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 2)!!).start()
+        }
+
+    @Test
+    fun displayAdded_orchestratorForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+            runCurrent()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start()
+        }
+
+    @Test
+    fun displayAdded_initializerForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+            runCurrent()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+                .isTrue()
+        }
+
+    @Test
+    fun displayAddedDuringStart_initializerForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+                .isTrue()
+        }
+
+    @Test
+    fun displayAddedDuringStart_orchestratorForNewDisplayIsStarted() =
+        testScope.runTest {
+            underTest.start()
+
+            fakeDisplayRepository.addDisplay(displayId = 3)
+            runCurrent()
+
+            expect
+                .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+                .isTrue()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
similarity index 66%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
index 9142972..e312d00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -20,13 +20,17 @@
 import android.app.FragmentTransaction
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.view.ViewGroup
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import org.junit.Assert.assertThrows
@@ -34,33 +38,40 @@
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
 import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class StatusBarInitializerTest : SysuiTestCase() {
-    val windowController = mock(StatusBarWindowController::class.java)
+    private val windowController = mock(StatusBarWindowController::class.java)
+    private val windowControllerStore = mock(StatusBarWindowControllerStore::class.java)
+    private val transaction = mock(FragmentTransaction::class.java)
+    private val fragmentManager = mock(FragmentManager::class.java)
+    private val fragmentHostManager = mock(FragmentHostManager::class.java)
+    private val backgroundView = mock(ViewGroup::class.java)
 
     @Before
     fun setup() {
         // TODO(b/364360986) this will go away once the fragment is deprecated. Hence, there is no
         // need right now for moving this to kosmos
-        val transaction = mock(FragmentTransaction::class.java)
-        val fragmentManager = mock(FragmentManager::class.java)
-        val fragmentHostManager = mock(FragmentHostManager::class.java)
         whenever(fragmentHostManager.addTagListener(any(), any())).thenReturn(fragmentHostManager)
         whenever(fragmentHostManager.fragmentManager).thenReturn(fragmentManager)
         whenever(fragmentManager.beginTransaction()).thenReturn(transaction)
         whenever(transaction.replace(any(), any(), any())).thenReturn(transaction)
-
+        whenever(windowControllerStore.defaultDisplay).thenReturn(windowController)
         whenever(windowController.fragmentHostManager).thenReturn(fragmentHostManager)
+        whenever(windowController.backgroundView).thenReturn(backgroundView)
     }
 
     val underTest =
         StatusBarInitializerImpl(
-            windowController,
-            { mock(CollapsedStatusBarFragment::class.java) },
-            setOf(),
+            statusBarWindowController = windowController,
+            collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) },
+            statusBarRootFactory = mock(StatusBarRootFactory::class.java),
+            componentFactory = mock(HomeStatusBarComponent.Factory::class.java),
+            creationListeners = setOf(),
         )
 
     @Test
@@ -77,6 +88,15 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+    fun simpleFragment_flagEnabled_doesNotCreateFragment() {
+        underTest.start()
+
+        verify(fragmentManager, never()).beginTransaction()
+        verify(transaction, never()).replace(any(), any(), any())
+    }
+
+    @Test
     @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     fun flagOff_doesNotInitializeViaCoreStartable() {
         underTest.start()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
similarity index 77%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
index 5803365..ab8e878 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -38,12 +38,9 @@
 import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT
 import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
 import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
 import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
-import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
 import com.android.systemui.statusbar.window.fakeStatusBarWindowController
 import com.android.systemui.testKosmos
 import com.android.wm.shell.bubbles.bubbles
@@ -60,25 +57,20 @@
 @RunWith(AndroidJUnit4::class)
 class StatusBarOrchestratorTest : SysuiTestCase() {
 
-    private val kosmos =
-        testKosmos().also {
-            it.testDispatcher = it.unconfinedTestDispatcher
-            it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore
-        }
+    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
     private val testScope = kosmos.testScope
-    private val statusBarViewController = kosmos.mockPhoneStatusBarViewController
-    private val statusBarWindowController = kosmos.fakeStatusBarWindowController
-    private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
-    private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider
-    private val notificationShadeWindowViewController =
+    private val fakeStatusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository
+    private val mockPluginDependencyProvider = kosmos.mockPluginDependencyProvider
+    private val mockNotificationShadeWindowViewController =
         kosmos.mockNotificationShadeWindowViewController
-    private val shadeSurface = kosmos.mockShadeSurface
-    private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
-    private val fakeStatusBarWindowStateRepositoryStore =
-        kosmos.fakeStatusBarWindowStateRepositoryStore
+    private val mockShadeSurface = kosmos.mockShadeSurface
+    private val fakeBouncerRepository = kosmos.fakeKeyguardBouncerRepository
+    private val fakeStatusBarWindowStatePerDisplayRepository =
+        kosmos.fakeStatusBarWindowStatePerDisplayRepository
     private val fakePowerRepository = kosmos.fakePowerRepository
-    private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
     private val mockBubbles = kosmos.bubbles
+    private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController
+    private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer
 
     private val orchestrator = kosmos.statusBarOrchestrator
 
@@ -86,30 +78,31 @@
     fun start_setsUpPluginDependencies() {
         orchestrator.start()
 
-        verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
-        verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java)
+        verify(mockPluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
+        verify(mockPluginDependencyProvider)
+            .allowPluginDependency(StatusBarStateController::class.java)
     }
 
     @Test
     fun start_attachesWindow() {
         orchestrator.start()
 
-        assertThat(statusBarWindowController.isAttached).isTrue()
+        assertThat(fakeStatusBarWindowController.isAttached).isTrue()
     }
 
     @Test
     fun start_setsStatusBarControllerOnShade() {
         orchestrator.start()
 
-        verify(notificationShadeWindowViewController)
-            .setStatusBarViewController(statusBarViewController)
+        verify(mockNotificationShadeWindowViewController)
+            .setStatusBarViewController(fakeStatusBarInitializer.statusBarViewController)
     }
 
     @Test
     fun start_updatesShadeExpansion() {
         orchestrator.start()
 
-        verify(shadeSurface).updateExpansionAndVisibility()
+        verify(mockShadeSurface).updateExpansionAndVisibility()
     }
 
     @Test
@@ -117,9 +110,9 @@
         testScope.runTest {
             orchestrator.start()
 
-            bouncerRepository.setPrimaryShow(isShowing = true)
+            fakeBouncerRepository.setPrimaryShow(isShowing = true)
 
-            verify(statusBarViewController)
+            verify(fakeStatusBarInitializer.statusBarViewController)
                 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
         }
 
@@ -128,9 +121,9 @@
         testScope.runTest {
             orchestrator.start()
 
-            bouncerRepository.setPrimaryShow(isShowing = false)
+            fakeBouncerRepository.setPrimaryShow(isShowing = false)
 
-            verify(statusBarViewController)
+            verify(fakeStatusBarInitializer.statusBarViewController)
                 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
         }
 
@@ -141,7 +134,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions).finishAnimations()
+            verify(fakeStatusBarInitializer.statusBarTransitions).finishAnimations()
         }
 
     @Test
@@ -151,7 +144,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions, never()).finishAnimations()
+            verify(fakeStatusBarInitializer.statusBarTransitions, never()).finishAnimations()
         }
 
     @Test
@@ -208,7 +201,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
         }
 
@@ -222,19 +215,19 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
 
             setStatusBarMode(OPAQUE)
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true)
 
             setStatusBarMode(LIGHTS_OUT)
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true)
 
             setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
         }
 
@@ -248,7 +241,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
         }
 
@@ -262,7 +255,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
         }
 
@@ -276,7 +269,7 @@
 
             orchestrator.start()
 
-            verify(mockPhoneStatusBarTransitions)
+            verify(fakeStatusBarInitializer.statusBarTransitions)
                 .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
         }
 
@@ -295,7 +288,7 @@
             setTransientStatusBar()
             clearTransientStatusBar()
 
-            verify(mockPhoneStatusBarTransitions, times(1))
+            verify(fakeStatusBarInitializer.statusBarTransitions, times(1))
                 .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
         }
 
@@ -318,18 +311,18 @@
     }
 
     private fun setTransientStatusBar() {
-        statusBarModeRepository.defaultDisplay.showTransient()
+        fakeStatusBarModePerDisplayRepository.showTransient()
     }
 
     private fun clearTransientStatusBar() {
-        statusBarModeRepository.defaultDisplay.clearTransient()
+        fakeStatusBarModePerDisplayRepository.clearTransient()
     }
 
     private fun setStatusBarWindowState(state: StatusBarWindowState) {
-        fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state)
+        fakeStatusBarWindowStatePerDisplayRepository.setWindowState(state)
     }
 
     private fun setStatusBarMode(statusBarMode: StatusBarMode) {
-        statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode
+        fakeStatusBarModePerDisplayRepository.statusBarMode.value = statusBarMode
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 48ae7a2..feda0c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.phone.LetterboxAppearance
 import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
@@ -62,8 +62,8 @@
     private val commandQueue = mock<CommandQueue>()
     private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>()
     private val statusBarBoundsProvider = mock<StatusBarBoundsProvider>()
-    private val statusBarFragmentComponent =
-        mock<StatusBarFragmentComponent>().also {
+    private val homeStatusBarComponent =
+        mock<HomeStatusBarComponent>().also {
             whenever(it.boundsProvider).thenReturn(statusBarBoundsProvider)
         }
     private val ongoingCallRepository = kosmos.ongoingCallRepository
@@ -78,7 +78,7 @@
             )
             .apply {
                 this.start()
-                this.onStatusBarViewInitialized(statusBarFragmentComponent)
+                this.onStatusBarViewInitialized(homeStatusBarComponent)
             }
 
     private val commandQueueCallback: CommandQueue.Callbacks
@@ -235,9 +235,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.isInFullscreenMode)
 
-            onSystemBarAttributesChanged(
-                requestedVisibleTypes = WindowInsets.Type.statusBars(),
-            )
+            onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.statusBars())
 
             assertThat(latest).isFalse()
         }
@@ -247,9 +245,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.isInFullscreenMode)
 
-            onSystemBarAttributesChanged(
-                requestedVisibleTypes = WindowInsets.Type.navigationBars(),
-            )
+            onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars())
 
             assertThat(latest).isTrue()
         }
@@ -259,9 +255,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.isInFullscreenMode)
 
-            onSystemBarAttributesChanged(
-                requestedVisibleTypes = WindowInsets.Type.navigationBars(),
-            )
+            onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars())
             assertThat(latest).isTrue()
 
             onSystemBarAttributesChanged(
@@ -347,7 +341,7 @@
             val startingLetterboxAppearance =
                 LetterboxAppearance(
                     APPEARANCE_LIGHT_STATUS_BARS,
-                    listOf(AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, Rect(0, 0, 1, 1)))
+                    listOf(AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, Rect(0, 0, 1, 1))),
                 )
             whenever(
                     letterboxAppearanceCalculator.getLetterboxAppearance(
@@ -371,7 +365,7 @@
             val newLetterboxAppearance =
                 LetterboxAppearance(
                     APPEARANCE_LOW_PROFILE_BARS,
-                    listOf(AppearanceRegion(APPEARANCE_LOW_PROFILE_BARS, Rect(10, 20, 30, 40)))
+                    listOf(AppearanceRegion(APPEARANCE_LOW_PROFILE_BARS, Rect(10, 20, 30, 40))),
                 )
             whenever(
                     letterboxAppearanceCalculator.getLetterboxAppearance(
@@ -398,9 +392,7 @@
             val latest by collectLastValue(underTest.statusBarAppearance)
 
             ongoingCallRepository.setOngoingCallState(inCallModel(startTimeMs = 34))
-            onSystemBarAttributesChanged(
-                requestedVisibleTypes = WindowInsets.Type.navigationBars(),
-            )
+            onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars())
 
             assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT)
         }
@@ -438,9 +430,7 @@
     fun statusBarMode_transientShown_semiTransparent() =
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
-            onSystemBarAttributesChanged(
-                appearance = APPEARANCE_OPAQUE_STATUS_BARS,
-            )
+            onSystemBarAttributesChanged(appearance = APPEARANCE_OPAQUE_STATUS_BARS)
 
             underTest.showTransient()
 
@@ -453,7 +443,7 @@
             val latest by collectLastValue(underTest.statusBarAppearance)
 
             onSystemBarAttributesChanged(
-                appearance = APPEARANCE_LOW_PROFILE_BARS or APPEARANCE_OPAQUE_STATUS_BARS,
+                appearance = APPEARANCE_LOW_PROFILE_BARS or APPEARANCE_OPAQUE_STATUS_BARS
             )
 
             assertThat(latest!!.mode).isEqualTo(StatusBarMode.LIGHTS_OUT)
@@ -464,9 +454,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            onSystemBarAttributesChanged(
-                appearance = APPEARANCE_LOW_PROFILE_BARS,
-            )
+            onSystemBarAttributesChanged(appearance = APPEARANCE_LOW_PROFILE_BARS)
 
             assertThat(latest!!.mode).isEqualTo(StatusBarMode.LIGHTS_OUT_TRANSPARENT)
         }
@@ -476,9 +464,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            onSystemBarAttributesChanged(
-                appearance = APPEARANCE_OPAQUE_STATUS_BARS,
-            )
+            onSystemBarAttributesChanged(appearance = APPEARANCE_OPAQUE_STATUS_BARS)
 
             assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
         }
@@ -488,9 +474,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            onSystemBarAttributesChanged(
-                appearance = APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
-            )
+            onSystemBarAttributesChanged(appearance = APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS)
 
             assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT)
         }
@@ -500,9 +484,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            onSystemBarAttributesChanged(
-                appearance = 0,
-            )
+            onSystemBarAttributesChanged(appearance = 0)
 
             assertThat(latest!!.mode).isEqualTo(StatusBarMode.TRANSPARENT)
         }
@@ -540,7 +522,7 @@
                 LetterboxDetails(
                     /* letterboxInnerBounds= */ Rect(0, 0, 10, 10),
                     /* letterboxFullBounds= */ Rect(0, 0, 20, 20),
-                    /* appAppearance= */ 0
+                    /* appAppearance= */ 0,
                 )
             )
         private val REGIONS_FROM_LETTERBOX_CALCULATOR =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/FakeStatusEvent.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index 984bda1..a629b24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -53,6 +54,7 @@
 
     @get:Rule val animatorTestRule = AnimatorTestRule(this)
     @Mock private lateinit var sbWindowController: StatusBarWindowController
+    @Mock private lateinit var sbWindowControllerStore: StatusBarWindowControllerStore
     @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider
 
     private var testView = TestView(mContext)
@@ -61,7 +63,7 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-
+        whenever(sbWindowControllerStore.defaultDisplay).thenReturn(sbWindowController)
         // StatusBarWindowController is mocked. The addViewToWindow function needs to be mocked to
         // ensure that the chip view is added to a parent view
         whenever(sbWindowController.addViewToWindow(any(), any())).then {
@@ -93,8 +95,8 @@
         controller =
             SystemEventChipAnimationController(
                 context = mContext,
-                statusBarWindowController = sbWindowController,
-                contentInsetsProvider = insetsProvider
+                statusBarWindowControllerStore = sbWindowControllerStore,
+                contentInsetsProvider = insetsProvider,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
index 35e4047..97fa6eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
@@ -31,6 +31,7 @@
 @RunWith(AndroidJUnit4::class)
 class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
     private var isStarted = false
+    private var wasStarted = false
     private var scrimOffset = 0f
     private var contentHeight = 0f
     private var isCurrentGestureOverscroll = false
@@ -46,7 +47,10 @@
             minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT },
             isCurrentGestureOverscroll = { isCurrentGestureOverscroll },
             onStart = { isStarted = true },
-            onStop = { isStarted = false },
+            onStop = {
+                wasStarted = true
+                isStarted = false
+            },
         )
 
     @Test
@@ -180,6 +184,7 @@
             )
 
         assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+        assertThat(wasStarted).isEqualTo(false)
         assertThat(isStarted).isEqualTo(false)
     }
 
@@ -196,7 +201,9 @@
             )
 
         assertThat(offsetConsumed).isEqualTo(Offset.Zero)
-        assertThat(isStarted).isEqualTo(true)
+        // Returning 0 offset will immediately stop the connection
+        assertThat(wasStarted).isEqualTo(true)
+        assertThat(isStarted).isEqualTo(false)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreMocks.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/FakeNodeController.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/FakeNodeController.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/FakeNodeController.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/FakeNodeController.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLoggerFake.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index d04d6fc..0947cd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -45,12 +45,12 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.testing.UiThreadTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 22f1e46..6435e82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -24,9 +24,9 @@
 
 import android.provider.Settings;
 import android.testing.TestableResources;
-import android.testing.UiThreadTest;
 import android.util.KeyValueListParser;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/NotificationViewFlipperViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 95db95c..789701f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -13,6 +13,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static com.android.systemui.Flags.FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -36,6 +38,7 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.service.notification.StatusBarNotification;
 import android.testing.TestableLooper;
@@ -52,6 +55,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 
@@ -85,6 +89,7 @@
     private NotificationMenuRowPlugin mMenuRow;
     private Handler mHandler;
     private ExpandableNotificationRow mNotificationRow;
+    private NotificationShelf mShelf;
     private Runnable mFalsingCheck;
     private final FeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
@@ -111,6 +116,7 @@
         mEvent = mock(MotionEvent.class);
         mMenuRow = mock(NotificationMenuRowPlugin.class);
         mNotificationRow = mock(ExpandableNotificationRow.class);
+        mShelf = mock(NotificationShelf.class);
         mHandler = mock(Handler.class);
         mFalsingCheck = mock(Runnable.class);
     }
@@ -665,6 +671,54 @@
     }
 
     @Test
+    @EnableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF)
+    public void testIsTouchInView_notificationShelf_flagEnabled() {
+        doReturn(500).when(mShelf).getWidth();
+        doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight();
+
+        Answer answer = (Answer) invocation -> {
+            int[] arr = invocation.getArgument(0);
+            arr[0] = 0;
+            arr[1] = 0;
+            return null;
+        };
+
+        doReturn(5f).when(mEvent).getRawX();
+        doReturn(10f).when(mEvent).getRawY();
+        doAnswer(answer).when(mShelf).getLocationOnScreen(any());
+        assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+
+        doReturn(50f).when(mEvent).getRawX();
+        assertFalse("Touch is not within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+    }
+
+    @Test
+    @DisableFlags(FLAG_IGNORE_TOUCHES_NEXT_TO_NOTIFICATION_SHELF)
+    public void testIsTouchInView_notificationShelf_flagDisabled() {
+        doReturn(500).when(mShelf).getWidth();
+        doReturn(FAKE_ROW_WIDTH).when(mShelf).getActualWidth();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getHeight();
+        doReturn(FAKE_ROW_HEIGHT).when(mShelf).getActualHeight();
+
+        Answer answer = (Answer) invocation -> {
+            int[] arr = invocation.getArgument(0);
+            arr[0] = 0;
+            arr[1] = 0;
+            return null;
+        };
+
+        doReturn(5f).when(mEvent).getRawX();
+        doReturn(10f).when(mEvent).getRawY();
+        doAnswer(answer).when(mShelf).getLocationOnScreen(any());
+        assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+
+        doReturn(50f).when(mEvent).getRawX();
+        assertTrue("Touch is within the view", mSwipeHelper.isTouchInView(mEvent, mShelf));
+    }
+
+    @Test
     public void testContentAlphaRemainsUnchangedWhenNotificationIsNotDismissible() {
         doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 8d678ef..bf14472 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -21,7 +21,6 @@
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
-import com.android.app.tracing.coroutines.flow.map
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.DisableSceneContainer
@@ -51,6 +50,7 @@
 import com.android.systemui.util.ui.value
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLoggerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index 1797995..bac79a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -94,6 +95,7 @@
     @Mock private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
     @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
     @Mock private lateinit var statusBarWindowController: StatusBarWindowController
+    @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore
     @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController
     @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
     @Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -112,6 +114,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        `when`(statusBarWindowControllerStore.defaultDisplay).thenReturn(statusBarWindowController)
         underTest =
             LegacyActivityStarterInternalImpl(
                 centralSurfacesOptLazy = { Optional.of(centralSurfaces) },
@@ -128,7 +131,7 @@
                 context = context,
                 displayId = DISPLAY_ID,
                 lockScreenUserManager = lockScreenUserManager,
-                statusBarWindowController = statusBarWindowController,
+                statusBarWindowControllerStore = statusBarWindowControllerStore,
                 wakefulnessLifecycle = wakefulnessLifecycle,
                 keyguardStateController = keyguardStateController,
                 statusBarStateController = statusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index c804fc6..ba5fb7f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -98,7 +98,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
@@ -135,7 +135,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
@@ -164,8 +164,7 @@
         val chipWidth = 30
         val dotWidth = 10
         val isRtl = false
-        val contentRect =
-            Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
+        val contentRect = Rect(/* left= */ 0, /* top= */ 10, /* right= */ 1000, /* bottom= */ 100)
 
         val chipBounds =
             getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
@@ -207,7 +206,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -228,7 +227,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -251,7 +250,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -263,7 +262,7 @@
                 minLeftPadding,
                 0,
                 screenBounds.height() - dcBounds.height() - dotWidth,
-                sbHeightLandscape
+                sbHeightLandscape,
             )
 
         bounds =
@@ -278,7 +277,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -320,7 +319,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -331,7 +330,7 @@
                 protectionBounds.bottom,
                 0,
                 screenBounds.height() - minRightPadding,
-                sbHeightLandscape
+                sbHeightLandscape,
             )
 
         bounds =
@@ -346,7 +345,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -369,7 +368,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -381,7 +380,7 @@
                 minLeftPadding,
                 0,
                 screenBounds.height() - protectionBounds.bottom - dotWidth,
-                sbHeightLandscape
+                sbHeightLandscape,
             )
 
         bounds =
@@ -396,7 +395,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -415,7 +414,7 @@
                 left = screenBounds.right - dcWidth,
                 top = 0,
                 right = screenBounds.right,
-                bottom = dcHeight
+                bottom = dcHeight,
             )
         val dcBoundsLandscape = Rect(left = 0, top = 0, right = dcHeight, bottom = dcWidth)
         val dcBoundsSeascape =
@@ -423,14 +422,14 @@
                 left = screenBounds.right - dcHeight,
                 top = screenBounds.bottom - dcWidth,
                 right = screenBounds.right - dcHeight,
-                bottom = screenBounds.bottom - dcWidth
+                bottom = screenBounds.bottom - dcWidth,
             )
         val dcBoundsUpsideDown =
             Rect(
                 left = 0,
                 top = screenBounds.bottom - dcHeight,
                 right = dcWidth,
-                bottom = screenBounds.bottom - dcHeight
+                bottom = screenBounds.bottom - dcHeight,
             )
         val minLeftPadding = 20
         val minRightPadding = 20
@@ -448,7 +447,7 @@
                 left = minLeftPadding,
                 top = 0,
                 right = dcBoundsPortrait.left - dotWidth,
-                bottom = sbHeightPortrait
+                bottom = sbHeightPortrait,
             )
 
         var bounds =
@@ -463,7 +462,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -475,7 +474,7 @@
                 left = dcBoundsLandscape.height(),
                 top = 0,
                 right = screenBounds.height() - minRightPadding,
-                bottom = sbHeightLandscape
+                bottom = sbHeightLandscape,
             )
 
         bounds =
@@ -490,7 +489,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -502,7 +501,7 @@
                 left = minLeftPadding,
                 top = 0,
                 right = screenBounds.width() - minRightPadding,
-                bottom = sbHeightPortrait
+                bottom = sbHeightPortrait,
             )
 
         bounds =
@@ -517,7 +516,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -529,7 +528,7 @@
                 left = minLeftPadding,
                 top = 0,
                 right = screenBounds.height() - minRightPadding,
-                bottom = sbHeightLandscape
+                bottom = sbHeightLandscape,
             )
 
         bounds =
@@ -544,7 +543,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -584,7 +583,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -595,7 +594,7 @@
                 protectionBounds.bottom,
                 0,
                 screenBounds.height() - minRightPadding,
-                sbHeightLandscape
+                sbHeightLandscape,
             )
 
         bounds =
@@ -610,7 +609,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -633,7 +632,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -645,7 +644,7 @@
                 minLeftPadding,
                 0,
                 screenBounds.height() - protectionBounds.bottom - dotWidth,
-                sbHeightLandscape
+                sbHeightLandscape,
             )
 
         bounds =
@@ -660,7 +659,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -682,7 +681,7 @@
                 isRtl = false,
                 dotWidth = 10,
                 bottomAlignedMargin = BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight = 15
+                statusBarContentHeight = 15,
             )
 
         assertThat(bounds.top).isEqualTo(0)
@@ -704,7 +703,7 @@
                 isRtl = false,
                 dotWidth = 10,
                 bottomAlignedMargin = 5,
-                statusBarContentHeight = 15
+                statusBarContentHeight = 15,
             )
 
         // Content in the status bar is centered vertically. To achieve the bottom margin we want,
@@ -756,7 +755,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -777,7 +776,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -798,7 +797,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -809,7 +808,7 @@
                 minLeftPadding,
                 0,
                 screenBounds.height() - dcBounds.height() - dotWidth,
-                sbHeightLandscape
+                sbHeightLandscape,
             )
 
         bounds =
@@ -824,7 +823,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -860,7 +859,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
@@ -880,7 +879,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
@@ -900,7 +899,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
 
@@ -920,7 +919,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
     }
@@ -958,7 +957,7 @@
                 isRtl,
                 dotWidth,
                 BOTTOM_ALIGNED_MARGIN_NONE,
-                statusBarContentHeight
+                statusBarContentHeight,
             )
 
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -968,12 +967,12 @@
     fun testDisplayChanged_returnsUpdatedInsets() {
         // GIVEN: get insets on the first display and switch to the second display
         val provider =
-            StatusBarContentInsetsProvider(
+            StatusBarContentInsetsProviderImpl(
                 contextMock,
                 configurationController,
                 mock<DumpManager>(),
                 mock<CommandRegistry>(),
-                mock<SysUICutoutProvider>()
+                mock<SysUICutoutProvider>(),
             )
 
         configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
@@ -993,12 +992,12 @@
         // GIVEN: get insets on the first display, switch to the second display,
         // get insets and switch back
         val provider =
-            StatusBarContentInsetsProvider(
+            StatusBarContentInsetsProviderImpl(
                 contextMock,
                 configurationController,
                 mock<DumpManager>(),
                 mock<CommandRegistry>(),
-                mock<SysUICutoutProvider>()
+                mock<SysUICutoutProvider>(),
             )
 
         configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
@@ -1024,12 +1023,12 @@
         configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
         configurationController.onConfigurationChanged(configuration)
         val provider =
-            StatusBarContentInsetsProvider(
+            StatusBarContentInsetsProviderImpl(
                 contextMock,
                 configurationController,
                 mock<DumpManager>(),
                 mock<CommandRegistry>(),
-                mock<SysUICutoutProvider>()
+                mock<SysUICutoutProvider>(),
             )
         val listener =
             object : StatusBarContentInsetsChangedListener {
@@ -1053,12 +1052,12 @@
     fun onDensityOrFontScaleChanged_listenerNotified() {
         configuration.densityDpi = 12
         val provider =
-            StatusBarContentInsetsProvider(
+            StatusBarContentInsetsProviderImpl(
                 contextMock,
                 configurationController,
                 mock<DumpManager>(),
                 mock<CommandRegistry>(),
-                mock<SysUICutoutProvider>()
+                mock<SysUICutoutProvider>(),
             )
         val listener =
             object : StatusBarContentInsetsChangedListener {
@@ -1081,12 +1080,12 @@
     @Test
     fun onThemeChanged_listenerNotified() {
         val provider =
-            StatusBarContentInsetsProvider(
+            StatusBarContentInsetsProviderImpl(
                 contextMock,
                 configurationController,
                 mock<DumpManager>(),
                 mock<CommandRegistry>(),
-                mock<SysUICutoutProvider>()
+                mock<SysUICutoutProvider>(),
             )
         val listener =
             object : StatusBarContentInsetsChangedListener {
@@ -1108,13 +1107,13 @@
         expected: Rect,
         actual: Rect,
         @Rotation currentRotation: Int,
-        @Rotation targetRotation: Int
+        @Rotation targetRotation: Int,
     ) {
         assertTrue(
             "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
                 " targetRotation=${RotationUtils.toString(targetRotation)}" +
                 " expected=$expected actual=$actual",
-            expected.equals(actual)
+            expected.equals(actual),
         )
     }
 
@@ -1126,7 +1125,7 @@
         left: Rect = Rect(),
         top: Rect = Rect(),
         right: Rect = Rect(),
-        bottom: Rect = Rect()
+        bottom: Rect = Rect(),
     ) {
         whenever(dc.boundingRects)
             .thenReturn(listOf(left, top, right, bottom).filter { !it.isEmpty })
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
similarity index 78%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1d74331..94753f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -34,7 +34,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -43,9 +42,7 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.service.trust.TrustAgentService;
 import android.testing.TestableLooper;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
@@ -67,7 +64,6 @@
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.TrustGrantFlags;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
@@ -580,22 +576,6 @@
     }
 
     @Test
-    @DisableSceneContainer
-    @DisableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void testShowAlternateBouncer_unlockingWithBiometricAllowed() {
-        // GIVEN will show alternate bouncer
-        when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false);
-        when(mAlternateBouncerInteractor.show()).thenReturn(true);
-
-        // WHEN showGenericBouncer is called
-        mStatusBarKeyguardViewManager.showBouncer(true);
-
-        // THEN alt auth bouncer is shown
-        verify(mAlternateBouncerInteractor).show();
-        verify(mPrimaryBouncerInteractor, never()).show(anyBoolean());
-    }
-
-    @Test
     public void testUpdateResources_delegatesToBouncer() {
         mStatusBarKeyguardViewManager.updateResources();
 
@@ -841,145 +821,6 @@
     }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // THEN the touch is not acted upon
-        verify(mCallback, never()).onTouch(any());
-    }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
-        // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // THEN the touch is not intercepted
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-    }
-
-    @Test
-    public void handleDispatchTouchEvent_alternateBouncerNotVisible() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
-        // THEN handleDispatchTouchEvent doesn't use the touches
-        assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-
-        // THEN the touch is not acted upon
-        verify(mCallback, never()).onTouch(any());
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void handleDispatchTouchEvent_shouldInterceptTouchAndHandleTouch() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // GIVEN all touches are NOT the udfps overlay
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
-        // to its child views (handleDispatchTouchEvent returns true)
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-
-        // THEN the touch is acted upon once for each dispatchTOuchEvent call
-        verify(mCallback, times(3)).onTouch(any());
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void handleDispatchTouchEvent_shouldInterceptTouchButNotHandleTouch() {
-        mStatusBarKeyguardViewManager.addCallback(mCallback);
-
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // GIVEN all touches are within the udfps overlay
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(true);
-
-        // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
-        // to its child views (handleDispatchTouchEvent returns true)
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-
-        // THEN the touch is NOT acted upon at the moment
-        verify(mCallback, never()).onTouch(any());
-    }
-
-    @Test
-    @DisableSceneContainer
-    public void shouldInterceptTouch_alternateBouncerNotVisible() {
-        // GIVEN the alternate bouncer is not visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
-        // THEN no motion events are intercepted
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void shouldInterceptTouch_alternateBouncerVisible() {
-        // GIVEN the alternate bouncer is visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // THEN all motion events are intercepted
-        assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
-        ));
-        assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
-        ));
-    }
-
-    @Test
     public void alternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() {
         // GIVEN the alternate bouncer has shown and calls to hide()  will result in successfully
         // hiding it
@@ -997,106 +838,6 @@
 
     @Test
     @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void alternateBouncerOnTouch_actionDownThenUp_noMinTimeShown_noHideAltBouncer() {
-        reset(mAlternateBouncerInteractor);
-
-        // GIVEN the alternate bouncer has shown for a minimum amount of time
-        when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(false);
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // WHEN ACTION_DOWN and ACTION_UP touch event comes
-        boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
-        when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
-        boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
-        // THEN the touches are handled (doesn't let touches through to underlying views)
-        assertTrue(touchHandledDown);
-        assertTrue(touchHandledUp);
-
-        // THEN alternate bouncer does NOT attempt to hide since min showing time wasn't met
-        verify(mAlternateBouncerInteractor, never()).hide();
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void alternateBouncerOnTouch_actionDownThenUp_handlesTouch_hidesAltBouncer() {
-        reset(mAlternateBouncerInteractor);
-
-        // GIVEN the alternate bouncer has shown for a minimum amount of time
-        when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // WHEN ACTION_DOWN and ACTION_UP touch event comes
-        boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
-        when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
-        boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
-        // THEN the touches are handled
-        assertTrue(touchHandledDown);
-        assertTrue(touchHandledUp);
-
-        // THEN alternate bouncer attempts to hide
-        verify(mAlternateBouncerInteractor).hide();
-    }
-
-    @Test
-    @DisableSceneContainer
-    public void alternateBouncerOnTouch_actionUp_doesNotHideAlternateBouncer() {
-        reset(mAlternateBouncerInteractor);
-
-        // GIVEN the alternate bouncer has shown for a minimum amount of time
-        when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
-        // WHEN only ACTION_UP touch event comes
-        mStatusBarKeyguardViewManager.onTouch(
-                MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
-        // THEN the alternateBouncer doesn't hide
-        verify(mAlternateBouncerInteractor, never()).hide();
-    }
-
-    @Test
-    @DisableSceneContainer
-    @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() {
-        // GIVEN keyguard update monitor callback is registered
-        verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture());
-
-        reset(mKeyguardUpdateMonitor);
-        reset(mKeyguardMessageAreaController);
-
-        // GIVEN alternate bouncer state = not visible
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
-        // WHEN the device is trusted by active unlock
-        mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser(
-                true,
-                true,
-                new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
-                        | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE),
-                null
-        );
-
-        // THEN the false visibility state is propagated to the keyguardUpdateMonitor
-        verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false));
-
-        // THEN message area visibility updated to FALSE with empty message
-        verify(mKeyguardMessageAreaController).setIsVisible(eq(false));
-        verify(mKeyguardMessageAreaController).setMessage(eq(""));
-    }
-
-    @Test
-    @DisableSceneContainer
     @DisableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART)
     public void testShowBouncerOrKeyguard_needsFullScreen() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
index 597e2e4..e0d9fac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt
@@ -50,6 +50,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
@@ -74,6 +75,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 private const val CALL_UID = 900
 
@@ -106,6 +108,7 @@
     @Mock private lateinit var mockActivityStarter: ActivityStarter
     @Mock private lateinit var mockIActivityManager: IActivityManager
     @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
+    @Mock private lateinit var mockStatusBarWindowControllerStore: StatusBarWindowControllerStore
 
     private lateinit var chipView: View
 
@@ -118,6 +121,8 @@
 
         MockitoAnnotations.initMocks(this)
         val notificationCollection = mock(CommonNotifCollection::class.java)
+        whenever(mockStatusBarWindowControllerStore.defaultDisplay)
+            .thenReturn(mockStatusBarWindowController)
 
         controller =
             OngoingCallController(
@@ -131,7 +136,7 @@
                 mainExecutor,
                 mockIActivityManager,
                 DumpManager(),
-                mockStatusBarWindowController,
+                mockStatusBarWindowControllerStore,
                 mockSwipeStatusBarAwayGestureHandler,
                 statusBarModeRepository,
                 logcatLogBuffer("OngoingCallControllerViaListenerTest"),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
index dfe01bf..2ad50cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -93,6 +94,7 @@
     private val mockActivityStarter = kosmos.activityStarter
     private val mockIActivityManager = mock<IActivityManager>()
     private val mockStatusBarWindowController = mock<StatusBarWindowController>()
+    private val mockStatusBarWindowControllerStore = mock<StatusBarWindowControllerStore>()
 
     private lateinit var chipView: View
 
@@ -103,6 +105,8 @@
             chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
         }
 
+        whenever(mockStatusBarWindowControllerStore.defaultDisplay)
+            .thenReturn(mockStatusBarWindowController)
         controller =
             OngoingCallController(
                 testScope.backgroundScope,
@@ -115,7 +119,7 @@
                 mainExecutor,
                 mockIActivityManager,
                 DumpManager(),
-                mockStatusBarWindowController,
+                mockStatusBarWindowControllerStore,
                 mockSwipeStatusBarAwayGestureHandler,
                 statusBarModeRepository,
                 logcatLogBuffer("OngoingCallControllerViaRepoTest"),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconListTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconListTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconListTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconListTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/FakeDeviceBasedSatelliteViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/FakeDeviceBasedSatelliteViewModel.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/FakeDeviceBasedSatelliteViewModel.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/FakeDeviceBasedSatelliteViewModel.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
index 5036e77..46f822a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
@@ -21,6 +21,7 @@
 import android.app.StatusBarManager.DISABLE_NONE
 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
 import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -31,8 +32,10 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith;
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class CollapsedStatusBarInteractorTest : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewBinder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
similarity index 84%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewBinder.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
index 2ee928f..cdc7aa2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt
@@ -17,21 +17,21 @@
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
 import android.view.View
-import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener
 
 /**
  * A fake view binder that can be used from Java tests.
  *
  * Since Java tests can't run tests within test scopes, we need to bypass the flows from
- * [CollapsedStatusBarViewModel] and just trigger the listener directly.
+ * [HomeStatusBarViewModel] and just trigger the listener directly.
  */
-class FakeCollapsedStatusBarViewBinder : CollapsedStatusBarViewBinder {
+class FakeHomeStatusBarViewBinder : HomeStatusBarViewBinder {
     var listener: StatusBarVisibilityChangeListener? = null
 
     override fun bind(
         view: View,
-        viewModel: CollapsedStatusBarViewModel,
+        viewModel: HomeStatusBarViewModel,
         listener: StatusBarVisibilityChangeListener,
     ) {
         this.listener = listener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index cc90c11..02c1540 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -23,7 +23,7 @@
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 
-class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel {
+class FakeHomeStatusBarViewModel : HomeStatusBarViewModel {
     private val areNotificationLightsOut = MutableStateFlow(false)
 
     override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false)
@@ -39,7 +39,7 @@
 
     override val isClockVisible =
         MutableStateFlow(
-            CollapsedStatusBarViewModel.VisibilityModel(
+            HomeStatusBarViewModel.VisibilityModel(
                 visibility = View.GONE,
                 shouldAnimateChange = false,
             )
@@ -47,7 +47,7 @@
 
     override val isNotificationIconContainerVisible =
         MutableStateFlow(
-            CollapsedStatusBarViewModel.VisibilityModel(
+            HomeStatusBarViewModel.VisibilityModel(
                 visibility = View.GONE,
                 shouldAnimateChange = false,
             )
@@ -55,7 +55,7 @@
 
     override val isSystemInfoVisible =
         MutableStateFlow(
-            CollapsedStatusBarViewModel.VisibilityModel(
+            HomeStatusBarViewModel.VisibilityModel(
                 visibility = View.GONE,
                 shouldAnimateChange = false,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index bd85780..b3a73d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
 import android.app.StatusBarManager.DISABLE2_NONE
 import android.app.StatusBarManager.DISABLE_CLOCK
 import android.app.StatusBarManager.DISABLE_NONE
@@ -33,6 +34,7 @@
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -75,7 +77,7 @@
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
-class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
+class HomeStatusBarViewModelImplTest : SysuiTestCase() {
     private val kosmos =
         Kosmos().also {
             it.testCase = this
@@ -89,13 +91,13 @@
     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
     private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository
 
-    private lateinit var underTest: CollapsedStatusBarViewModel
+    private lateinit var underTest: HomeStatusBarViewModel
 
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
         // Initialize here because some flags are checked when this class is constructed
-        underTest = kosmos.collapsedStatusBarViewModel
+        underTest = kosmos.homeStatusBarViewModel
     }
 
     @Test
@@ -746,6 +748,45 @@
             assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
         }
 
+    @Test
+    @DisableSceneContainer
+    fun secureCameraActive_sceneFlagOff_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            // Secure camera is an occluding activity
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.OCCLUDED,
+                testScope = this,
+            )
+            kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun secureCameraActive_sceneFlagOn_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
+            // Secure camera is an occluding activity
+            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null)
+            kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
         ActiveNotificationsStore.Builder()
             .apply { notifications.forEach(::addIndividualNotif) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BlockingQueueIntentReceiver.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BlockingQueueIntentReceiver.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BlockingQueueIntentReceiver.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BlockingQueueIntentReceiver.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 2588f1f..e3bd885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -61,7 +61,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class BluetoothControllerImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ClockTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ClockTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt
new file mode 100644
index 0000000..fd89581
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.app.AutomaticZenRule
+import android.app.NotificationManager
+import android.net.Uri
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@EnableFlags(android.app.Flags.FLAG_MODES_UI)
+class ZenModesCleanupStartableTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    @Mock private lateinit var notificationManager: NotificationManager
+
+    private lateinit var underTest: ZenModesCleanupStartable
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            ZenModesCleanupStartable(
+                testScope.backgroundScope,
+                kosmos.backgroundCoroutineContext,
+                notificationManager,
+            )
+    }
+
+    @Test
+    fun start_withGamingModeZenRule_deletesIt() =
+        testScope.runTest {
+            whenever(notificationManager.automaticZenRules)
+                .thenReturn(
+                    mutableMapOf(
+                        Pair(
+                            "gaming",
+                            AutomaticZenRule.Builder(
+                                    "Gaming Mode",
+                                    Uri.parse(
+                                        "android-app://com.android.systemui/game-mode-dnd-controller"
+                                    ),
+                                )
+                                .setPackage("com.android.systemui")
+                                .build(),
+                        ),
+                        Pair(
+                            "other",
+                            AutomaticZenRule.Builder("Other Mode", Uri.parse("something-else"))
+                                .setPackage("com.other.package")
+                                .build(),
+                        ),
+                    )
+                )
+
+            underTest.start()
+            runCurrent()
+
+            verify(notificationManager).removeAutomaticZenRule(eq("gaming"))
+        }
+
+    @Test
+    fun start_withoutGamingModeZenRule_doesNothing() =
+        testScope.runTest {
+            whenever(notificationManager.automaticZenRules)
+                .thenReturn(
+                    mutableMapOf(
+                        Pair(
+                            "other",
+                            AutomaticZenRule.Builder("Other Mode", Uri.parse("something-else"))
+                                .setPackage("com.android.systemui")
+                                .build(),
+                        )
+                    )
+                )
+
+            underTest.start()
+            runCurrent()
+
+            verify(notificationManager, never()).removeAutomaticZenRule(any())
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/FakeBluetoothRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/bluetooth/FakeBluetoothRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/FakeBluetoothRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/bluetooth/FakeBluetoothRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index c5ccf9e..74d4178 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -18,6 +18,8 @@
 
 import android.app.AutomaticZenRule
 import android.app.Flags
+import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
+import android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY
 import android.app.NotificationManager.Policy
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
@@ -25,6 +27,7 @@
 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
 import android.service.notification.SystemZenRules
+import android.service.notification.ZenPolicy
 import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -383,6 +386,120 @@
         }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    fun activeModesBlockingEverything_hasModesWithFilterNone() =
+        testScope.runTest {
+            val blockingEverything by collectLastValue(underTest.activeModesBlockingEverything)
+
+            zenModeRepository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("Filter=None, Not active")
+                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Filter=Priority, Active")
+                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Filter=None, Active")
+                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Filter=None, Active Too")
+                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+                        .setActive(true)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(blockingEverything!!.mainMode!!.name).isEqualTo("Filter=None, Active")
+            assertThat(blockingEverything!!.modeNames)
+                .containsExactly("Filter=None, Active", "Filter=None, Active Too")
+                .inOrder()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    fun activeModesBlockingMedia_hasModesWithPolicyBlockingMedia() =
+        testScope.runTest {
+            val blockingMedia by collectLastValue(underTest.activeModesBlockingMedia)
+
+            zenModeRepository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("Blocks media, Not active")
+                        .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Allows media, Active")
+                        .setZenPolicy(ZenPolicy.Builder().allowMedia(true).build())
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Blocks media, Active")
+                        .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Blocks media, Active Too")
+                        .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+                        .setActive(true)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(blockingMedia!!.mainMode!!.name).isEqualTo("Blocks media, Active")
+            assertThat(blockingMedia!!.modeNames)
+                .containsExactly("Blocks media, Active", "Blocks media, Active Too")
+                .inOrder()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    fun activeModesBlockingAlarms_hasModesWithPolicyBlockingAlarms() =
+        testScope.runTest {
+            val blockingAlarms by collectLastValue(underTest.activeModesBlockingAlarms)
+
+            zenModeRepository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("Blocks alarms, Not active")
+                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Allows alarms, Active")
+                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(true).build())
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Blocks alarms, Active")
+                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Blocks alarms, Active Too")
+                        .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
+                        .setActive(true)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(blockingAlarms!!.mainMode!!.name).isEqualTo("Blocks alarms, Active")
+            assertThat(blockingAlarms!!.modeNames)
+                .containsExactly("Blocks alarms, Active", "Blocks alarms, Active Too")
+                .inOrder()
+        }
+
+    @Test
     @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
     fun modesHidingNotifications_onlyIncludesModesWithNotifListSuppression() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
index 9d93a9c..39836e2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
@@ -19,6 +19,7 @@
 package com.android.systemui.statusbar.policy.ui.dialog.viewmodel
 
 import android.content.Intent
+import android.content.applicationContext
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -38,9 +39,11 @@
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.clearInvocations
+import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
@@ -55,14 +58,20 @@
     private val mockDialogDelegate = kosmos.mockModesDialogDelegate
     private val mockDialogEventLogger = kosmos.mockModesDialogEventLogger
 
-    private val underTest =
-        ModesDialogViewModel(
-            context,
-            interactor,
-            kosmos.testDispatcher,
-            mockDialogDelegate,
-            mockDialogEventLogger,
-        )
+    private lateinit var underTest: ModesDialogViewModel
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            ModesDialogViewModel(
+                kosmos.applicationContext,
+                interactor,
+                kosmos.testDispatcher,
+                kosmos.mockModesDialogDelegate,
+                kosmos.mockModesDialogEventLogger,
+            )
+    }
 
     @Test
     fun tiles_filtersOutUserDisabledModes() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt
new file mode 100644
index 0000000..6e66287
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImplTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.window
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.policy.statusBarConfigurationController
+import com.android.systemui.testKosmos
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarWindowControllerImplTest : SysuiTestCase() {
+
+    private val kosmos =
+        testKosmos().also { it.statusBarWindowViewInflater = it.fakeStatusBarWindowViewInflater }
+
+    private val underTest = kosmos.statusBarWindowControllerImpl
+    private val fakeStatusBarWindowViewInflater = kosmos.fakeStatusBarWindowViewInflater
+    private val statusBarConfigurationController = kosmos.statusBarConfigurationController
+
+    @Test
+    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun attach_connectedDisplaysFlagEnabled_setsConfigControllerOnWindowView() {
+        val windowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first()
+
+        underTest.attach()
+
+        verify(windowView).setStatusBarConfigurationController(statusBarConfigurationController)
+    }
+
+    @Test
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun attach_connectedDisplaysFlagDisabled_doesNotSetConfigControllerOnWindowView() {
+        val mockWindowView = fakeStatusBarWindowViewInflater.inflatedMockViews.first()
+
+        underTest.attach()
+
+        verify(mockWindowView, never()).setStatusBarConfigurationController(any())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowViewTest.kt
new file mode 100644
index 0000000..9917f99
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/StatusBarWindowViewTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.content.res.Configuration
+import android.view.LayoutInflater
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarWindowViewTest : SysuiTestCase() {
+
+    private val underTest =
+        LayoutInflater.from(context).inflate(R.layout.super_status_bar, /* root= */ null)
+            as StatusBarWindowView
+
+    @Test
+    fun onConfigurationChanged_configurationControllerSet_forwardsCall() {
+        val configuration = Configuration()
+        val configurationController = mock<StatusBarConfigurationController>()
+        underTest.setStatusBarConfigurationController(configurationController)
+
+        underTest.onConfigurationChanged(configuration)
+
+        verify(configurationController).onConfigurationChanged(configuration)
+    }
+
+    @Test
+    fun onConfigurationChanged_configurationControllerNotSet_doesNotCrash() {
+        val configuration = Configuration()
+
+        underTest.onConfigurationChanged(configuration)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
index 0c27e58..c7c7fdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.app.StatusBarManager.WINDOW_STATE_HIDING
 import android.app.StatusBarManager.WINDOW_STATE_SHOWING
 import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -34,11 +35,13 @@
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.argumentCaptor
 
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
 class StatusBarWindowStatePerDisplayRepositoryTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
index b6a3f36..e23e88c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
@@ -19,6 +19,7 @@
 import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
 import android.app.StatusBarManager.WINDOW_STATE_SHOWING
 import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -33,12 +34,14 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.reset
 
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
 class StatusBarWindowStateRepositoryStoreTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
similarity index 70%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
index ba9fa92..29e9ba7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
@@ -25,19 +25,22 @@
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 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 BackGestureMonitorTest : SysuiTestCase() {
+class BackGestureRecognizerTest : SysuiTestCase() {
 
     private var gestureState: GestureState = NotStarted
-    private val gestureMonitor =
-        BackGestureMonitor(
-            gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
-            gestureStateChangedCallback = { gestureState = it },
-        )
+    private val gestureRecognizer =
+        BackGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+
+    @Before
+    fun before() {
+        gestureRecognizer.addGestureStateCallback { gestureState = it }
+    }
 
     @Test
     fun triggersGestureFinishedForThreeFingerGestureRight() {
@@ -58,6 +61,26 @@
     }
 
     @Test
+    fun triggersProgressRelativeToDistance() {
+        assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+        assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+        assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE, expectedProgress = 1f)
+        assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE, expectedProgress = 1f)
+    }
+
+    private fun assertProgressWhileMovingFingers(deltaX: Float, expectedProgress: Float) {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
+            expectedState = InProgress(progress = expectedProgress),
+        )
+    }
+
+    @Test
+    fun triggeredProgressIsNoBiggerThanOne() {
+        assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE * 2, expectedProgress = 1f)
+    }
+
+    @Test
     fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
         assertStateAfterEvents(
             events = ThreeFingerGesture.swipeLeft(distancePx = SWIPE_DISTANCE / 2),
@@ -82,7 +105,7 @@
     }
 
     private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
-        events.forEach { gestureMonitor.processTouchpadEvent(it) }
+        events.forEach { gestureRecognizer.accept(it) }
         assertThat(gestureState).isEqualTo(expectedState)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
index a83ed56..ff0cec5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
@@ -36,10 +36,7 @@
     private var triggered = false
     private val handler =
         TouchpadGestureHandler(
-            BackGestureMonitor(
-                gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
-                gestureStateChangedCallback = {},
-            ),
+            BackGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt()),
             EasterEggGestureMonitor(callback = { triggered = true }),
         )
 
@@ -107,7 +104,8 @@
     }
 
     private fun assertStateAfterTwoFingerGesture(gesturePath: List<Point>, wasTriggered: Boolean) {
-        val events = TwoFingerGesture.createEvents { gesturePath.forEach { (x, y) -> move(x, y) } }
+        val events =
+            TwoFingerGesture.eventsForFullGesture { gesturePath.forEach { (x, y) -> move(x, y) } }
         assertStateAfterEvents(events = events, wasTriggered = wasTriggered)
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
similarity index 70%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
index 59cc026..7d3ed92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
@@ -25,19 +25,22 @@
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 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 HomeGestureMonitorTest : SysuiTestCase() {
+class HomeGestureRecognizerTest : SysuiTestCase() {
 
     private var gestureState: GestureState = GestureState.NotStarted
-    private val gestureMonitor =
-        HomeGestureMonitor(
-            gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
-            gestureStateChangedCallback = { gestureState = it },
-        )
+    private val gestureRecognizer =
+        HomeGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+
+    @Before
+    fun before() {
+        gestureRecognizer.addGestureStateCallback { gestureState = it }
+    }
 
     @Test
     fun triggersGestureFinishedForThreeFingerGestureUp() {
@@ -53,6 +56,27 @@
     }
 
     @Test
+    fun triggersProgressRelativeToDistance() {
+        assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+        assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f)
+    }
+
+    private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
+            expectedState = InProgress(progress = expectedProgress),
+        )
+    }
+
+    @Test
+    fun triggeredProgressIsBetweenZeroAndOne() {
+        // going in the wrong direction
+        assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f)
+        // going further than required distance
+        assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
+    }
+
+    @Test
     fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
         assertStateAfterEvents(
             events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
@@ -78,7 +102,7 @@
     }
 
     private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
-        events.forEach { gestureMonitor.processTouchpadEvent(it) }
+        events.forEach { gestureRecognizer.accept(it) }
         assertThat(gestureState).isEqualTo(expectedState)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
similarity index 72%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
index 7eac6bb..c5c0d59 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 import com.google.common.truth.Truth.assertThat
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.doReturn
@@ -34,7 +35,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class RecentAppsGestureMonitorTest : SysuiTestCase() {
+class RecentAppsGestureRecognizerTest : SysuiTestCase() {
 
     companion object {
         const val THRESHOLD_VELOCITY_PX_PER_MS = 0.1f
@@ -44,19 +45,23 @@
     }
 
     private var gestureState: GestureState = GestureState.NotStarted
-    private val velocityTracker =
+    private val velocityTracker1D =
         mock<VelocityTracker1D> {
             // by default return correct speed for the gesture - as if pointer is slowing down
             on { calculateVelocity() } doReturn SLOW
         }
-    private val gestureMonitor =
-        RecentAppsGestureMonitor(
+    private val gestureRecognizer =
+        RecentAppsGestureRecognizer(
             gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
-            gestureStateChangedCallback = { gestureState = it },
             velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS,
-            velocityTracker = velocityTracker,
+            velocityTracker = VerticalVelocityTracker(velocityTracker1D),
         )
 
+    @Before
+    fun before() {
+        gestureRecognizer.addGestureStateCallback { gestureState = it }
+    }
+
     @Test
     fun triggersGestureFinishedForThreeFingerGestureUp() {
         assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished)
@@ -64,7 +69,7 @@
 
     @Test
     fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() {
-        whenever(velocityTracker.calculateVelocity()).thenReturn(FAST)
+        whenever(velocityTracker1D.calculateVelocity()).thenReturn(FAST)
         assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
     }
 
@@ -72,11 +77,32 @@
     fun triggersGestureProgressForThreeFingerGestureStarted() {
         assertStateAfterEvents(
             events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
-            expectedState = InProgress(),
+            expectedState = InProgress(progress = 0f),
         )
     }
 
     @Test
+    fun triggersProgressRelativeToDistance() {
+        assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+        assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f)
+    }
+
+    private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
+        assertStateAfterEvents(
+            events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
+            expectedState = InProgress(progress = expectedProgress),
+        )
+    }
+
+    @Test
+    fun triggeredProgressIsBetweenZeroAndOne() {
+        // going in the wrong direction
+        assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f)
+        // going further than required distance
+        assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
+    }
+
+    @Test
     fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
         assertStateAfterEvents(
             events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
@@ -102,7 +128,7 @@
     }
 
     private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
-        events.forEach { gestureMonitor.processTouchpadEvent(it) }
+        events.forEach { gestureRecognizer.accept(it) }
         assertThat(gestureState).isEqualTo(expectedState)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
index 296d4dc..42fe1e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
@@ -25,11 +25,23 @@
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_X
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_Y
 
-/**
- * Interface for gesture builders which support creating list of [MotionEvent] for common swipe
- * gestures. For simple usage see swipe* methods or use [createEvents] for more specific scenarios.
- */
-interface MultiFingerGesture {
+/** Given gesture move events can build list of [MotionEvent]s included in that gesture */
+interface GestureEventsBuilder {
+    /**
+     * Creates full gesture including provided move events. This means returned events include DOWN,
+     * MOVE and UP. Note that move event's x and y is always relative to the starting one.
+     */
+    fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent>
+
+    /**
+     * Creates partial gesture including provided move events. This means returned events include
+     * DOWN and MOVE. Note that move event's x and y is always relative to the starting one.
+     */
+    fun eventsForGestureInProgress(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent>
+}
+
+/** Support creating list of [MotionEvent] for common swipe gestures. */
+interface MultiFingerGesture : GestureEventsBuilder {
 
     companion object {
         const val SWIPE_DISTANCE = 100f
@@ -37,27 +49,41 @@
         const val DEFAULT_Y = 500f
     }
 
-    fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = -distancePx) }
+    fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+        move(deltaY = -distancePx)
+    }
 
-    fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = distancePx) }
+    fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+        move(deltaY = distancePx)
+    }
 
-    fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = distancePx) }
+    fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+        move(deltaX = distancePx)
+    }
 
-    fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = -distancePx) }
-
-    /**
-     * Creates gesture with provided move events. Note that move event's x and y is always relative
-     * to the starting one
-     */
-    fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent>
+    fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+        move(deltaX = -distancePx)
+    }
 }
 
 object ThreeFingerGesture : MultiFingerGesture {
-    override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
-        return touchpadGesture(
+
+    private val moveEventsBuilder = MoveEventsBuilder(::threeFingerEvent)
+
+    override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> {
+        return buildGesture(
             startEvents = { x, y -> startEvents(x, y) },
-            moveEvents = GestureBuilder(::threeFingerEvent).apply { moveEvents() }.events,
-            endEvents = { x, y -> endEvents(x, y) }
+            moveEvents = moveEventsBuilder.getEvents(moveEvents),
+            endEvents = { x, y -> endEvents(x, y) },
+        )
+    }
+
+    override fun eventsForGestureInProgress(
+        moveEvents: MoveEventsBuilder.() -> Unit
+    ): List<MotionEvent> {
+        return buildGesture(
+            startEvents = { x, y -> startEvents(x, y) },
+            moveEvents = moveEventsBuilder.getEvents(moveEvents),
         )
     }
 
@@ -65,7 +91,7 @@
         return listOf(
             threeFingerEvent(ACTION_DOWN, x, y),
             threeFingerEvent(ACTION_POINTER_DOWN, x, y),
-            threeFingerEvent(ACTION_POINTER_DOWN, x, y)
+            threeFingerEvent(ACTION_POINTER_DOWN, x, y),
         )
     }
 
@@ -73,32 +99,43 @@
         return listOf(
             threeFingerEvent(ACTION_POINTER_UP, x, y),
             threeFingerEvent(ACTION_POINTER_UP, x, y),
-            threeFingerEvent(ACTION_UP, x, y)
+            threeFingerEvent(ACTION_UP, x, y),
         )
     }
 
     private fun threeFingerEvent(
         action: Int,
         x: Float = DEFAULT_X,
-        y: Float = DEFAULT_Y
+        y: Float = DEFAULT_Y,
     ): MotionEvent {
         return touchpadEvent(
             action = action,
             x = x,
             y = y,
             classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE,
-            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f)
+            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f),
         )
     }
 }
 
 object FourFingerGesture : MultiFingerGesture {
 
-    override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
-        return touchpadGesture(
+    private val moveEventsBuilder = MoveEventsBuilder(::fourFingerEvent)
+
+    override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> {
+        return buildGesture(
             startEvents = { x, y -> startEvents(x, y) },
-            moveEvents = GestureBuilder(::fourFingerEvent).apply { moveEvents() }.events,
-            endEvents = { x, y -> endEvents(x, y) }
+            moveEvents = moveEventsBuilder.getEvents(moveEvents),
+            endEvents = { x, y -> endEvents(x, y) },
+        )
+    }
+
+    override fun eventsForGestureInProgress(
+        moveEvents: MoveEventsBuilder.() -> Unit
+    ): List<MotionEvent> {
+        return buildGesture(
+            startEvents = { x, y -> startEvents(x, y) },
+            moveEvents = moveEventsBuilder.getEvents(moveEvents),
         )
     }
 
@@ -107,7 +144,7 @@
             fourFingerEvent(ACTION_DOWN, x, y),
             fourFingerEvent(ACTION_POINTER_DOWN, x, y),
             fourFingerEvent(ACTION_POINTER_DOWN, x, y),
-            fourFingerEvent(ACTION_POINTER_DOWN, x, y)
+            fourFingerEvent(ACTION_POINTER_DOWN, x, y),
         )
     }
 
@@ -116,61 +153,74 @@
             fourFingerEvent(ACTION_POINTER_UP, x, y),
             fourFingerEvent(ACTION_POINTER_UP, x, y),
             fourFingerEvent(ACTION_POINTER_UP, x, y),
-            fourFingerEvent(ACTION_UP, x, y)
+            fourFingerEvent(ACTION_UP, x, y),
         )
     }
 
     private fun fourFingerEvent(
         action: Int,
         x: Float = DEFAULT_X,
-        y: Float = DEFAULT_Y
+        y: Float = DEFAULT_Y,
     ): MotionEvent {
         return touchpadEvent(
             action = action,
             x = x,
             y = y,
             classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE,
-            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f)
+            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f),
         )
     }
 }
 
 object TwoFingerGesture : MultiFingerGesture {
 
-    override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
-        return touchpadGesture(
-            startEvents = { x, y -> listOf(twoFingerEvent(ACTION_DOWN, x, y)) },
-            moveEvents = GestureBuilder(::twoFingerEvent).apply { moveEvents() }.events,
-            endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) }
+    private val moveEventsBuilder = MoveEventsBuilder(::twoFingerEvent)
+
+    override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> {
+        return buildGesture(
+            startEvents = { x, y -> startEvents(x, y) },
+            moveEvents = moveEventsBuilder.getEvents(moveEvents),
+            endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) },
         )
     }
 
+    override fun eventsForGestureInProgress(
+        moveEvents: MoveEventsBuilder.() -> Unit
+    ): List<MotionEvent> {
+        return buildGesture(
+            startEvents = { x, y -> startEvents(x, y) },
+            moveEvents = moveEventsBuilder.getEvents(moveEvents),
+        )
+    }
+
+    private fun startEvents(x: Float, y: Float) = listOf(twoFingerEvent(ACTION_DOWN, x, y))
+
     private fun twoFingerEvent(
         action: Int,
         x: Float = DEFAULT_X,
-        y: Float = DEFAULT_Y
+        y: Float = DEFAULT_Y,
     ): MotionEvent {
         return touchpadEvent(
             action = action,
             x = x,
             y = y,
             classification = MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE,
-            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f)
+            axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f),
         )
     }
 }
 
-private fun touchpadGesture(
+private fun buildGesture(
     startEvents: (Float, Float) -> List<MotionEvent>,
     moveEvents: List<MotionEvent>,
-    endEvents: (Float, Float) -> List<MotionEvent>
+    endEvents: (Float, Float) -> List<MotionEvent> = { _, _ -> emptyList() },
 ): List<MotionEvent> {
     val lastX = moveEvents.last().x
     val lastY = moveEvents.last().y
     return startEvents(DEFAULT_X, DEFAULT_Y) + moveEvents + endEvents(lastX, lastY)
 }
 
-class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) {
+class MoveEventsBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) {
 
     val events = mutableListOf<MotionEvent>()
 
@@ -178,3 +228,11 @@
         events.add(eventBuilder(ACTION_MOVE, DEFAULT_X + deltaX, DEFAULT_Y + deltaY))
     }
 }
+
+private fun MoveEventsBuilder.getEvents(
+    moveEvents: MoveEventsBuilder.() -> Unit
+): List<MotionEvent> {
+    events.clear()
+    this.moveEvents()
+    return events
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
index 13ebb42..6413677 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
@@ -54,7 +54,28 @@
                 ACTION_MOVE,
                 ACTION_POINTER_UP,
                 ACTION_POINTER_UP,
-                ACTION_UP
+                ACTION_UP,
+            )
+            .inOrder()
+    }
+
+    @Test
+    fun threeFingerGestureInProgressProducesCorrectEvents() {
+        val events =
+            ThreeFingerGesture.eventsForGestureInProgress {
+                move(deltaX = 10f)
+                move(deltaX = 20f)
+            }
+
+        val actions = events.map { it.actionMasked }
+        assertWithMessage("Events have expected action type")
+            .that(actions)
+            .containsExactly(
+                ACTION_DOWN,
+                ACTION_POINTER_DOWN,
+                ACTION_POINTER_DOWN,
+                ACTION_MOVE,
+                ACTION_MOVE,
             )
             .inOrder()
     }
@@ -80,7 +101,7 @@
                 ACTION_POINTER_UP,
                 ACTION_POINTER_UP,
                 ACTION_POINTER_UP,
-                ACTION_UP
+                ACTION_UP,
             )
             .inOrder()
     }
@@ -109,7 +130,7 @@
     @Test
     fun gestureBuilderProducesCorrectEventCoordinates() {
         val events =
-            ThreeFingerGesture.createEvents {
+            ThreeFingerGesture.eventsForFullGesture {
                 move(deltaX = 50f)
                 move(deltaX = 100f)
             }
@@ -127,7 +148,7 @@
                 // up events
                 DEFAULT_X + 100f to DEFAULT_Y,
                 DEFAULT_X + 100f to DEFAULT_Y,
-                DEFAULT_X + 100f to DEFAULT_Y
+                DEFAULT_X + 100f to DEFAULT_Y,
             )
             .inOrder()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
index 4d26366..c302b40 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 import com.google.common.truth.Truth.assertThat
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -35,14 +36,14 @@
 class TouchpadGestureHandlerTest : SysuiTestCase() {
 
     private var gestureState: GestureState = GestureState.NotStarted
-    private val handler =
-        TouchpadGestureHandler(
-            BackGestureMonitor(
-                gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
-                gestureStateChangedCallback = { gestureState = it },
-            ),
-            EasterEggGestureMonitor {},
-        )
+    private val gestureRecognizer =
+        BackGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+    private val handler = TouchpadGestureHandler(gestureRecognizer, EasterEggGestureMonitor {})
+
+    @Before
+    fun before() {
+        gestureRecognizer.addGestureStateCallback { gestureState = it }
+    }
 
     @Test
     fun handlesEventsFromTouchpad() {
@@ -84,7 +85,7 @@
     }
 
     private fun backGestureEvents(): List<MotionEvent> {
-        return ThreeFingerGesture.createEvents {
+        return ThreeFingerGesture.eventsForFullGesture {
             move(deltaX = SWIPE_DISTANCE / 4)
             move(deltaX = SWIPE_DISTANCE / 2)
             move(deltaX = SWIPE_DISTANCE)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 5d850d8..f101539 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -18,15 +18,20 @@
 
 import android.content.Context
 import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.defaultDeviceState
+import com.android.systemui.deviceStateManager
 import com.android.systemui.display.data.repository.DeviceStateRepository
 import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
+import com.android.systemui.foldedDeviceStateList
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.power.shared.model.WakeSleepReason
@@ -39,10 +44,10 @@
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.unfoldedDeviceState
 import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
@@ -63,6 +68,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
@@ -78,8 +84,14 @@
     private val animationStatusRepository = mock<AnimationStatusRepository>()
     private val keyguardInteractor = mock<KeyguardInteractor>()
     private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
+    private val kosmos = Kosmos()
+    private val deviceStateManager = kosmos.deviceStateManager
+    private val closedDeviceState = kosmos.foldedDeviceStateList.first()
+    private val openDeviceState = kosmos.unfoldedDeviceState
+    private val defaultDeviceState = kosmos.defaultDeviceState
+    private val nonEmptyClosedDeviceStatesArray: IntArray =
+        IntArray(2) { closedDeviceState.identifier }
 
-    private val nonEmptyClosedDeviceStatesArray: IntArray = IntArray(2) { 0 }
     private val testDispatcher: TestDispatcher = StandardTestDispatcher()
     private val testScope: TestScope = TestScope(testDispatcher)
     private val isAsleep = MutableStateFlow(false)
@@ -108,6 +120,10 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         whenever(mockContext.resources).thenReturn(resources)
+        whenever(mockContext.getSystemService(DeviceStateManager::class.java))
+            .thenReturn(deviceStateManager)
+        whenever(deviceStateManager.supportedDeviceStates)
+            .thenReturn(listOf(closedDeviceState, openDeviceState))
         whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
             .thenReturn(nonEmptyClosedDeviceStatesArray)
         whenever(foldStateRepository.state).thenReturn(deviceState)
@@ -128,7 +144,8 @@
                 testDispatcher.asExecutor(),
                 testScope.backgroundScope,
                 displaySwitchLatencyLogger,
-                systemClock
+                systemClock,
+                deviceStateManager
             )
     }
 
@@ -182,7 +199,8 @@
                     testDispatcher.asExecutor(),
                     testScope.backgroundScope,
                     displaySwitchLatencyLogger,
-                    systemClock
+                    systemClock,
+                    deviceStateManager
                 )
             areAnimationEnabled.emit(true)
 
@@ -321,6 +339,8 @@
             deviceState.emit(DeviceState.UNFOLDED)
             whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
                 .thenReturn(IntArray(0))
+            whenever(deviceStateManager.supportedDeviceStates)
+                .thenReturn(listOf(defaultDeviceState))
 
             displaySwitchLatencyTracker.start()
             deviceState.emit(DeviceState.HALF_FOLDED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index 1e5929d..a1122c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -23,9 +23,13 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.foldedDeviceStateList
+import com.android.systemui.halfFoldedDeviceState
 import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.unfold.util.FoldableDeviceStates
 import com.android.systemui.unfold.util.FoldableTestUtils
+import com.android.systemui.unfoldedDeviceState
 import com.android.systemui.util.mockito.any
 import java.util.Optional
 import org.junit.Before
@@ -38,6 +42,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -62,6 +67,13 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(deviceStateManager.supportedDeviceStates).thenReturn(
+            listOf(
+                Kosmos().foldedDeviceStateList[0],
+                Kosmos().unfoldedDeviceState
+            )
+        )
+
         unfoldLatencyTracker =
             UnfoldLatencyTracker(
                     latencyTracker,
@@ -73,6 +85,7 @@
                     screenLifecycle
                 )
                 .apply { init() }
+
         deviceStates = FoldableTestUtils.findDeviceStates(context)
 
         verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
index e4a1c26..9a9ec13 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
@@ -18,34 +18,53 @@
 
 import android.content.Context
 import android.hardware.devicestate.DeviceState
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags
 import org.junit.Assume.assumeTrue
 
 object FoldableTestUtils {
-
     /** Finds device state for folded and unfolded. */
     fun findDeviceStates(context: Context): FoldableDeviceStates {
-        // TODO(b/325474477): Migrate clients to updated DeviceStateManager API's
-        val foldedDeviceStates: IntArray = context.resources.getIntArray(
-            com.android.internal.R.array.config_foldedDeviceStates)
-        assumeTrue("Test should be launched on a foldable device",
-            foldedDeviceStates.isNotEmpty())
+        if (DeviceStateManagerFlags.deviceStatePropertyMigration()) {
+            val deviceStateManager = context.getSystemService(DeviceStateManager::class.java)
+            val deviceStateList = deviceStateManager.supportedDeviceStates
 
-        val folded = getDeviceState(
-            identifier = foldedDeviceStates.maxOrNull()!!
-        )
-        val unfolded = getDeviceState(
-            identifier = folded.identifier + 1
-        )
-        return FoldableDeviceStates(folded = folded, unfolded = unfolded)
+            val folded =
+                deviceStateList.firstOrNull { state ->
+                    state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+                }
+            val unfolded =
+                deviceStateList.firstOrNull { state ->
+                    state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)
+                }
+
+            assumeTrue(
+                "Test should only be ran on a foldable device",
+                (folded != null) && (unfolded != null)
+            )
+
+            return FoldableDeviceStates(folded = folded!!, unfolded = unfolded!!)
+        } else {
+            val foldedDeviceStates: IntArray =
+                context.resources.getIntArray(
+                    com.android.internal.R.array.config_foldedDeviceStates
+                )
+            assumeTrue(
+                "Test should be launched on a foldable device",
+                foldedDeviceStates.isNotEmpty()
+            )
+
+            val folded = getDeviceState(identifier = foldedDeviceStates.maxOrNull()!!)
+            val unfolded = getDeviceState(identifier = folded.identifier + 1)
+            return FoldableDeviceStates(folded = folded, unfolded = unfolded)
+        }
     }
 
     private fun getDeviceState(identifier: Int): DeviceState {
-        return DeviceState(
-            DeviceState.Configuration.Builder(
-                identifier, "" /* name */
-            ).build()
-        )
+        return DeviceState(DeviceState.Configuration.Builder(identifier, "" /* name */).build())
     }
 }
 
-data class FoldableDeviceStates(val folded: DeviceState, val unfolded: DeviceState)
\ No newline at end of file
+data class FoldableDeviceStates(val folded: DeviceState, val unfolded: DeviceState)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/CreateUserActivityTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/CreateUserActivityTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/UserCreatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/UserCreatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/NotificationChannelsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/NotificationChannelsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/NotificationChannelsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/NotificationChannelsTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
new file mode 100644
index 0000000..b4ba41a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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.util
+
+import android.hardware.devicestate.DeviceState
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN
+import android.hardware.devicestate.feature.flags.Flags
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.testing.TestableResources
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.deviceStateManager
+import com.android.systemui.kosmos.Kosmos
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UtilsTest : SysuiTestCase() {
+
+    private val kosmos = Kosmos()
+    private val deviceStateManager = kosmos.deviceStateManager
+    private lateinit var testableResources: TestableResources
+
+    @Before
+    fun setUp() {
+        testableResources = context.orCreateTestableResources
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun isFoldableReturnsFalse_overlayConfigurationValues() {
+        testableResources.addOverride(
+            com.android.internal.R.array.config_foldedDeviceStates,
+            intArrayOf() // empty array <=> device is not foldable
+        )
+        whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
+        assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun isFoldableReturnsFalse_deviceStateManager() {
+        testableResources.addOverride(
+            com.android.internal.R.array.config_foldedDeviceStates,
+            intArrayOf() // empty array <=> device is not foldable
+        )
+        whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
+        assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun isFoldableReturnsTrue_overlayConfigurationValues() {
+        testableResources.addOverride(
+            com.android.internal.R.array.config_foldedDeviceStates,
+            intArrayOf(FOLDED_DEVICE_STATE.identifier)
+        )
+        whenever(deviceStateManager.supportedDeviceStates)
+            .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
+        assertTrue(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    fun isFoldableReturnsTrue_deviceStateManager() {
+        testableResources.addOverride(
+            com.android.internal.R.array.config_foldedDeviceStates,
+            intArrayOf(FOLDED_DEVICE_STATE.identifier)
+        )
+        whenever(deviceStateManager.supportedDeviceStates)
+            .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
+        assertTrue(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
+    }
+
+    companion object {
+        private val DEFAULT_DEVICE_STATE =
+            DeviceState(DeviceState.Configuration.Builder(0 /* identifier */, "DEFAULT").build())
+        private val FOLDED_DEVICE_STATE =
+            DeviceState(
+                DeviceState.Configuration.Builder(1 /* identifier */, "FOLDED")
+                    .setSystemProperties(
+                        setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+                    )
+                    .build()
+            )
+        private val UNFOLDED_DEVICE_STATE =
+            DeviceState(
+                DeviceState.Configuration.Builder(2 /* identifier */, "UNFOLDED")
+                    .setSystemProperties(
+                        setOf(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)
+                    )
+                    .build()
+            )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
index b8f5815..a4b3916 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
@@ -35,7 +35,7 @@
         val drawable =
             BitmapDrawable(
                 resources,
-                Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888)
+                Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888),
             )
         val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300)
         assertThat(result).isSameInstanceAs(drawable)
@@ -48,7 +48,7 @@
         val drawable =
             BitmapDrawable(
                 resources,
-                Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888)
+                Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888),
             )
 
         val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75)
@@ -64,4 +64,31 @@
         val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
         assertThat(result).isSameInstanceAs(drawable)
     }
+
+    @Test
+    fun testDownscaleToSize_layerDrawable_allLayersSameType_resized() {
+        val drawable =
+            resources.getDrawable(
+                com.android.systemui.tests.R.drawable.layer_drawable_all_same_type,
+                resources.newTheme(),
+            )
+
+        val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
+
+        assertThat(result).isNotSameInstanceAs(drawable)
+    }
+
+    /** Regression test for b/244282477. */
+    @Test
+    fun testDownscaleToSize_layerDrawable_layersAreDifferentTypes_unchanged() {
+        val drawable =
+            resources.getDrawable(
+                com.android.systemui.tests.R.drawable.layer_drawable_different_types,
+                resources.newTheme(),
+            )
+
+        val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
+
+        assertThat(result).isSameInstanceAs(drawable)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/LeakDetectorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakDetectorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/LeakDetectorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/ReferenceTestUtilsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/ReferenceTestUtilsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/leak/ReferenceTestUtilsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/ReferenceTestUtilsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/WeakIdentityHashMapTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/CsdWarningDialogTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/CsdWarningDialogTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
index 7ce421a..31d2eb3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.plugins.fakeVolumeDialogController
 import com.android.systemui.testKosmos
 import com.android.systemui.volume.Events
-import com.android.systemui.volume.dialog.domain.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.days
 import kotlin.time.Duration.Companion.seconds
@@ -44,7 +44,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper()
+@TestableLooper.RunWithLooper
 class VolumeDialogVisibilityInteractorTest : SysuiTestCase() {
 
     private val kosmos: Kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
new file mode 100644
index 0000000..7c5a487
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.interactor
+
+import android.content.packageManager
+import android.content.pm.PackageManager
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
+
+private const val AUDIO_SHARING_STREAM = 99
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSlidersInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    private lateinit var underTest: VolumeDialogSlidersInteractor
+
+    private var isTv: Boolean = false
+
+    @Before
+    fun setUp() {
+        with(kosmos) {
+            whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)).thenAnswer {
+                isTv
+            }
+
+            underTest = kosmos.volumeDialogSlidersInteractor
+        }
+    }
+
+    @Test
+    fun activeStreamIsSlider() =
+        with(kosmos) {
+            testScope.runTest {
+                runCurrent()
+                fakeVolumeDialogController.updateState {
+                    activeStream = AudioManager.STREAM_SYSTEM
+                    states.put(AudioManager.STREAM_MUSIC, buildStreamState())
+                    states.put(AudioManager.STREAM_SYSTEM, buildStreamState())
+                }
+
+                val slidersModel by collectLastValue(underTest.sliders)
+                runCurrent()
+
+                assertThat(slidersModel!!.slider)
+                    .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM))
+                assertThat(slidersModel!!.floatingSliders)
+                    .isEqualTo(listOf(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC)))
+            }
+        }
+
+    @Test
+    fun streamsOrder() =
+        with(kosmos) {
+            testScope.runTest {
+                runCurrent()
+                fakeVolumeDialogController.onAccessibilityModeChanged(true)
+                fakeVolumeDialogController.updateState {
+                    activeStream = AudioManager.STREAM_MUSIC
+                    states.put(AUDIO_SHARING_STREAM, buildStreamState { dynamic = true })
+                    states.put(AUDIO_SHARING_STREAM + 1, buildStreamState { dynamic = true })
+                    states.put(AudioManager.STREAM_MUSIC, buildStreamState())
+                    states.put(AudioManager.STREAM_ACCESSIBILITY, buildStreamState())
+                }
+
+                val slidersModel by collectLastValue(underTest.sliders)
+                runCurrent()
+
+                assertThat(slidersModel!!.slider)
+                    .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC))
+                assertThat(slidersModel!!.floatingSliders)
+                    .isEqualTo(
+                        listOf(
+                            VolumeDialogSliderType.Stream(AudioManager.STREAM_ACCESSIBILITY),
+                            VolumeDialogSliderType.AudioSharingStream(AUDIO_SHARING_STREAM),
+                            VolumeDialogSliderType.RemoteMediaStream(AUDIO_SHARING_STREAM + 1),
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun accessibilityStreamDisabled_filteredOut() =
+        with(kosmos) {
+            testScope.runTest {
+                runCurrent()
+                fakeVolumeDialogController.onAccessibilityModeChanged(false)
+                fakeVolumeDialogController.updateState {
+                    states.put(AudioManager.STREAM_ACCESSIBILITY, buildStreamState())
+                    states.put(AudioManager.STREAM_MUSIC, buildStreamState())
+                }
+
+                val slidersModel by collectLastValue(underTest.sliders)
+                runCurrent()
+
+                assertThat(slidersModel!!.slider)
+                    .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC))
+                assertThat(slidersModel!!.floatingSliders).isEmpty()
+            }
+        }
+
+    @Test
+    fun isTv_onlyActiveStream() =
+        with(kosmos) {
+            testScope.runTest {
+                runCurrent()
+                isTv = true
+                fakeVolumeDialogController.updateState {
+                    activeStream = AudioManager.STREAM_SYSTEM
+                    states.put(AudioManager.STREAM_MUSIC, buildStreamState())
+                    states.put(AudioManager.STREAM_SYSTEM, buildStreamState())
+                }
+
+                val slidersModel by collectLastValue(underTest.sliders)
+                runCurrent()
+
+                assertThat(slidersModel!!.slider)
+                    .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM))
+                assertThat(slidersModel!!.floatingSliders).isEmpty()
+            }
+        }
+
+    private fun buildStreamState(
+        build: VolumeDialogController.StreamState.() -> Unit = {}
+    ): VolumeDialogController.StreamState {
+        return VolumeDialogController.StreamState().apply(build)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
index fa7f37c..449dc20 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -16,11 +16,16 @@
 
 package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
 
+import android.content.mockedContext
+import android.content.packageManager
+import android.content.pm.PackageManager.FEATURE_PC
 import android.graphics.drawable.TestStubDrawable
 import android.media.AudioManager
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.media.flags.Flags;
 import com.android.settingslib.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -42,6 +47,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
 
 private const val builtInDeviceName = "This phone"
 
@@ -79,6 +85,8 @@
     fun inCall_stateIs_Calling() =
         with(kosmos) {
             testScope.runTest {
+                whenever(mockedContext.getPackageManager()).thenReturn(packageManager)
+                whenever(packageManager.hasSystemFeature(FEATURE_PC)).thenReturn(false)
                 with(audioRepository) {
                     setMode(AudioManager.MODE_IN_CALL)
                     setCommunicationDevice(TestAudioDevicesFactory.builtInDevice())
@@ -98,6 +106,33 @@
             }
         }
 
+    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    fun inCall_stateIs_Calling_enableInputRouting_desktop() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(mockedContext.getPackageManager()).thenReturn(packageManager)
+                whenever(packageManager.hasSystemFeature(FEATURE_PC)).thenReturn(true)
+
+                with(audioRepository) {
+                    setMode(AudioManager.MODE_IN_CALL)
+                    setCommunicationDevice(TestAudioDevicesFactory.builtInDevice())
+                }
+
+                val model by collectLastValue(underTest.mediaOutputModel.filterData())
+                runCurrent()
+
+                assertThat(model)
+                    .isEqualTo(
+                        MediaOutputComponentModel.Calling(
+                            device = AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
+                            isInAudioSharing = false,
+                            canOpenAudioSwitcher = true,
+                        )
+                    )
+            }
+        }
+
     @Test
     fun hasSession_stateIs_MediaSession() =
         with(kosmos) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
new file mode 100644
index 0000000..76a1269
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.volume.domain.interactor
+
+import android.media.AudioManager
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.data.repository.audioSystemRepository
+import com.android.systemui.volume.panel.component.volume.domain.model.SliderType
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AudioSlidersInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    private lateinit var underTest: AudioSlidersInteractor
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            audioRepository.setMode(AudioManager.MODE_NORMAL)
+            underTest = audioSlidersInteractor
+        }
+
+    @Test
+    fun shouldAddAllStreams_notInCall() =
+        with(kosmos) {
+            testScope.runTest {
+                val sliders by collectLastValue(underTest.volumePanelSliders)
+                runCurrent()
+
+                assertThat(sliders).isEqualTo(
+                    mutableListOf(
+                        AudioManager.STREAM_MUSIC,
+                        AudioManager.STREAM_VOICE_CALL,
+                        AudioManager.STREAM_RING,
+                        AudioManager.STREAM_NOTIFICATION,
+                        AudioManager.STREAM_ALARM
+                    ).map { SliderType.Stream(AudioStream(it)) })
+            }
+        }
+
+    @Test
+    fun shouldAddAllStreams_inCall() =
+        with(kosmos) {
+            testScope.runTest {
+                audioRepository.setMode(AudioManager.MODE_IN_CALL)
+
+                val sliders by collectLastValue(underTest.volumePanelSliders)
+                runCurrent()
+
+                // Call stream is before music stream while in call.
+                assertThat(sliders).isEqualTo(
+                    mutableListOf(
+                        AudioManager.STREAM_VOICE_CALL,
+                        AudioManager.STREAM_MUSIC,
+                        AudioManager.STREAM_RING,
+                        AudioManager.STREAM_NOTIFICATION,
+                        AudioManager.STREAM_ALARM
+                    ).map { SliderType.Stream(AudioStream(it)) })
+            }
+        }
+
+
+    @Test
+    @EnableFlags(Flags.FLAG_ONLY_SHOW_MEDIA_STREAM_SLIDER_IN_SINGLE_VOLUME_MODE)
+    fun shouldAddMusicStreamOnly_singleVolumeMode() =
+        with(kosmos) {
+            testScope.runTest {
+                audioSystemRepository.setIsSingleVolume(true)
+
+                val sliders by collectLastValue(underTest.volumePanelSliders)
+                runCurrent()
+
+                assertThat(sliders).isEqualTo(
+                    mutableListOf(SliderType.Stream(AudioStream(AudioManager.STREAM_MUSIC))))
+            }
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
new file mode 100644
index 0000000..f80b36a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
+
+import android.app.Flags
+import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
+import android.media.AudioManager
+import android.platform.test.annotations.EnableFlags
+import android.service.notification.ZenPolicy
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLogger
+import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
+import com.android.systemui.volume.shared.volumePanelLogger
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AudioStreamSliderViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val zenModeRepository = kosmos.fakeZenModeRepository
+
+    private lateinit var mediaStream: AudioStreamSliderViewModel
+    private lateinit var alarmsStream: AudioStreamSliderViewModel
+    private lateinit var notificationStream: AudioStreamSliderViewModel
+    private lateinit var otherStream: AudioStreamSliderViewModel
+
+    @Before
+    fun setUp() {
+        mediaStream = audioStreamSliderViewModel(AudioManager.STREAM_MUSIC)
+        alarmsStream = audioStreamSliderViewModel(AudioManager.STREAM_ALARM)
+        notificationStream = audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION)
+        otherStream = audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL)
+    }
+
+    private fun audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel {
+        return AudioStreamSliderViewModel(
+            AudioStreamSliderViewModel.FactoryAudioStreamWrapper(AudioStream(stream)),
+            testScope.backgroundScope,
+            context,
+            kosmos.audioVolumeInteractor,
+            kosmos.zenModeInteractor,
+            kosmos.uiEventLogger,
+            kosmos.volumePanelLogger,
+        )
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+    fun slider_media_hasDisabledByModesText() =
+        testScope.runTest {
+            val mediaSlider by collectLastValue(mediaStream.slider)
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setName("Media is ok")
+                    .setZenPolicy(ZenPolicy.Builder().allowAllSounds().build())
+                    .setActive(true)
+                    .build()
+            )
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setName("No media plz")
+                    .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+                    .setActive(true)
+                    .build()
+            )
+            runCurrent()
+
+            assertThat(mediaSlider!!.disabledMessage)
+                .isEqualTo("Unavailable because No media plz is on")
+
+            zenModeRepository.clearModes()
+            runCurrent()
+
+            assertThat(mediaSlider!!.disabledMessage).isEqualTo("Unavailable")
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+    fun slider_alarms_hasDisabledByModesText() =
+        testScope.runTest {
+            val alarmsSlider by collectLastValue(alarmsStream.slider)
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setName("Alarms are ok")
+                    .setZenPolicy(ZenPolicy.Builder().allowAllSounds().build())
+                    .setActive(true)
+                    .build()
+            )
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setName("Zzzzz")
+                    .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build())
+                    .setActive(true)
+                    .build()
+            )
+            runCurrent()
+
+            assertThat(alarmsSlider!!.disabledMessage).isEqualTo("Unavailable because Zzzzz is on")
+
+            zenModeRepository.clearModes()
+            runCurrent()
+
+            assertThat(alarmsSlider!!.disabledMessage).isEqualTo("Unavailable")
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+    fun slider_other_hasDisabledByModesText() =
+        testScope.runTest {
+            val otherSlider by collectLastValue(otherStream.slider)
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setName("Everything blocked")
+                    .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+                    .setActive(true)
+                    .build()
+            )
+            runCurrent()
+
+            assertThat(otherSlider!!.disabledMessage)
+                .isEqualTo("Unavailable because Everything blocked is on")
+
+            zenModeRepository.clearModes()
+            runCurrent()
+
+            assertThat(otherSlider!!.disabledMessage).isEqualTo("Unavailable")
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+    fun slider_notification_hasSpecialDisabledText() =
+        testScope.runTest {
+            val notificationSlider by collectLastValue(notificationStream.slider)
+            runCurrent()
+
+            assertThat(notificationSlider!!.disabledMessage)
+                .isEqualTo("Unavailable because ring is muted")
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubblesTestActivity.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubblesTestActivity.java
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
index b1736b1..c09509d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
@@ -14,7 +14,6 @@
 
 package com.android.systemui.plugins;
 
-import android.annotation.IntegerRes;
 import android.content.ComponentName;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -22,6 +21,8 @@
 import android.os.VibrationEffect;
 import android.util.SparseArray;
 
+import androidx.annotation.StringRes;
+
 import com.android.systemui.plugins.VolumeDialogController.Callbacks;
 import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.plugins.VolumeDialogController.StreamState;
@@ -90,7 +91,7 @@
         public int levelMax;
         public boolean muted;
         public boolean muteSupported;
-        public @IntegerRes int name;
+        public @StringRes int name;
         public String remoteLabel;
         public boolean routedToBluetooth;
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index fc9c917..8bef475 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -26,7 +26,7 @@
     android:clipChildren="false"
     android:layout_gravity="center_horizontal|top">
     <com.android.keyguard.KeyguardClockFrame
-        android:id="@+id/lockscreen_clock_view"
+        android:id="@id/lockscreen_clock_view"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/small_clock_height"
         android:layout_alignParentStart="true"
@@ -35,7 +35,7 @@
         android:paddingStart="@dimen/clock_padding_start"
         android:visibility="invisible" />
     <com.android.keyguard.KeyguardClockFrame
-        android:id="@+id/lockscreen_clock_view_large"
+        android:id="@id/lockscreen_clock_view_large"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:clipChildren="false"
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 042067b..fcb3a3e 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -20,7 +20,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"‏‫اپنا PIN درج کریں"</string>
+    <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"‏اپنا PIN درج کریں"</string>
     <string name="keyguard_enter_pin" msgid="8114529922480276834">"‏PIN درج کریں"</string>
     <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"اپنا پیٹرن درج کریں"</string>
     <string name="keyguard_enter_pattern" msgid="7616595160901084119">"پیٹرن ڈرا کریں"</string>
diff --git a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
index 627b92b..3c16684 100644
--- a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
+++ b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
@@ -21,11 +21,8 @@
     android:color="?android:attr/colorControlHighlight">
     <item>
         <shape android:shape="rectangle">
-            <solid android:color="@android:color/transparent"/>
+            <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/>
             <corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/>
-            <stroke
-                android:width="1dp"
-                android:color="?androidprv:attr/textColorTertiary" />
         </shape>
     </item>
 </ripple>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
new file mode 100644
index 0000000..7d7498f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/volume_dialog_background_corner_radius" />
+    <solid android:color="?androidprv:attr/materialColorSurface" />
+</shape>
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
similarity index 60%
copy from packages/SystemUI/res/layout/udfps_bp_view.xml
copy to packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
index f1c55ef..2694435 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,9 +13,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsBpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="20dp" />
+    <solid android:color="?androidprv:attr/colorSurface" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_floating_sliders_spacer.xml b/packages/SystemUI/res/drawable/volume_dialog_floating_sliders_spacer.xml
new file mode 100644
index 0000000..66a205a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_dialog_floating_sliders_spacer.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size
+        android:width="@dimen/volume_dialog_floating_sliders_spacing"
+        android:height="@dimen/volume_dialog_floating_sliders_spacing" />
+    <solid android:color="@color/transparent" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_spacer.xml b/packages/SystemUI/res/drawable/volume_dialog_spacer.xml
new file mode 100644
index 0000000..3c60784
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_dialog_spacer.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size
+        android:width="@dimen/volume_dialog_spacing"
+        android:height="@dimen/volume_dialog_spacing" />
+    <solid android:color="@color/transparent" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
index 21b177b..fa06bd6 100644
--- a/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
@@ -22,7 +22,7 @@
     android:autoMirrored="true">
     <item android:id="@+id/volume_seekbar_progress_solid">
         <shape>
-            <size android:height="@dimen/volume_dialog_slider_width" />
+            <size android:height="@dimen/volume_dialog_slider_width_legacy" />
             <solid android:color="?android:attr/colorAccent" />
             <corners android:radius="@dimen/volume_dialog_slider_corner_radius"/>
         </shape>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
index 0fbc519..f77db95 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
@@ -1,92 +1,67 @@
 <!--
-  ~ Copyright (C) 2020 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
-  -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/volume_dialog_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="@android:color/transparent"
+    android:layout_gravity="right"
+    android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+    android:orientation="horizontal"
+    android:showDividers="middle|end|beginning"
     android:theme="@style/volume_dialog_theme">
 
-    <FrameLayout
-        android:id="@+id/volume_dialog"
+    <LinearLayout
+        android:id="@+id/volume_dialog_floating_sliders_container"
         android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:background="@drawable/volume_dialog_background"
+        android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+        android:gravity="bottom"
+        android:orientation="horizontal"
+        android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
+        android:showDividers="middle" />
+
+    <LinearLayout
+        android:layout_width="@dimen/volume_dialog_width"
         android:layout_height="wrap_content"
-        android:layout_gravity="right"
-        android:background="@android:color/transparent"
-        android:padding="@dimen/volume_dialog_panel_transparent_padding"
-        android:clipToPadding="false">
-
-        <LinearLayout
-            android:id="@+id/volume_dialog_rows_container"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="right"
-            android:orientation="vertical"
-            android:translationZ="@dimen/volume_dialog_elevation"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:background="@android:color/transparent">
-
-            <LinearLayout
-                android:id="@+id/volume_dialog_rows"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="center"
-                android:orientation="horizontal"
-                android:background="@drawable/tv_volume_dialog_background">
-                <!-- volume rows added and removed here! :-) -->
-            </LinearLayout>
-
-        </LinearLayout>
+        android:background="@drawable/volume_dialog_background"
+        android:divider="@drawable/volume_dialog_spacer"
+        android:gravity="center_horizontal"
+        android:orientation="vertical"
+        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
+        android:showDividers="middle">
 
         <FrameLayout
-            android:id="@+id/odi_captions"
-            android:layout_width="@dimen/volume_dialog_caption_size"
-            android:layout_height="@dimen/volume_dialog_caption_size"
-            android:layout_marginRight="68dp"
-            android:layout_gravity="right"
-            android:clipToPadding="false"
-            android:translationZ="@dimen/volume_dialog_elevation"
-            android:background="@drawable/rounded_bg_full">
+            android:id="@+id/volume_dialog_ringer_button"
+            android:layout_width="@dimen/volume_dialog_button_size"
+            android:layout_height="@dimen/volume_dialog_button_size" />
 
-            <com.android.systemui.volume.CaptionsToggleImageButton
-                android:id="@+id/odi_captions_icon"
-                android:src="@drawable/ic_volume_odi_captions_disabled"
-                style="@style/VolumeButtons"
-                android:background="@drawable/rounded_ripple"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:tint="@color/caption_tint_color_selector"
-                android:layout_gravity="center"
-                android:soundEffectsEnabled="false"/>
+        <include
+            android:id="@+id/volume_dialog_slider"
+            layout="@layout/volume_dialog_slider" />
 
-        </FrameLayout>
-
-        <ViewStub
-            android:id="@+id/odi_captions_tooltip_stub"
-            android:inflatedId="@+id/odi_captions_tooltip_view"
-            android:layout="@layout/volume_tool_tip_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginRight="@dimen/volume_tool_tip_right_margin"
-            android:layout_marginTop="@dimen/volume_tool_tip_top_margin"
-            android:layout_gravity="right"/>
-
-    </FrameLayout>
-
-</FrameLayout>
+        <Button
+            android:id="@+id/volume_dialog_settings"
+            android:layout_width="@dimen/volume_dialog_button_size"
+            android:layout_height="@dimen/volume_dialog_button_size"
+            android:background="@drawable/ripple_drawable_20dp"
+            android:contentDescription="@string/accessibility_volume_settings"
+            android:soundEffectsEnabled="false"
+            android:src="@drawable/horizontal_ellipsis"
+            android:tint="?androidprv:attr/materialColorPrimary" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_legacy.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_legacy.xml
new file mode 100644
index 0000000..0fbc519
--- /dev/null
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_legacy.xml
@@ -0,0 +1,92 @@
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/volume_dialog_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:color/transparent"
+    android:theme="@style/volume_dialog_theme">
+
+    <FrameLayout
+        android:id="@+id/volume_dialog"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right"
+        android:background="@android:color/transparent"
+        android:padding="@dimen/volume_dialog_panel_transparent_padding"
+        android:clipToPadding="false">
+
+        <LinearLayout
+            android:id="@+id/volume_dialog_rows_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="right"
+            android:orientation="vertical"
+            android:translationZ="@dimen/volume_dialog_elevation"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:background="@android:color/transparent">
+
+            <LinearLayout
+                android:id="@+id/volume_dialog_rows"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:orientation="horizontal"
+                android:background="@drawable/tv_volume_dialog_background">
+                <!-- volume rows added and removed here! :-) -->
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/odi_captions"
+            android:layout_width="@dimen/volume_dialog_caption_size"
+            android:layout_height="@dimen/volume_dialog_caption_size"
+            android:layout_marginRight="68dp"
+            android:layout_gravity="right"
+            android:clipToPadding="false"
+            android:translationZ="@dimen/volume_dialog_elevation"
+            android:background="@drawable/rounded_bg_full">
+
+            <com.android.systemui.volume.CaptionsToggleImageButton
+                android:id="@+id/odi_captions_icon"
+                android:src="@drawable/ic_volume_odi_captions_disabled"
+                style="@style/VolumeButtons"
+                android:background="@drawable/rounded_ripple"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:tint="@color/caption_tint_color_selector"
+                android:layout_gravity="center"
+                android:soundEffectsEnabled="false"/>
+
+        </FrameLayout>
+
+        <ViewStub
+            android:id="@+id/odi_captions_tooltip_stub"
+            android:inflatedId="@+id/odi_captions_tooltip_view"
+            android:layout="@layout/volume_tool_tip_view"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="@dimen/volume_tool_tip_right_margin"
+            android:layout_marginTop="@dimen/volume_tool_tip_top_margin"
+            android:layout_gravity="right"/>
+
+    </FrameLayout>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index cf301c9..eb89489 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -63,8 +63,8 @@
                 android:layout_height="match_parent"
                 android:layout_gravity="center"
                 android:layoutDirection="ltr"
-                android:maxHeight="@dimen/volume_dialog_slider_width"
-                android:minHeight="@dimen/volume_dialog_slider_width"
+                android:maxHeight="@dimen/volume_dialog_slider_width_legacy"
+                android:minHeight="@dimen/volume_dialog_slider_width_legacy"
                 android:progressDrawable="@drawable/volume_row_seekbar"
                 android:thumb="@drawable/tv_volume_row_seek_thumb"
                 android:splitTrack="false"
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 08edf59..f77db95 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -1,146 +1,67 @@
 <!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/volume_dialog_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:gravity="right"
     android:layout_gravity="right"
-    android:background="@android:color/transparent"
+    android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+    android:orientation="horizontal"
+    android:showDividers="middle|end|beginning"
     android:theme="@style/volume_dialog_theme">
 
-    <!-- right-aligned to be physically near volume button -->
     <LinearLayout
-        android:id="@+id/volume_dialog"
+        android:id="@+id/volume_dialog_floating_sliders_container"
         android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:background="@drawable/volume_dialog_background"
+        android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+        android:gravity="bottom"
+        android:orientation="horizontal"
+        android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
+        android:showDividers="middle" />
+
+    <LinearLayout
+        android:layout_width="@dimen/volume_dialog_width"
         android:layout_height="wrap_content"
-        android:gravity="right"
-        android:layout_gravity="right"
-        android:layout_marginRight="@dimen/volume_dialog_panel_transparent_padding_right"
+        android:background="@drawable/volume_dialog_background"
+        android:divider="@drawable/volume_dialog_spacer"
+        android:gravity="center_horizontal"
         android:orientation="vertical"
-        android:clipToPadding="false"
-        android:clipChildren="false">
-
-
-        <LinearLayout
-            android:id="@+id/volume_dialog_top_container"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:clipChildren="false"
-            android:gravity="right">
-
-            <include layout="@layout/volume_ringer_drawer" />
-
-            <FrameLayout
-                android:visibility="gone"
-                android:id="@+id/ringer"
-                android:layout_width="@dimen/volume_dialog_ringer_size"
-                android:layout_height="@dimen/volume_dialog_ringer_size"
-                android:layout_marginBottom="@dimen/volume_dialog_spacer"
-                android:gravity="right"
-                android:layout_gravity="right"
-                android:translationZ="@dimen/volume_dialog_elevation"
-                android:clipToPadding="false"
-                android:background="@drawable/rounded_bg_full">
-                <com.android.keyguard.AlphaOptimizedImageButton
-                    android:id="@+id/ringer_icon"
-                    style="@style/VolumeButtons"
-                    android:background="@drawable/rounded_ripple"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:scaleType="fitCenter"
-                    android:padding="@dimen/volume_dialog_ringer_icon_padding"
-                    android:tint="?android:attr/textColorPrimary"
-                    android:layout_gravity="center"
-                    android:soundEffectsEnabled="false" />
-            </FrameLayout>
-
-            <LinearLayout
-                android:id="@+id/volume_dialog_rows_container"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="right"
-                android:layout_gravity="right"
-                android:orientation="vertical"
-                android:clipChildren="false"
-                android:clipToPadding="false" >
-                <LinearLayout
-                    android:id="@+id/volume_dialog_rows"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:gravity="center"
-                    android:orientation="horizontal">
-                    <!-- volume rows added and removed here! :-) -->
-                </LinearLayout>
-                <FrameLayout
-                    android:id="@+id/settings_container"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:background="@drawable/volume_background_bottom"
-                    android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
-                    android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
-                    android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
-
-                    <com.android.keyguard.AlphaOptimizedImageButton
-                        android:id="@+id/settings"
-                        android:layout_width="@dimen/volume_dialog_tap_target_size"
-                        android:layout_height="@dimen/volume_dialog_tap_target_size"
-                        android:layout_gravity="center"
-                        android:background="@drawable/ripple_drawable_20dp"
-                        android:contentDescription="@string/accessibility_volume_settings"
-                        android:scaleType="centerInside"
-                        android:soundEffectsEnabled="false"
-                        android:src="@drawable/horizontal_ellipsis"
-                        android:tint="?androidprv:attr/colorAccent" />
-                </FrameLayout>
-            </LinearLayout>
-
-        </LinearLayout>
+        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
+        android:showDividers="middle">
 
         <FrameLayout
-            android:id="@+id/odi_captions"
-            android:layout_width="@dimen/volume_dialog_caption_size"
-            android:layout_height="@dimen/volume_dialog_caption_size"
-            android:layout_marginTop="@dimen/volume_dialog_row_margin_bottom"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:clipToPadding="false"
-            android:clipToOutline="true"
-            android:background="@drawable/volume_row_rounded_background">
-            <com.android.systemui.volume.CaptionsToggleImageButton
-                android:id="@+id/odi_captions_icon"
-                android:src="@drawable/ic_volume_odi_captions_disabled"
-                style="@style/VolumeButtons"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:tint="?android:attr/colorAccent"
-                android:layout_gravity="center"
-                android:soundEffectsEnabled="false" />
-        </FrameLayout>
+            android:id="@+id/volume_dialog_ringer_button"
+            android:layout_width="@dimen/volume_dialog_button_size"
+            android:layout_height="@dimen/volume_dialog_button_size" />
+
+        <include
+            android:id="@+id/volume_dialog_slider"
+            layout="@layout/volume_dialog_slider" />
+
+        <Button
+            android:id="@+id/volume_dialog_settings"
+            android:layout_width="@dimen/volume_dialog_button_size"
+            android:layout_height="@dimen/volume_dialog_button_size"
+            android:background="@drawable/ripple_drawable_20dp"
+            android:contentDescription="@string/accessibility_volume_settings"
+            android:soundEffectsEnabled="false"
+            android:src="@drawable/horizontal_ellipsis"
+            android:tint="?androidprv:attr/materialColorPrimary" />
     </LinearLayout>
-
-    <ViewStub
-        android:id="@+id/odi_captions_tooltip_stub"
-        android:inflatedId="@+id/odi_captions_tooltip_view"
-        android:layout="@layout/volume_tool_tip_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom | right"
-        android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
-
-</FrameLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
new file mode 100644
index 0000000..08edf59
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
@@ -0,0 +1,146 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/volume_dialog_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="right"
+    android:layout_gravity="right"
+    android:background="@android:color/transparent"
+    android:theme="@style/volume_dialog_theme">
+
+    <!-- right-aligned to be physically near volume button -->
+    <LinearLayout
+        android:id="@+id/volume_dialog"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="right"
+        android:layout_gravity="right"
+        android:layout_marginRight="@dimen/volume_dialog_panel_transparent_padding_right"
+        android:orientation="vertical"
+        android:clipToPadding="false"
+        android:clipChildren="false">
+
+
+        <LinearLayout
+            android:id="@+id/volume_dialog_top_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:clipChildren="false"
+            android:gravity="right">
+
+            <include layout="@layout/volume_ringer_drawer" />
+
+            <FrameLayout
+                android:visibility="gone"
+                android:id="@+id/ringer"
+                android:layout_width="@dimen/volume_dialog_ringer_size"
+                android:layout_height="@dimen/volume_dialog_ringer_size"
+                android:layout_marginBottom="@dimen/volume_dialog_spacer"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:translationZ="@dimen/volume_dialog_elevation"
+                android:clipToPadding="false"
+                android:background="@drawable/rounded_bg_full">
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/ringer_icon"
+                    style="@style/VolumeButtons"
+                    android:background="@drawable/rounded_ripple"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:scaleType="fitCenter"
+                    android:padding="@dimen/volume_dialog_ringer_icon_padding"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:layout_gravity="center"
+                    android:soundEffectsEnabled="false" />
+            </FrameLayout>
+
+            <LinearLayout
+                android:id="@+id/volume_dialog_rows_container"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:orientation="vertical"
+                android:clipChildren="false"
+                android:clipToPadding="false" >
+                <LinearLayout
+                    android:id="@+id/volume_dialog_rows"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center"
+                    android:orientation="horizontal">
+                    <!-- volume rows added and removed here! :-) -->
+                </LinearLayout>
+                <FrameLayout
+                    android:id="@+id/settings_container"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/volume_background_bottom"
+                    android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+                    android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
+                    android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+
+                    <com.android.keyguard.AlphaOptimizedImageButton
+                        android:id="@+id/settings"
+                        android:layout_width="@dimen/volume_dialog_tap_target_size"
+                        android:layout_height="@dimen/volume_dialog_tap_target_size"
+                        android:layout_gravity="center"
+                        android:background="@drawable/ripple_drawable_20dp"
+                        android:contentDescription="@string/accessibility_volume_settings"
+                        android:scaleType="centerInside"
+                        android:soundEffectsEnabled="false"
+                        android:src="@drawable/horizontal_ellipsis"
+                        android:tint="?androidprv:attr/colorAccent" />
+                </FrameLayout>
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/odi_captions"
+            android:layout_width="@dimen/volume_dialog_caption_size"
+            android:layout_height="@dimen/volume_dialog_caption_size"
+            android:layout_marginTop="@dimen/volume_dialog_row_margin_bottom"
+            android:gravity="right"
+            android:layout_gravity="right"
+            android:clipToPadding="false"
+            android:clipToOutline="true"
+            android:background="@drawable/volume_row_rounded_background">
+            <com.android.systemui.volume.CaptionsToggleImageButton
+                android:id="@+id/odi_captions_icon"
+                android:src="@drawable/ic_volume_odi_captions_disabled"
+                style="@style/VolumeButtons"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:tint="?android:attr/colorAccent"
+                android:layout_gravity="center"
+                android:soundEffectsEnabled="false" />
+        </FrameLayout>
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/odi_captions_tooltip_stub"
+        android:inflatedId="@+id/odi_captions_tooltip_view"
+        android:layout="@layout/volume_tool_tip_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom | right"
+        android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/audio_sharing_dialog.xml b/packages/SystemUI/res/layout/audio_sharing_dialog.xml
new file mode 100644
index 0000000..7534e15
--- /dev/null
+++ b/packages/SystemUI/res/layout/audio_sharing_dialog.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/root"
+    style="@style/Widget.SliceView.Panel"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <ImageView android:id="@+id/icon"
+        android:layout_width="28dp"
+        android:layout_height="28dp"
+        android:src="@drawable/ic_bt_le_audio_sharing"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="20dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/title"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="20dp"
+        android:gravity="center_vertical|center_horizontal"
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:text="@string/quick_settings_bluetooth_audio_sharing_dialog_title"
+        android:textAppearance="@style/TextAppearance.Dialog.Title"
+        android:textSize="24sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/subtitle"
+        app:layout_constraintTop_toBottomOf="@id/icon" />
+
+    <TextView
+        android:id="@+id/subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="20dp"
+        android:gravity="center_vertical|center_horizontal"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+        android:textFontWeight="500"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/message"
+        app:layout_constraintTop_toBottomOf="@id/title" />
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="20dp"
+        android:gravity="center_vertical|center_horizontal"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:text="@string/quick_settings_bluetooth_audio_sharing_dialog_message"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/share_audio_button"
+        app:layout_constraintTop_toBottomOf="@id/subtitle" />
+
+    <Button
+        android:id="@+id/share_audio_button"
+        style="@style/SettingsLibActionButton"
+        android:textColor="?androidprv:attr/textColorOnAccent"
+        android:background="@drawable/audio_sharing_rounded_bg_ripple"
+        android:layout_marginBottom="4dp"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:minHeight="64dp"
+        android:contentDescription="@string/accessibility_bluetooth_device_settings_see_all"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/message"
+        app:layout_constraintBottom_toTopOf="@+id/switch_active_button"
+        android:text="@string/quick_settings_bluetooth_audio_sharing_button"
+        android:maxLines="2" />
+
+    <Button
+        android:id="@+id/switch_active_button"
+        style="@style/SettingsLibActionButton"
+        android:textColor="?androidprv:attr/textColorOnAccent"
+        android:background="@drawable/audio_sharing_rounded_bg_ripple"
+        android:layout_marginBottom="20dp"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:minHeight="64dp"
+        android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/share_audio_button"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:maxLines="2" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 4a7bef9..80692f9 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -72,7 +72,6 @@
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toBottomOf="@id/device_function_barrier"
-        app:layout_constraintBottom_toTopOf="@id/related_tools_scroll"
         android:drawableStart="@drawable/ic_add"
         android:drawablePadding="20dp"
         android:drawableTint="?android:attr/textColorPrimary"
@@ -92,24 +91,16 @@
         app:barrierDirection="bottom"
         app:constraint_referenced_ids="device_function_barrier, pair_new_device_button" />
 
-    <HorizontalScrollView
-        android:id="@+id/related_tools_scroll"
+    <LinearLayout
+        android:id="@+id/related_tools_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
         android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
         android:layout_marginTop="@dimen/hearing_devices_layout_margin"
-        android:scrollbars="none"
-        android:fillViewport="true"
+        android:orientation="horizontal"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/preset_spinner">
-        <LinearLayout
-            android:id="@+id/related_tools_container"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal">
-        </LinearLayout>
-    </HorizontalScrollView>
+        app:layout_constraintTop_toBottomOf="@id/device_barrier" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_tool_item.xml b/packages/SystemUI/res/layout/hearing_tool_item.xml
index ff2fbe07..f5baf2a 100644
--- a/packages/SystemUI/res/layout/hearing_tool_item.xml
+++ b/packages/SystemUI/res/layout/hearing_tool_item.xml
@@ -17,8 +17,8 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/tool_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
     android:orientation="vertical"
     android:gravity="top|center_horizontal"
     android:focusable="true"
@@ -26,8 +26,10 @@
     android:layout_weight="1">
     <FrameLayout
         android:id="@+id/icon_frame"
-        android:layout_width="@dimen/hearing_devices_tool_icon_frame_width"
-        android:layout_height="@dimen/hearing_devices_tool_icon_frame_height"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="20dp"
+        android:paddingBottom="20dp"
         android:background="@drawable/qs_hearing_devices_related_tools_background"
         android:focusable="false" >
         <ImageView
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 690a89a..d0a1ce8 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -45,31 +45,26 @@
             android:tint="?android:attr/colorPrimary"
         />
 
-        <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever
-             be shown at one time. -->
+        <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text,
+             ongoing_activity_chip_short_time_delta] will ever be shown at one time. -->
+
+        <!-- Shows a timer, like 00:01. -->
         <com.android.systemui.statusbar.chips.ui.view.ChipChronometer
             android:id="@+id/ongoing_activity_chip_time"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:gravity="center|start"
-            android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
-            android:textAppearance="@android:style/TextAppearance.Material.Small"
-            android:fontFamily="@*android:string/config_headlineFontFamily"
-            android:textColor="?android:attr/colorPrimary"
+            style="@style/StatusBar.Chip.Text"
         />
 
-        <!-- Used to show generic text in the chip instead of a timer. -->
+        <!-- Shows generic text. -->
         <TextView
             android:id="@+id/ongoing_activity_chip_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:gravity="center|start"
-            android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
-            android:textAppearance="@android:style/TextAppearance.Material.Small"
-            android:fontFamily="@*android:string/config_headlineFontFamily"
-            android:textColor="?android:attr/colorPrimary"
+            style="@style/StatusBar.Chip.Text"
+            android:visibility="gone"
+            />
+
+        <!-- Shows a time delta in short form, like "15min" or "1hr". -->
+        <android.widget.DateTimeView
+            android:id="@+id/ongoing_activity_chip_short_time_delta"
+            style="@style/StatusBar.Chip.Text"
             android:visibility="gone"
             />
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index e214666..77fbb64 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -128,12 +128,6 @@
 
     <include layout="@layout/dock_info_bottom_area_overlay" />
 
-    <com.android.keyguard.LockIconView
-        android:id="@+id/lock_icon_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content">
-    </com.android.keyguard.LockIconView>
-
     <include
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 22d34eb..fbb07be 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -58,6 +58,14 @@
              android:layout_height="match_parent"
              android:visibility="invisible" />
 
+    <!-- Root for all keyguard content. It was previously located within the shade. -->
+    <com.android.systemui.keyguard.ui.view.KeyguardRootView
+        android:id="@id/keyguard_root_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        />
+
     <!-- Shared container for the notification stack. Can be positioned by either
          the keyguard_root_view or notification_panel -->
     <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
@@ -68,14 +76,6 @@
         android:clipToPadding="false"
         />
 
-    <!-- Root for all keyguard content. It was previously located within the shade. -->
-    <com.android.systemui.keyguard.ui.view.KeyguardRootView
-        android:id="@id/keyguard_root_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clipChildren="false"
-        />
-
     <include layout="@layout/brightness_mirror_container" />
 
     <com.android.systemui.scrim.ScrimView
diff --git a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml b/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml
deleted file mode 100644
index 4799f8c..0000000
--- a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.systemui.biometrics.UdfpsFpmEmptyView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <!-- The layout height/width are placeholders, which will be overwritten by
-     FingerprintSensorPropertiesInternal. -->
-    <View
-        android:id="@+id/udfps_enroll_accessibility_view"
-        android:layout_gravity="center"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:contentDescription="@string/accessibility_fingerprint_label"/>
-</com.android.systemui.biometrics.UdfpsFpmEmptyView>
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
deleted file mode 100644
index 530d752..0000000
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view_legacy"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. -->
-
-</com.android.systemui.biometrics.UdfpsKeyguardViewLegacy>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
deleted file mode 100644
index 257d238..0000000
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.systemui.biometrics.UdfpsView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/udfps_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    systemui:sensorTouchAreaCoefficient="1.0"
-    android:contentDescription="@string/accessibility_fingerprint_label">
-
-    <ViewStub
-        android:id="@+id/animation_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
-
-</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 39a1f1f..f77db95 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -1,5 +1,5 @@
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     Copyright (C) 2024 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,133 +13,55 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/volume_dialog_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:gravity="right"
     android:layout_gravity="right"
-    android:clipToPadding="false"
+    android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+    android:orientation="horizontal"
+    android:showDividers="middle|end|beginning"
     android:theme="@style/volume_dialog_theme">
 
-    <!-- right-aligned to be physically near volume button -->
     <LinearLayout
-        android:id="@+id/volume_dialog"
+        android:id="@+id/volume_dialog_floating_sliders_container"
         android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:background="@drawable/volume_dialog_background"
+        android:divider="@drawable/volume_dialog_floating_sliders_spacer"
+        android:gravity="bottom"
+        android:orientation="horizontal"
+        android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
+        android:showDividers="middle" />
+
+    <LinearLayout
+        android:layout_width="@dimen/volume_dialog_width"
         android:layout_height="wrap_content"
-        android:gravity="right"
-        android:layout_gravity="right"
-        android:layout_marginRight="@dimen/volume_dialog_panel_transparent_padding_right"
+        android:background="@drawable/volume_dialog_background"
+        android:divider="@drawable/volume_dialog_spacer"
+        android:gravity="center_horizontal"
         android:orientation="vertical"
-        android:clipToPadding="false"
-        android:clipChildren="false">
-
-        <LinearLayout
-            android:id="@+id/volume_dialog_top_container"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:clipChildren="false"
-            android:orientation="vertical"
-            android:gravity="right">
-
-            <include layout="@layout/volume_ringer_drawer" />
-
-            <FrameLayout
-                android:visibility="gone"
-                android:id="@+id/ringer"
-                android:layout_width="@dimen/volume_dialog_ringer_size"
-                android:layout_height="@dimen/volume_dialog_ringer_size"
-                android:layout_marginBottom="@dimen/volume_dialog_spacer"
-                android:gravity="right"
-                android:layout_gravity="right"
-                android:translationZ="@dimen/volume_dialog_elevation"
-                android:clipToPadding="false"
-                android:background="@drawable/rounded_bg_full">
-                <com.android.keyguard.AlphaOptimizedImageButton
-                    android:id="@+id/ringer_icon"
-                    style="@style/VolumeButtons"
-                    android:background="@drawable/rounded_ripple"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:scaleType="fitCenter"
-                    android:padding="@dimen/volume_dialog_ringer_icon_padding"
-                    android:tint="?android:attr/textColorPrimary"
-                    android:layout_gravity="center"
-                    android:soundEffectsEnabled="false" />
-            </FrameLayout>
-
-            <LinearLayout
-                android:id="@+id/volume_dialog_rows_container"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="right"
-                android:layout_gravity="right"
-                android:orientation="vertical"
-                android:clipChildren="false"
-                android:clipToPadding="false" >
-                <LinearLayout
-                    android:id="@+id/volume_dialog_rows"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:gravity="center"
-                    android:orientation="horizontal">
-                        <!-- volume rows added and removed here! :-) -->
-                </LinearLayout>
-                <FrameLayout
-                    android:id="@+id/settings_container"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:background="@drawable/volume_background_bottom"
-                    android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
-                    android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
-                    android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
-                    <com.android.keyguard.AlphaOptimizedImageButton
-                        android:id="@+id/settings"
-                        android:src="@drawable/horizontal_ellipsis"
-                        android:layout_width="@dimen/volume_dialog_tap_target_size"
-                        android:layout_height="@dimen/volume_dialog_tap_target_size"
-                        android:layout_gravity="center"
-                        android:contentDescription="@string/accessibility_volume_settings"
-                        android:background="@drawable/ripple_drawable_20dp"
-                        android:tint="?androidprv:attr/colorAccent"
-                        android:soundEffectsEnabled="false" />
-                </FrameLayout>
-            </LinearLayout>
-
-        </LinearLayout>
+        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
+        android:showDividers="middle">
 
         <FrameLayout
-            android:id="@+id/odi_captions"
-            android:layout_width="@dimen/volume_dialog_caption_size"
-            android:layout_height="@dimen/volume_dialog_caption_size"
-            android:layout_marginTop="@dimen/volume_dialog_row_margin_bottom"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:clipToPadding="false"
-            android:clipToOutline="true"
-            android:background="@drawable/volume_row_rounded_background">
-            <com.android.systemui.volume.CaptionsToggleImageButton
-                android:id="@+id/odi_captions_icon"
-                android:src="@drawable/ic_volume_odi_captions_disabled"
-                style="@style/VolumeButtons"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:tint="?android:attr/colorAccent"
-                android:layout_gravity="center"
-                android:soundEffectsEnabled="false"/>
-        </FrameLayout>
+            android:id="@+id/volume_dialog_ringer_button"
+            android:layout_width="@dimen/volume_dialog_button_size"
+            android:layout_height="@dimen/volume_dialog_button_size" />
+
+        <include
+            android:id="@+id/volume_dialog_slider"
+            layout="@layout/volume_dialog_slider" />
+
+        <Button
+            android:id="@+id/volume_dialog_settings"
+            android:layout_width="@dimen/volume_dialog_button_size"
+            android:layout_height="@dimen/volume_dialog_button_size"
+            android:background="@drawable/ripple_drawable_20dp"
+            android:contentDescription="@string/accessibility_volume_settings"
+            android:soundEffectsEnabled="false"
+            android:src="@drawable/horizontal_ellipsis"
+            android:tint="?androidprv:attr/materialColorPrimary" />
     </LinearLayout>
-
-    <ViewStub
-        android:id="@+id/odi_captions_tooltip_stub"
-        android:inflatedId="@+id/odi_captions_tooltip_view"
-        android:layout="@layout/volume_tool_tip_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom | right"
-        android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
-
-</FrameLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_legacy.xml b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
new file mode 100644
index 0000000..39a1f1f
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
@@ -0,0 +1,145 @@
+<!--
+     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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/volume_dialog_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="right"
+    android:layout_gravity="right"
+    android:clipToPadding="false"
+    android:theme="@style/volume_dialog_theme">
+
+    <!-- right-aligned to be physically near volume button -->
+    <LinearLayout
+        android:id="@+id/volume_dialog"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="right"
+        android:layout_gravity="right"
+        android:layout_marginRight="@dimen/volume_dialog_panel_transparent_padding_right"
+        android:orientation="vertical"
+        android:clipToPadding="false"
+        android:clipChildren="false">
+
+        <LinearLayout
+            android:id="@+id/volume_dialog_top_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:clipChildren="false"
+            android:orientation="vertical"
+            android:gravity="right">
+
+            <include layout="@layout/volume_ringer_drawer" />
+
+            <FrameLayout
+                android:visibility="gone"
+                android:id="@+id/ringer"
+                android:layout_width="@dimen/volume_dialog_ringer_size"
+                android:layout_height="@dimen/volume_dialog_ringer_size"
+                android:layout_marginBottom="@dimen/volume_dialog_spacer"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:translationZ="@dimen/volume_dialog_elevation"
+                android:clipToPadding="false"
+                android:background="@drawable/rounded_bg_full">
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/ringer_icon"
+                    style="@style/VolumeButtons"
+                    android:background="@drawable/rounded_ripple"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:scaleType="fitCenter"
+                    android:padding="@dimen/volume_dialog_ringer_icon_padding"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:layout_gravity="center"
+                    android:soundEffectsEnabled="false" />
+            </FrameLayout>
+
+            <LinearLayout
+                android:id="@+id/volume_dialog_rows_container"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:orientation="vertical"
+                android:clipChildren="false"
+                android:clipToPadding="false" >
+                <LinearLayout
+                    android:id="@+id/volume_dialog_rows"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center"
+                    android:orientation="horizontal">
+                        <!-- volume rows added and removed here! :-) -->
+                </LinearLayout>
+                <FrameLayout
+                    android:id="@+id/settings_container"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/volume_background_bottom"
+                    android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+                    android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
+                    android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+                    <com.android.keyguard.AlphaOptimizedImageButton
+                        android:id="@+id/settings"
+                        android:src="@drawable/horizontal_ellipsis"
+                        android:layout_width="@dimen/volume_dialog_tap_target_size"
+                        android:layout_height="@dimen/volume_dialog_tap_target_size"
+                        android:layout_gravity="center"
+                        android:contentDescription="@string/accessibility_volume_settings"
+                        android:background="@drawable/ripple_drawable_20dp"
+                        android:tint="?androidprv:attr/colorAccent"
+                        android:soundEffectsEnabled="false" />
+                </FrameLayout>
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/odi_captions"
+            android:layout_width="@dimen/volume_dialog_caption_size"
+            android:layout_height="@dimen/volume_dialog_caption_size"
+            android:layout_marginTop="@dimen/volume_dialog_row_margin_bottom"
+            android:gravity="right"
+            android:layout_gravity="right"
+            android:clipToPadding="false"
+            android:clipToOutline="true"
+            android:background="@drawable/volume_row_rounded_background">
+            <com.android.systemui.volume.CaptionsToggleImageButton
+                android:id="@+id/odi_captions_icon"
+                android:src="@drawable/ic_volume_odi_captions_disabled"
+                style="@style/VolumeButtons"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:tint="?android:attr/colorAccent"
+                android:layout_gravity="center"
+                android:soundEffectsEnabled="false"/>
+        </FrameLayout>
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/odi_captions_tooltip_stub"
+        android:inflatedId="@+id/odi_captions_tooltip_view"
+        android:layout="@layout/volume_tool_tip_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom | right"
+        android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
new file mode 100644
index 0000000..8acdd39
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -0,0 +1,27 @@
+<!--
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/volume_dialog_slider_width"
+    android:layout_height="@dimen/volume_dialog_slider_height">
+
+    <com.google.android.material.slider.Slider
+        android:id="@+id/volume_dialog_slider"
+        android:layout_width="@dimen/volume_dialog_slider_height"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:rotation="270"
+        android:theme="@style/Theme.MaterialComponents.DayNight" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
new file mode 100644
index 0000000..db800aa
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@drawable/volume_dialog_floating_slider_background"
+    android:paddingHorizontal="@dimen/volume_dialog_floating_sliders_horizontal_padding"
+    android:paddingVertical="@dimen/volume_dialog_floating_sliders_vertical_padding">
+
+    <include layout="@layout/volume_dialog_slider" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 0f411ca..f00ba9d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deel oudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deel tans oudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"voer instellings vir oudiodeling in"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Hierdie toestel se musiek en video’s sal op albei stelle oorfone speel"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Deel jou oudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> en <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Skakel oor na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Op • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Af"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nie gestel nie"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Bestuur in instellings"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Geen aktiewe modusse nie}=1{{mode} is aktief}other{# modusse is aktief}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Jy sal nie deur geluide en vibrasies gepla word nie, behalwe deur wekkers, herinneringe, geleenthede en bellers wat jy spesifiseer. Jy sal steeds enigiets hoor wat jy kies om te speel, insluitend musiek, video\'s en speletjies."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Onbeskikbaar omdat luitoon gedemp is"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Onbeskikbaar want Moenie Steur Nie is aan"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Onbeskikbaar want Moenie Steur Nie is aan"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Onbeskikbaar omdat <xliff:g id="MODE">%s</xliff:g> aan is"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Onbeskikbaar"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om te ontdemp."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om op vibreer te stel. Toeganklikheidsdienste kan dalk gedemp wees."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Leer raakpaneelgebare"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeer met jou sleutelbord en raakpaneel"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer raakpaneelgebare, kortpadsleutels en meer"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gaan terug"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gaan na tuisskerm"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Bekyk onlangse apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gaan terug"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swiep links of regs met drie vingers op jou raakpaneel"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Mooi so!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Jy het die Gaan Terug-gebaar voltooi."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gaan na tuisskerm"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swiep op met drie vingers op jou raakpaneel"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Uitstekende werk!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Jy het die Gaan na Tuisskerm-gebaar voltooi"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Bekyk onlangse apps"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swiep op en hou met drie vingers op jou raakpaneel"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Knap gedaan!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jy het die Bekyk Onlangse Apps-gebaar voltooi."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Bekyk alle apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk die handelingsleutel op jou sleutelbord"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Welgedaan!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jy het die Bekyk Onlangse Apps-gebaar voltooi"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 5531424..9f759fb 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ኦዲዮ አጋራ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ኦዲዮ በማጋራት ላይ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"የድምፅ ማጋሪያ ቅንብሮች አስገባ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"የዚህ መሣሪያ ሙዚቃ እና ቪዲዮዎች በሁለቱም የራስ ላይ ማዳመጫዎች ላይ ይጫወታሉ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ኦዲዮዎን ያጋሩ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> እና <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"ወደ <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ቀይር"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"በርቷል"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"በርቷል • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ጠፍቷል"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"አልተቀናበረም"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"በቅንብሮች ውስጥ አስተዳድር"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ምንም ገቢር ሁነታዎች የሉም}=1{{mode} ገቢር ነው}one{# ሁኔታ ገቢር ነው}other{# ሁኔታዎች ገቢር ናቸው}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"እርስዎ ከወሰንዋቸው ማንቂያዎች፣ አስታዋሾች፣ ክስተቶች እና ደዋዮች በስተቀር፣ በድምጾች እና ንዝረቶች አይረበሹም። ሙዚቃ፣ ቪዲዮዎች እና ጨዋታዎች ጨምሮ ለመጫወት የሚመርጡትን ማንኛውም ነገር አሁንም ይሰማሉ።"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"የጥሪ ድምጽ ስለተዘጋ አይገኝም"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"አትረብሽ ስለበራ አይገኝም"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"አትረብሽ ስለበራ አይገኝም"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ስለበራ አይገኝም"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"አይገኝም"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"የመዳሰሻ ሰሌዳ ምልክቶችን ይወቁ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"የእርስዎን የቁልፍ ሰሌዳ እና የመዳሰሻ ሰሌዳ በመጠቀም ያስሱ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"የመዳሰሻ ሰሌዳ ምልክቶችን፣ የቁልፍ ሰሌዳ አቋራጮችን እና ሌሎችን ይወቁ"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ወደኋላ ተመለስ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ወደ መነሻ ሂድ"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ተከናውኗል"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ወደኋላ ተመለስ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"የመዳሰሻ ሰሌዳዎ ላይ ሦስት ጣቶችን በመጠቀም ወደ ግራ ወይም ወደ ቀኝ ያንሸራትቱ"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"አሪፍ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ወደኋላ የመመለስ ምልክትን አጠናቅቀዋል።"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ወደ መነሻ ሂድ"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"በመዳሰሻ ሰሌዳዎ ላይ በሦስት ጣቶች ወደ ላይ ያንሸራትቱ"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ጥሩ ሥራ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ወደ መነሻ ሂድ ምልክትን አጠናቅቀዋል"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"የመዳሰሻ ሰሌዳዎ ላይ ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ እና ይያዙ"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ጥሩ ሠርተዋል!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"የቅርብ ጊዜ መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል።"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"ሁሉንም መተግበሪያዎች ይመልከቱ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ጥሩ ሠርተዋል!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"የሁሉንም መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 091b58e..c2539a7 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"مشاركة الصوت"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"جارٍ مشاركة الصوت"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"أدخِل إعدادات ميزة \"مشاركة الصوت\""</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"سيتمّ تشغيل الموسيقى والفيديوهات في هذا الجهاز على سماعات الرأس"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"مشاركة صوت جهازك"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"‫\"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\" و\"<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>\""</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"التبديل إلى \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"مفعَّل"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"مفعّل • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"غير مفعَّل"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"لم يتم ضبط الوضع"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"الإدارة في الإعدادات"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ما مِن أوضاع مفعَّلة}=1{الوضع \"{mode}\" مفعَّل}two{وضعان مفعَّلان}few{‫# أوضاع مفعَّلة}many{‫# وضعًا مفعَّلاً}other{‫# وضع مفعَّل}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"لن يتم إزعاجك بالأصوات والاهتزاز، باستثناء المُنبِّهات والتذكيرات والأحداث والمتصلين الذين تحددهم. وسيظل بإمكانك سماع أي عناصر أخرى تختار تشغيلها، بما في ذلك الموسيقى والفيديوهات والألعاب."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"غير متاح بسبب كتم صوت الرنين"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"مستوى الصوت غير متاح بسبب تفعيل وضع \"عدم الإزعاج\""</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"مستوى الصوت غير متاح لأنّ وضع \"عدم الإزعاج\" مفعّل"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"غير متوفِّر لأنّ وضع \"<xliff:g id="MODE">%s</xliff:g>\" مفعَّل"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"غير متوفِّر"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏%1$s. انقر لإلغاء التجاهل."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"تعرَّف على إيماءات لوحة اللمس"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"التنقّل باستخدام لوحة المفاتيح ولوحة اللمس"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"تعرَّف على إيماءات لوحة اللمس واختصارات لوحة المفاتيح والمزيد"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"رجوع"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"الانتقال إلى الصفحة الرئيسية"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"عرض التطبيقات المستخدَمة مؤخرًا"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تم"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"رجوع"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"مرِّر سريعًا لليمين أو لليسار باستخدام 3 أصابع على لوحة اللمس"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"أحسنت."</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"لقد أكملت التدريب على إيماءة الرجوع."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"الانتقال إلى الشاشة الرئيسية"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"مرّر سريعًا للأعلى باستخدام 3 أصابع على لوحة اللمس"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"أحسنت صنعًا."</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"لقد أكملت الدليل التوجيهي عن إيماءة \"الانتقال إلى الشاشة الرئيسية\""</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"عرض التطبيقات المستخدَمة مؤخرًا"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"مرِّر سريعًا للأعلى مع الاستمرار باستخدام 3 أصابع على لوحة اللمس"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"أحسنت."</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"لقد أكملْت الدليل التوجيهي على إيماءة \"عرض التطبيقات المستخدَمة مؤخرًا\"."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"عرض جميع التطبيقات"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"أحسنت!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"لقد أكملْت الدليل التوجيهي عن إيماءة \"عرض جميع التطبيقات\""</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏مستوى الإضاءة: %1$d من %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index bbaf974..240d29e 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিঅ’ শ্বেয়াৰ কৰক"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"অডিঅ’ শ্বেয়াৰ কৰাৰ ছেটিঙলৈ যাওক"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"এই ডিভাইচটোৰ সংগীত আৰু ভিডিঅ’সমূহ দুয়োটা হেডফ’নতে প্লে’ হ’ব"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"আপোনাৰ অডিঅ’ শ্বেয়াৰ কৰক"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> আৰু <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>লৈ সলনি কৰক"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"অন আছে"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"অন আছে • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"অফ আছে"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"ছেট কৰা হোৱা নাই"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ছেটিঙত পৰিচালনা কৰক"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{কোনো সক্ৰিয় ম’ড নাই}=1{{mode} সক্ৰিয় আছে}one{# টা ম’ড সক্ৰিয় আছে}other{# টা ম’ড সক্ৰিয় আছে}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্ম, ৰিমাইণ্ডাৰ, ইভেন্ট আৰু কল কৰোঁতাৰ বাহিৰে আন কোনো শব্দৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"ৰিং মিউট কৰি থোৱাৰ বাবে উপলব্ধ নহয়"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"অসুবিধা নিদিব অন থকাৰ কাৰণে উপলব্ধ নহয়"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"অসুবিধা নিদিব অন থকাৰ কাৰণে উপলব্ধ নহয়"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> অন থকাৰ বাবে উপলব্ধ নহয়"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"উপলব্ধ নহয়"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। আনমিউট কৰিবৰ বাবে টিপক।"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পনৰ বাবে টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট কৰিবলৈ টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"ডেম\' ম\'ড দেখুৱাওক"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"ইথাৰনেট"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"এলাৰ্ম"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> অন আছে"</string>
     <string name="wallet_title" msgid="5369767670735827105">"ৱালেট"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"আপোনাৰ ফ’নটোৰে দ্ৰুত তথা অধিক সুৰক্ষিত ক্ৰয় কৰিবলৈ ছেট আপ পাওক"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"আটাইবোৰ দেখুৱাওক"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"টাচ্চপেডৰ নিৰ্দেশসমূহ জানক"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"আপোনাৰ কীব’ৰ্ড আৰু টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচ্চপেডৰ নিৰ্দেশ, কীব’ৰ্ডৰ শ্বৰ্টকাট আৰু অধিকৰ বিষয়ে জানক"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"উভতি যাওক"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"গৃহ পৃষ্ঠালৈ যাওক"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"শেহতীয়া এপ্‌সমূহ চাওক"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হ’ল"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"উভতি যাওক"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"আপোনাৰ টাচ্চপেডত তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁফাললৈ বা সোঁফাললৈ ছোৱাইপ কৰক"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"সুন্দৰ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"আপুনি উভতি যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"গৃহ পৃষ্ঠালৈ যাওক"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপোনাৰ টাচ্চপেডৰ তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"বঢ়িয়া!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"শেহতীয়া এপ্‌সমূহ চাওক"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"আপোনাৰ টাচ্চপেডত তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"বঢ়িয়া!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"আপুনি শেহতীয়া এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে।"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"আটাইবোৰ এপ্ চাওক"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"বঢ়িয়া!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপুনি আটাইবোৰ এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 211a05d..d3ebd7d 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audio paylaşın"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio paylaşılır"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audio paylaşma ayarlarına daxil olun"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Bu cihazın musiqi və videoları hər iki qulaqlıqda oxudulacaq"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audio paylaşın"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> və <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> cihazına keçin"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktiv • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Deaktiv"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Ayarlanmayıb"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Ayarlarda idarə edin"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aktiv rejim yoxdur}=1{{mode} aktivdir}other{# rejim aktivdir}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Seçdiyiniz siqnal, xatırladıcı, tədbir və zənglər istisna olmaqla səslər və vibrasiyalar Sizi narahat etməyəcək. Musiqi, video və oyunlar da daxil olmaqla oxutmaq istədiyiniz hər şeyi eşidəcəksiniz."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zəng səssiz edildiyi üçün əlçatan deyil"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Narahat Etməyin aktiv olduğu üçün əlçatan deyil"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Narahat Etməyin aktiv olduğu üçün əlçatan deyil"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> yanılı olduğu üçün əlçatan deyil"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Əlçatan deyil"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Səsli etmək üçün tıklayın."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Vibrasiyanı ayarlamaq üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Demo rejimini göstərin"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Zəngli saat"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> aktivdir"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Pulqabı"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Telefonunuzla daha sürətli və təhlükəsiz satınalmalar etmək üçün ayarlayın"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Hamısını göstər"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Taçped jestlərini öyrənin"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura və taçpeddən istifadə edərək hərəkət edin"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Taçped jestləri, klaviatura qısayolları və s. haqqında öyrənin"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri qayıdın"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Əsas səhifəyə qayıdın"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son tətbiqlərə baxın"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hazırdır"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Taçpeddə üç barmaqla sola və ya sağa sürüşdürün"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Əla!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Geri getmə jestini tamamladınız."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ana ekrana qayıdın"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Taçpeddə üç barmaqla yuxarı sürüşdürün"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Əla!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Əsas səhifəyə keçid jestini tamamladınız"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Son tətbiqlərə baxın"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Taçpeddə üç barmaqla yuxarı çəkib saxlayın"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Əla!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son tətbiqlərə baxmaq jestini tamamladınız."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Bütün tətbiqlərə baxın"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturada fəaliyyət açarına basın"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Əla!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\"Bütün tətbiqlərə baxın\" jestini tamamladınız"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 6064cc1..a1fc846 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deli zvuk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deli se zvuk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"uđite u podešavanja deljenja zvuka"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzika i videi sa ovog uređaja se reprodukuju na paru slušalica"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Delite zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Pređi na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uklj. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nije podešeno"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte u podešavanjima"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nema aktivnih režima}=1{Aktivan je {mode} režim}one{Aktivan je # režim}few{Aktivna su # režima}other{Aktivno je # režima}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas uznemiravati zvukovi i vibracije osim za alarme, podsetnike, događaje i pozivaoce koje navedete. I dalje ćete čuti sve što odaberete da pustite, uključujući muziku, video snimke i igre."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvuk isključen"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je uključen režim Ne uznemiravaj"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je uključen režim Ne uznemiravaj"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nije dostupno jer je uključeno: <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupno"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste podesili na vibraciju. Zvuk usluga pristupačnosti će možda biti isključen."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučite pokrete za tačped"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tastature i tačpeda"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučite pokrete za tačped, tasterske prečice i drugo"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Idi na početni ekran"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavno korišćene aplikacije"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Prevucite ulevo ili udesno sa tri prsta na tačpedu"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Svaka čast!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dovršili ste pokret za povratak."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Idi na početni ekran"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prevucite nagore sa tri prsta na tačpedu"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Odlično!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dovršili ste pokret za povratak na početnu stranicu."</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaži nedavno korišćene aplikacije"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prevucite nagore i zadržite sa tri prsta na tačpedu"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Odlično!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dovršili ste pokret za prikazivanje nedavno korišćenih aplikacija."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite taster radnji na tastaturi"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dovršili ste pokret za prikazivanje svih aplikacija."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index cdaca7e..526a11e 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Абагуліць аўдыя"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ідзе абагульванне аўдыя"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"адкрыць налады абагульвання аўдыя"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музыка і відэа на гэтай прыладзе будуць прайгравацца праз абедзве пары навушнікаў"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Абагульвайце аўдыя"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> і <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Пераключыцца на прыладу \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Уключана"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Уключана • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Выключана"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Не зададзена"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Адкрыць налады"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Актыўных рэжымаў няма}=1{Рэжым \"{mode}\" актыўны}one{# рэжым актыўны}few{# рэжымы актыўныя}many{# рэжымаў актыўныя}other{# рэжыму актыўныя}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будуць турбаваць гукі і вібрацыя, за выключэннем будзільнікаў, напамінаў, падзей і выбраных вамі абанентаў. Вы будзеце чуць усё, што ўключыце, у тым ліку музыку, відэа і гульні."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недаступна, бо выключаны гук выклікаў"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недаступна, бо ўключаны рэжым \"Не турбаваць\""</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Недаступна, бо ўключаны рэжым \"Не турбаваць\""</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недаступна, бо ўключаны рэжым \"<xliff:g id="MODE">%s</xliff:g>\""</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недаступна"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дакраніцеся, каб уключыць гук."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дакраніцеся, каб уключыць вібрацыю. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Паказваць дэманстрацыйны рэжым"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Будзільнік"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"Уключаны рэжым \"<xliff:g id="MODENAME">%1$s</xliff:g>\""</string>
     <string name="wallet_title" msgid="5369767670735827105">"Кашалёк"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Наладзьце картку, каб рабіць больш хуткія і бяспечныя куплі з дапамогай тэлефона"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Паказаць усе"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Азнаёмцеся з жэстамі для сэнсарнай панэлі"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігацыя з дапамогай клавіятуры і сэнсарнай панэлі"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Азнаёмцеся з жэстамі для сэнсарнай панэлі, спалучэннямі клавіш і г. д."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На галоўную старонку"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прагляд нядаўніх праграм"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Гатова"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Правядзіце па сэнсарнай панэлі трыма пальцамі ўлева ці ўправа"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Выдатна!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы навучыліся рабіць жэст вяртання."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На галоўны экран"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Правядзіце па сэнсарнай панэлі трыма пальцамі ўверх"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Выдатная праца!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Вы навучыліся рабіць жэст для пераходу на галоўны экран"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прагляд нядаўніх праграм"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Правядзіце па сэнсарнай панэлі трыма пальцамі ўверх і затрымайце пальцы"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Выдатная праца!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы скончылі вывучэнне жэсту для прагляду нядаўніх праграм."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Глядзець усе праграмы"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Націсніце клавішу дзеяння на клавіятуры"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Выдатна!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы навучыліся рабіць жэст для прагляду ўсіх праграм"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 79fc4b2..b843068 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделяне на звука"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Звукът се споделя"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"отваряне на настройките за споделяне на звука"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музиката и видеоклиповете на това устройство ще се възпроизвеждат и на двата чифта слушалки"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Споделяне на звука ви"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Превключване към <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Вкл."</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Вкл. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Изкл."</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Не е зададено"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управление от настройките"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Няма активни режими}=1{Режимът „{mode}“ е активен}other{# активни режима}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Няма да бъдете обезпокоявани от звуци и вибрирания освен от будилници, напомняния, събития и обаждания от посочени от вас контакти. Пак ще чувате всичко, което изберете да се пусне, включително музика, видеоклипове и игри."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Не е налице, защото звъненето е спряно"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Не е налице, защото режимът „Не безпокойте“ е вкл."</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Не е налице, защото „Не безпокойте“ е вкл."</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Не е налице, защото режимът „<xliff:g id="MODE">%s</xliff:g>“ е включен"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Не е налице"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Докоснете, за да включите отново звука."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Докоснете, за да зададете вибриране. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научете за жестовете със сензорния панел"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигирайте посредством клавиатурата и сензорния панел"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете за жестовете със сензорния панел, клавишните комбинации и др."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Към началния екран"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Преглед на скорошните приложения"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Прекарайте три пръста наляво или надясно по сензорния панел"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Чудесно!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Изпълнихте жеста за връщане назад."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Към началния екран"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Прекарайте три пръста нагоре по сензорния панел"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отлично!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Изпълнихте жеста за преминаване към началния екран"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Преглед на скорошните приложения"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Прекарайте три пръста нагоре по сензорния панел и задръжте"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Изпълнихте жеста за преглед на скорошните приложения."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Преглед на всички приложения"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натиснете клавиша за действия на клавиатурата си"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Изпълнихте жеста за преглед на всички приложения"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за дома"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 485221a..0732387 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিও শেয়ার করুন"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিও শেয়ার করা হচ্ছে"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"অডিও শেয়ার করার সেটিংসে যান"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"এই ডিভাইসের মিউজিক ও ভিডিও, হেডফোনের দুটি পেয়ারেই চলবে"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"আপনার অডিও শেয়ার করুন"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ও <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>-এ পাল্টান"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"চালু আছে"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"চালু আছে • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"বন্ধ আছে"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"সেট করা নেই"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"সেটিংসে গিয়ে ম্যানেজ করুন"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{কোনও মোড চালু নেই}=1{{mode} চালু আছে}one{# মোড চালু আছে}other{# মোড চালু আছে}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"অ্যালার্ম, রিমাইন্ডার, ইভেন্ট, এবং আপনার নির্দিষ্ট করে দেওয়া ব্যক্তিদের কল ছাড়া অন্য কোনও আওয়াজ বা ভাইব্রেশন হবে না। তবে সঙ্গীত, ভিডিও, এবং গেম সহ আপনি যা কিছু চালাবেন তার আওয়াজ শুনতে পাবেন।"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"রিং মিউট করা হয়েছে বলে উপলভ্য নেই"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'বিরক্ত করবে না\' মোড চালু থাকার জন্য উপলভ্য নেই"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"\'বিরক্ত করবে না\' মোড চালু থাকার জন্য উপলভ্য নেই"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> চালু হওয়ার জন্য এই সুবিধা উপলভ্য নেই"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"উপলভ্য নেই"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। সশব্দ করতে আলতো চাপুন।"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"টাচপ্যাডের জেসচার সম্পর্কে জানুন"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"আপনার কীবোর্ড এবং টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচপ্যাড জেসচার, কীবোর্ড শর্টকাট এবং আরও অনেক কিছু সম্পর্কে জানুন"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ফিরে যান"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"হোমে যান"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হয়ে গেছে"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ফিরে যান"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"আপনার টাচপ্যাডে তিনটি আঙুল ব্যবহার করে বাঁদিকে বা ডানদিকে সোয়াইপ করুন"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"সাবাস!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"জেসচার ব্যবহার করে কীভাবে ফিরে যাওয়া যায় সেই সম্পর্কে আপনি জেনেছেন।"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"হোমে যান"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপনার টাচপ্যাডে তিনটি আঙুলের সাহায্যে উপরের দিকে সোয়াইপ করুন"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"অসাধারণ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"জেসচার ব্যবহার করে কীভাবে হোমে ফিরে যাওয়া যায় সেই সম্পর্কে আপনি জেনেছেন"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"আপনার টাচপ্যাডে তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"অসাধারণ!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপের জেসচার দেখা সম্পূর্ণ করেছেন।"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"সব অ্যাপ দেখুন"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"আপনার কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"দারুণ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"আপনি \'সব অ্যাপের জেসচার দেখুন\' টিউটোরিয়াল সম্পূর্ণ করেছেন"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string>
@@ -1464,7 +1457,7 @@
     <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচারের শর্টকাট সরানো হয়েছে"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"কানেক্টিভিটি"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"অ্যাক্সেসিবিলিটি"</string>
-    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"উপযোগিতা"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ইউটিলিটি"</string>
     <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"গোপনীয়তা"</string>
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"অ্যাপের তরফ থেকে দেওয়া"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিসপ্লে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 4c79f48..b1146f4 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dijeli zvuk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Dijeljenje zvuka"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ulazak u postavke dijeljenja zvuka"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzika i videozapisi na uređaju će se reproducirati na oba para slušalica"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Dijelite zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Prebaci na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uključeno • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nije postavljeno"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte opcijom u postavkama"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nema aktivnih načina rada}=1{Način rada {mode} je aktivan}one{# način rada je aktivan}few{# načina rada su aktivna}other{# načina rada je aktivno}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivalaca koje odredite. I dalje ćete čuti sve što ste odabrali za reprodukciju, uključujući muziku, videozapise i igre."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno zbog isključenog zvona"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je funkcija Ne ometaj uključena"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je funkcija Ne ometaj uključena"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nije dostupno jer je način rada <xliff:g id="MODE">%s</xliff:g> uključen"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupno"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Saznajte više o pokretima na dodirnoj podlozi"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tastature i dodirne podloge"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima na dodirnoj podlozi, prečicama tastature i drugim opcijama"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Odlazak na početni ekran"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavne aplikacije"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Prevucite ulijevo ili udesno s tri prsta na dodirnoj podlozi"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Lijepo!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Savladali ste pokret za vraćanje."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Odlazak na početni ekran"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prevucite nagore s tri prsta na dodirnoj podlozi"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Sjajno!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Savladali ste pokret za otvaranje početnog ekrana"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaz nedavnih aplikacija"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavnih aplikacija."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Pogledajte sve aplikacije"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku radnji na tastaturi"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Odlično!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvršili ste pokret za prikaz svih aplikacija"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 1552c87..a836b97 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Comparteix l\'àudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"S\'està compartint l\'àudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"introduir la configuració de compartició d\'àudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La música i els vídeos d\'aquest dispositiu es reproduiran en tots dos parells d\'auriculars"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Comparteix l\'àudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Canvia a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activat • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desactivat"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"No definit"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestiona a la configuració"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No hi ha cap mode actiu}=1{{mode} està actiu}many{Hi ha # de modes actius}other{Hi ha # modes actius}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"No t\'interromprà cap so ni cap vibració, tret dels de les alarmes, recordatoris, esdeveniments i trucades de les persones que especifiquis. Continuaràs sentint tot allò que decideixis reproduir, com ara música, vídeos i jocs."</string>
@@ -579,7 +582,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"No hi ha cap notificació"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"No hi ha cap notificació nova"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La moderació de notificacions està activada"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volum i les alertes del dispositiu es redueixen automàticament durant 2 minuts quan reps massa notificacions alhora."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volum i les alertes del dispositiu es redueixen automàticament durant 2 minuts com a màxim quan reps massa notificacions alhora."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactiva"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueja per veure notif. anteriors"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Els teus pares gestionen aquest dispositiu"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible perquè el so està silenciat"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible perquè No molestis està activat"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible perquè No molestis està activat"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"No està disponible perquè <xliff:g id="MODE">%s</xliff:g> està activat"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"No disponible"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca per activar el so."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca per activar la vibració. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprèn els gestos del ratolí tàctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega amb el teclat i el ratolí tàctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprèn els gestos del ratolí tàctil, les tecles de drecera i més"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Torna"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pàgina d\'inici"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Mostra les aplicacions recents"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Llisca cap a l\'esquerra o cap a la dreta amb tres dits al ratolí tàctil"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Molt bé!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Has completat el gest per tornar enrere."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ves a la pantalla d\'inici"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Llisca cap amunt amb tres dits al ratolí tàctil"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Ben fet!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Has completat el gest per anar a la pantalla d\'inici"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Mostra les aplicacions recents"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Llisca cap amunt amb tres dits i mantén premut al ratolí tàctil"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ben fet!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completat el gest per veure les aplicacions recents."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Mostra totes les aplicacions"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prem la tecla d\'acció al teclat"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Enhorabona!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completat el gest per veure totes les aplicacions"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 1689503..70bdb62 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sdílet zvuk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zvuk se sdílí"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"přejdete do nastavení sdílení zvuku"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Hudba a videa z tohoto zařízení se budou přehrávat na obou párech sluchátek"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Sdílet zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> a <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Přepnout na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuto"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Zapnuto • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuto"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nenastaveno"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Spravovat v nastavení"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Žádné aktivní}=1{Režim {mode} je aktivní}few{# režimy jsou aktivní}many{# režimu je aktivních}other{# režimů je aktivních}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků, upozornění, událostí a volajících, které zadáte. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, protože vyzvánění je ztlumené"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupné, protože je zapnutý režim Nerušit"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupné – je zapnutý režim Nerušit"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nedostupné, protože je zapnutý režim <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupné"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnete zvuk."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujete režim vibrací. Služby přístupnosti mohou být ztlumeny."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string>
@@ -931,8 +936,8 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozice není platná."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string>
-    <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Karta byla přidána"</string>
-    <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Karta byla odstraněna"</string>
+    <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Dlaždice byla přidána"</string>
+    <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Dlaždice byla odstraněna"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučte se gesta touchpadu"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigujte pomocí klávesnice a touchpadu"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte se gesta touchpadu, klávesové zkratky a další"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zpět"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Přejít na domovskou stránku"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazit nedávné aplikace"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Přejeďte po touchpadu třemi prsty doleva nebo doprava"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Skvělé!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dokončili jste gesto pro přechod zpět."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Přejít na plochu"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Přejeďte po touchpadu třemi prsty nahoru"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Výborně!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dokončili jste gesto pro přechod na plochu"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazit nedávné aplikace"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Přejeďte po touchpadu třemi prsty nahoru a podržte je"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Výborně!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Provedli jste gesto pro zobrazení nedávných aplikací."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazit všechny aplikace"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborně!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dokončili jste gesto k zobrazení všech aplikací"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 729473c..eeece4c 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"angive indstillinger for lyddeling"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik og videoer fra denne enhed afspilles på begge par høretelefoner"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Del din lyd"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> og <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Skift til <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Til"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Til • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Fra"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Ikke konfigureret"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrer i indstillingerne"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ingen aktive tilstande}=1{{mode} er aktiv}one{# tilstand er aktiv}other{# tilstande er aktive}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Du bliver ikke forstyrret af lyde eller vibrationer, undtagen fra alarmer, påmindelser, begivenheder og opkald fra udvalgte personer, du selv angiver. Du kan stadig høre alt, du vælger at afspille, f.eks. musik, videoer og spil."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ikke muligt, da ringetonen er slået fra"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ikke tilgængelig, fordi Forstyr ikke er aktiveret"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Ikke tilgængelig, fordi Forstyr ikke er aktiveret"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ikke tilgængelig, fordi <xliff:g id="MODE">%s</xliff:g> er aktiveret"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ikke tilgængelig"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryk for at slå lyden til."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryk for at konfigurere til at vibrere. Tilgængelighedstjenester kan blive deaktiveret."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Se bevægelser på touchpladen"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviger ved hjælp af dit tastatur og din touchplade"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Se bevægelser på touchpladen, tastaturgenveje m.m."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbage"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se seneste apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Udfør"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Stryg til venstre eller højre med tre fingre på touchpladen"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Sådan!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har fuldført bevægelsen for Gå tilbage."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gå til startskærmen"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Stryg opad med tre fingre på touchpladen"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Flot!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du har udført bevægelsen for at gå til startsiden"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Se seneste apps"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Stryg opad, og hold tre fingre på touchpladen"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Godt klaret!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har udført bevægelsen for at se de seneste apps."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tryk på handlingstasten på dit tastatur"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Flot klaret!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har udført bevægelsen for at se alle apps"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 655b7a2..9570314 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioinhalte freigeben"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioinhalte werden freigegeben"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"Einstellungen für die Audiofreigabe eingeben"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik und Videos auf diesem Gerät werden auf beiden Kopfhörerpaaren abgespielt"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audioinhalte freigeben"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> und <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Zu <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> wechseln"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"An"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"An • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Aus"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nicht festgelegt"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"In den Einstellungen verwalten"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Keine aktiven Modi}=1{{mode} aktiv}other{# aktiv}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Klingeltöne und die Vibration werden deaktiviert, außer für Weckrufe, Erinnerungen, Termine sowie Anrufe von zuvor von dir festgelegten Personen. Du hörst jedoch weiterhin Sound, wenn du dir Musik anhörst, Videos ansiehst oder Spiele spielst."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nicht verfügbar, da Klingelton stumm"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nicht verfügbar, weil „Bitte nicht stören“ an ist"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nicht verfügbar, weil „Bitte nicht stören“ an"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nicht verfügbar, weil <xliff:g id="MODE">%s</xliff:g> aktiviert ist"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nicht verfügbar"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Zum Aufheben der Stummschaltung tippen."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Demomodus anzeigen"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Weckruf"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"„<xliff:g id="MODENAME">%1$s</xliff:g>“ ist aktiviert"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Füge eine Zahlungsmethode hinzu, um noch schneller und sicherer mit deinem Smartphone zu bezahlen"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Alle anzeigen"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Informationen zu Touchpad-Gesten"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigation mit Tastatur und Touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Informationen zu Touchpad-Gesten, Tastenkombinationen und mehr"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zurück"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zur Startseite"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Letzte Apps aufrufen"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fertig"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Wische mit drei Fingern auf dem Touchpad nach links oder rechts"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Sehr gut!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du hast den Schritt für die „Zurück“-Geste abgeschlossen."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Startbildschirm"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Wische an einer beliebigen Stelle auf dem Touchpad mit drei Fingern nach oben"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Gut gemacht!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du hast den Schritt für die „Startbildschirm“-Touch-Geste abgeschlossen"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Letzte Apps aufrufen"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Wische mit drei Fingern nach oben und halte das Touchpad gedrückt"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Gut gemacht!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du hast das Tutorial für die Touch-Geste zum Aufrufen der zuletzt verwendeten Apps abgeschlossen."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle Apps anzeigen"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Drücke die Aktionstaste auf deiner Tastatur"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Perfekt!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du hast das Tutorial für die Touch-Geste zum Aufrufen aller Apps abgeschlossen"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 3c99fbb..5551740 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Κοινή χρήση ήχου"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Κοινή χρήση ήχου σε εξέλιξη"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"είσοδο στις ρυθμίσεις κοινής χρήσης ήχου"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Η μουσική και τα βίντεο αυτής της συσκευής θα αναπαράγονται και στα δύο ζευγάρια ακουστικών"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Κοινή χρήση του ήχου σας"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> και <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Εναλλαγή σε <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Ενεργό"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ενεργή • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Ανενεργό"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Δεν έχει οριστεί"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Διαχείριση στις ρυθμίσεις"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Δεν υπάρχει ενεργή λειτουργία}=1{{mode} ενεργή λειτουργία}other{# ενεργές λειτουργίες}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Δεν θα ενοχλείστε από ήχους και δονήσεις, παρά μόνο από ξυπνητήρια, υπενθυμίσεις, συμβάντα και καλούντες που έχετε καθορίσει. Θα εξακολουθείτε να ακούτε όλο το περιεχόμενο που επιλέγετε να αναπαραγάγετε, συμπεριλαμβανομένης της μουσικής, των βίντεο και των παιχνιδιών."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Μη διαθέσιμο λόγω σίγασης ήχου κλήσης"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Μη διαθ. επειδή η λειτ. Μην ενοχλείτε είναι ενεργή"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Μη διαθ. επειδή η λειτ. Μην ενοχλείτε είναι ενεργή"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Δεν διατίθεται επειδή η λειτουργία <xliff:g id="MODE">%s</xliff:g> είναι ενεργή"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Δεν διατίθεται"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Πατήστε για κατάργηση σίγασης."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Πατήστε για ενεργοποιήσετε τη δόνηση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Μάθετε κινήσεις επιφάνειας αφής"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Πλοήγηση με το πληκτρολόγιο και την επιφάνεια αφής"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Μάθετε κινήσεις επιφάνειας αφής, συντομεύσεις πληκτρολογίου και άλλα"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Επιστροφή"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Αρχική"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Προβολή πρόσφατων εφαρμογών"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Τέλος"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Επιστροφή"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Σύρετε προς τα αριστερά ή τα δεξιά με τρία δάχτυλα στην επιφάνεια αφής"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Ωραία!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ολοκληρώσατε την κίνηση επιστροφής."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Αρχική"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Σύρετε προς τα επάνω με τρία δάχτυλα στην επιφάνεια αφής"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Μπράβο!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Προβολή πρόσφατων εφαρμογών"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Σύρετε προς τα επάνω με τρία δάχτυλα στην επιφάνεια αφής και μην τα σηκώσετε"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Μπράβο!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ολοκληρώσατε την κίνηση για την προβολή πρόσφατων εφαρμογών."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Προβολή όλων των εφαρμογών"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Μπράβο!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ολοκληρώσατε την κίνηση για την προβολή όλων των εφαρμογών"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 4570c0a..df9d210 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Not set"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No active modes}=1{{mode} is active}other{# modes are active}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe left or right using three fingers on your touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Nice!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"You completed the go back gesture."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Go home"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe up with three fingers on your touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Well done!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"You completed the go home gesture"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe up and hold using three fingers on your touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3d33fa0..28cb9a3 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -672,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 4570c0a..df9d210 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Not set"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No active modes}=1{{mode} is active}other{# modes are active}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe left or right using three fingers on your touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Nice!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"You completed the go back gesture."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Go home"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe up with three fingers on your touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Well done!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"You completed the go home gesture"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe up and hold using three fingers on your touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 4570c0a..df9d210 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"enter audio sharing settings"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"This device\'s music and videos will play on both pairs of headphones"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Share your audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> and <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Switch to <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Not set"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No active modes}=1{{mode} is active}other{# modes are active}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Unavailable because <xliff:g id="MODE">%s</xliff:g> is on"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Unavailable"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe left or right using three fingers on your touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Nice!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"You completed the go back gesture."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Go home"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe up with three fingers on your touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Well done!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"You completed the go home gesture"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe up and hold using three fingers on your touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Press the action key on your keyboard"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Well done!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"You completed the view all apps gesture"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 3d157c7..b70fdeb 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -672,6 +672,10 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎Unavailable because ring is muted‎‏‎‎‏‎"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎Unavailable because Do Not Disturb is on‎‏‎‎‏‎"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‏‏‎‏‎Unavailable because Do Not Disturb is on‎‏‎‎‏‎"</string>
+    <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) -->
+    <skip />
+    <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) -->
+    <skip />
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎%1$s. Tap to unmute.‎‏‎‎‏‎"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎%1$s. Tap to set to vibrate. Accessibility services may be muted.‎‏‎‎‏‎"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎%1$s. Tap to mute. Accessibility services may be muted.‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 6c80a2c..e39b2a4 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ingresar la configuración de uso compartido de audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La música y los videos de este dispositivo se reproducirán en ambos pares de auriculares"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Comparte tu audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> y <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Cambiar a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sí • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Sin establecer"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrar en configuración"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No hay modos activos}=1{{mode} está activo}many{# de modos están activos}other{# modos están activos}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas de los emisores que especifiques. Podrás escuchar el contenido que reproduzcas, como música, videos y juegos."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible por timbre silenciado"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible si está activado No interrumpir"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible si está activado No interrumpir"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"No disponible porque se activó <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"No disponible"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Presiona para dejar de silenciar."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Presiona para establecer el modo vibración. Es posible que los servicios de accesibilidad estén silenciados."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende los gestos del panel táctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega con el teclado y el panel táctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende sobre los gestos del panel táctil, las combinaciones de teclas y mucho más"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atrás"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página de inicio"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recientes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Listo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Desliza hacia la izquierda o la derecha con tres dedos en el panel táctil"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"¡Muy bien!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaste el gesto para ir atrás."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir a la página principal"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Desliza hacia arriba con tres dedos en el panel táctil"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"¡Bien hecho!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaste el gesto para ir a la página principal"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver apps recientes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba con tres dedos en el panel táctil y mantenlos presionados."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaste el gesto para ver las apps recientes."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Presiona la tecla de acción en el teclado"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Bien hecho!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaste el gesto para ver todas las apps"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index f77119e..6d77be4 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acceder a las opciones para compartir audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Los vídeos y la música de este dispositivo se reproducirán en ambos auriculares"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Comparte tu audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> y <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Cambiar a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Sin definir"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestionar en los ajustes"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No hay modos activos}=1{{mode} está activo}many{Hay # modos activos}other{Hay # modos activos}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas, los recordatorios, los eventos y las llamadas que especifiques. Seguirás escuchando el contenido que quieras reproducir, como música, vídeos y juegos."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible (el tono está silenciado)"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible porque No molestar está activado"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible porque No molestar está activado"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"No disponible porque <xliff:g id="MODE">%s</xliff:g> está activado"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"No disponible"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar el sonido."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para poner el dispositivo en vibración. Los servicios de accesibilidad pueden silenciarse."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende gestos del panel táctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Desplázate con el teclado y el panel táctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende gestos del panel táctil, combinaciones de teclas y más"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a Inicio"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver aplicaciones recientes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hecho"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Desliza hacia la izquierda o la derecha con tres dedos en el panel táctil"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"¡Genial!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Has completado el gesto para volver."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir a Inicio"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Desliza hacia arriba con tres dedos en el panel táctil"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"¡Bien hecho!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Has completado el gesto para ir a la pantalla de inicio"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver aplicaciones recientes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba y mantén pulsado con tres dedos en el panel táctil"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completado el gesto para ver las aplicaciones recientes."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las aplicaciones"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pulsa la tecla de acción de tu teclado"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"¡Muy bien!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Has completado el gesto para ver todas las aplicaciones"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3323a3f..ed14409 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Jaga heli"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Heli jagamine"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"heli jagamise seadete sisestamiseks"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Selle seadme muusikat ja videoid esitatakse mõlemas paaris kõrvaklappides"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Heli jagamine"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ja <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Vaheta seadmele <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Sees"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sees • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Väljas"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Määramata"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Seadetes halamine"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aktiivsed režiimid puuduvad}=1{{mode} on aktiivne}other{# režiimi on aktiivsed}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Helid ja värinad ei sega teid. Kuulete siiski enda määratud äratusi, meeldetuletusi, sündmusi ja helistajaid. Samuti kuulete kõike, mille esitamise ise valite, sh muusika, videod ja mängud."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Pole saadaval, kuna helin on vaigistatud"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Pole saadaval, kuna režiim Mitte segada on sees"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Pole saadaval, kuna režiim Mitte segada on sees"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Pole saadaval, kuna režiim <xliff:g id="MODE">%s</xliff:g> on sisse lülitatud"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Pole saadaval"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Puudutage vaigistuse tühistamiseks."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Õppige puuteplaadi liigutusi"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeerige klaviatuuri ja puuteplaadi abil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Õppige puuteplaadi liigutusi, klaviatuuri otseteid ja palju muud"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Mine tagasi"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Avalehele"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Hiljutiste rakenduste vaatamine"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pühkige puuteplaadil kolme sõrmega vasakule või paremale"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Tubli töö!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Tegite tagasiliikumise liigutuse."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Avalehele"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pühkige puuteplaadil kolme sõrmega üles"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Väga hea!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Tegite avakuvale minemise liigutuse"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Hiljutiste rakenduste vaatamine"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pühkige üles ja hoidke kolme sõrme puuteplaadil"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Väga hea!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Lõpetasite hiljutiste rakenduste vaatamise liigutuse."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Kõigi rakenduste kuvamine"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Vajutage klaviatuuril toiminguklahvi"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hästi tehtud!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tegite kõigi rakenduste vaatamise liigutuse"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 246c230..2fb75d3 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partekatu audioa"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioa partekatzen"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audioa partekatzeko ezarpenetan sartzeko"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Gailu honetako musika eta bideoak 2 entzungailu pareetan erreproduzituko dira"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partekatu zure audioa"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> eta <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Erabili <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Aktibatuta"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktibo • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desaktibatuta"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Ezarri gabe"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Kudeatu ezarpenetan"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ez dago modurik aktibo}=1{\"{mode}\" aktibo dago}other{# modu aktibo daude}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak, gertaera eta abisuen tonuak, eta aukeratzen dituzun deitzaileen dei-tonuak joko ditu. Bestalde, zuk erreproduzitutako guztia entzungo duzu, besteak beste, musika, bideoak eta jokoak."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ez dago erabilgarri, tonua desaktibatuta dagoelako"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ez dago erabilgarri, ez molestatzeko modua aktibatuta baitago"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Ez dago erabilgarri, ez molestatzeko modua aktibatuta baitago"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ez dago erabilgarri, <xliff:g id="MODE">%s</xliff:g> aktibatuta dagoelako"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ez dago erabilgarri"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sakatu audioa aktibatzeko."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Erakutsi demo modua"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> aktibatuta dago"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Diru-zorroa"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Konfiguratu erosketa bizkorrago eta seguruagoak egiteko telefonoarekin"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Erakutsi guztiak"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Ikasi ukipen-paneleko keinuak"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Nabigatu teklatua eta ukipen-panela erabilita"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Ikasi ukipen-paneleko keinuak, lasterbideak eta abar"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Egin atzera"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Joan orri nagusira"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ikusi azkenaldiko aplikazioak"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Eginda"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Egin atzera"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pasatu 3 hatz ezkerrera edo eskuinera ukipen-panelean"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Ederki!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ikasi duzu atzera egiteko keinua."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Joan orri nagusira"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pasatu 3 hatz gora ukipen-panelean"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bikain!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ikasi duzu orri nagusira joateko keinua"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ikusi azkenaldiko aplikazioak"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pasatu 3 hatz gora eta eduki sakatuta ukipen-panelean"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bikain!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Osatu duzu azkenaldiko aplikazioak ikusteko keinua."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ikusi aplikazio guztiak"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Sakatu teklatuko ekintza-tekla"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bikain!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Osatu duzu aplikazio guztiak ikusteko keinua"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 29a2739..b18c84a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"هم‌رسانی صدا"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"درحال هم‌رسانی صدا"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"وارد شدن به تنظیمات «اشتراک صدا»"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"موسیقی و ویدیوهای این دستگاه در هر دو گوشی هدفون پخش خواهد شد"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"هم‌رسانی کردن صدای دستگاه"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"‫<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> و <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"رفتن به <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"روشن"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"روشن • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"خاموش"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"تنظیم نشده است"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"مدیریت در تنظیمات"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{حالت فعالی وجود ندارد}=1{‫{mode} فعال است}one{‫# حالت فعال است}other{‫# حالت فعال است}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"به‌جز هشدارها، یادآوری‌ها، رویدادها و تماس‌گیرندگانی که خودتان مشخص می‌کنید، هیچ صدا و لرزشی نخواهید داشت. همچنان صدای مواردی را که پخش می‌کنید می‌شنوید (ازجمله صدای موسیقی، ویدیو و بازی)."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"دردسترس نیست، چون زنگ بی‌صدا شده است"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"دردسترس نیست زیرا <xliff:g id="MODE">%s</xliff:g> روشن است"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"دردسترس نیست"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏%1$s. برای باصدا کردن تک‌ضرب بزنید."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏%1$s. برای تنظیم روی لرزش تک‌ضرب بزنید. ممکن است سرویس‌های دسترس‌پذیری بی‌صدا شوند."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏%1$s. برای صامت کردن تک‌ضرب بزنید. ممکن است سرویس‌های دسترس‌پذیری صامت شود."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"آشنایی با اشاره‌های صفحه لمسی"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"پیمایش کردن بااستفاده از صفحه‌کلید و صفحه لمسی"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"آشنایی با اشاره‌های صفحه لمسی، میان‌برهای صفحه‌کلید، و موارد دیگر"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"برگشتن"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"رفتن به صفحه اصلی"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"مشاهده کردن برنامه‌های اخیر"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تمام"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"برگشتن"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"با سه انگشت روی صفحه لمسی تند به چپ یا راست بکشید."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"چه خوب!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"اشاره برگشت را تکمیل کردید."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"رفتن به صفحه اصلی"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"با سه انگشت روی صفحه لمسی تند به بالا بکشید"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"عالی است!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"اشاره رفتن به صفحه اصلی را تکمیل کردید"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"مشاهده کردن برنامه‌های اخیر"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"با سه انگشت روی صفحه لمسی تند به بالا بکشید و نگه دارید"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"عالی است!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"اشاره مشاهده برنامه‌های اخیر را انجام دادید"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"مشاهده همه برنامه‌ها"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"دکمه کنش را روی صفحه لمسی فشار دهید"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"عالی بود!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"اشاره مشاهده همه برنامه‌ها را انجام دادید"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پس‌زمینه صفحه‌کلید"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏سطح %1$d از %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index e93ac1a..4602741 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Jaa audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audiota jaetaan"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"lisätäksesi audion jakamisasetukset"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Laitteen musiikki ja videot toistetaan molemmilla kuulokepareilla"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Jaa audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ja <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Vaihda: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Päällä"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Päällä • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Pois päältä"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Ei asetettu"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Muuta asetuksista"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ei aktiivisia tiloja}=1{{mode} on aktiivinen}other{# tilaa on aktiivisena}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Äänet ja värinät eivät häiritse sinua, paitsi jos ne ovat hälytyksiä, muistutuksia, tapahtumia tai määrittämiäsi soittajia. Kuulet edelleen kaiken valitsemasi sisällön, kuten musiikin, videot ja pelit."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ei käytettävissä, soittoääni mykistetty"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ei saatavilla, koska Älä häiritse on päällä"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Ei saatavilla, koska Älä häiritse on päällä"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ei käytettävissä, koska <xliff:g id="MODE">%s</xliff:g> on päällä"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ei saatavilla"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Poista mykistys koskettamalla."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Siirry värinätilaan koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Opettele kosketuslevyn eleitä"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Siirry käyttämällä näppäimistöä ja kosketuslevyä"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Opettele kosketuslevyn eleitä, pikanäppäimiä ja muuta"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Takaisin"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Siirry etusivulle"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Katso viimeisimmät sovellukset"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Takaisin"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pyyhkäise kosketuslevyllä vasemmalle tai oikealle kolmella sormella"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Hienoa!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Olet oppinut Takaisin-eleen."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Siirry etusivulle"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pyyhkäise ylös kolmella sormella kosketuslevyllä"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Hienoa!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Olet oppinut aloitusnäytölle palaamiseleen"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Katso viimeisimmät sovellukset"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Hienoa!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Olet oppinut Katso viimeisimmät sovellukset ‑eleen."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Näytä kaikki sovellukset"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hienoa!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c58ad93..77434a5 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partager l\'audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Partage de l\'audio en cours"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"entrer les paramètres de partage audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La musique et les vidéos de cet appareil peuvent être lues sur les deux paires d\'écouteurs"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partager votre audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> et <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Passer à <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activé • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Non défini"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gérer dans les paramètres"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aucun mode actif}=1{Le mode {mode} est actif}one{# mode est actif}many{# de modes sont actifs}other{# modes sont actifs}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par les sons et les vibrations, sauf pour les alarmes, les rappels, les événements et les appelants que vous sélectionnez. Vous entendrez tout ce que vous choisissez d\'écouter, y compris la musique, les vidéos et les jeux."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Inaccessible : sonnerie en sourdine"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Inaccessible parce que Ne pas déranger est activée"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Inaccessible parce que Ne pas déranger est activée"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non accessible, car le mode <xliff:g id="MODE">%s</xliff:g> est activé"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non accessible"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Touchez pour réactiver le son."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Afficher le mode Démo"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"Le mode <xliff:g id="MODENAME">%1$s</xliff:g> est activé"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Préparez-vous à faire des achats plus rapidement et de façon plus sûre avec votre téléphone"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Tout afficher"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Apprenez à utiliser les gestes du pavé tactile"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Apprenez les gestes du pavé tactile, les raccourcis-clavier et bien plus encore"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à la page d\'accueil"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Balayez votre pavé tactile vers la gauche ou vers la droite avec trois doigts"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bien!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous avez appris le geste de retour en arrière."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Retour à la page d\'accueil"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Balayez votre pavé tactile vers le haut avec trois doigts"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bon travail!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous avez appris le geste pour revenir à l\'écran d\'accueil"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Balayez votre pavé tactile vers le haut avec trois doigts, puis maintenez-les en place"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bon travail!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez effectué le geste pour afficher les applis récentes."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applis"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Félicitations!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index cf1cae9d..fa16862 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partager le contenu audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio partagé"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"accéder aux paramètres de partage audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La musique et les vidéos de cet appareil peuvent être lues sur les deux casques"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partager votre audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> et <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Passer à <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Activé"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activé • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Désactivé"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Non défini"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gérer dans les paramètres"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aucun mode actif}=1{{mode} est actif}one{# mode est actif}many{# de modes sont actifs}other{# modes sont actifs}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes, des rappels, des événements et des appelants de votre choix. Vous entendrez encore les sons que vous choisirez de jouer, notamment la musique, les vidéos et les jeux."</string>
@@ -579,7 +582,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La limitation des notifications est activée"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes max. quand vous recevez trop de notifications à la fois."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes maximum quand vous recevez trop de notifications à la fois."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverrouiller pour voir anciennes notifications"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Cet appareil est géré par tes parents"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponible, car la sonnerie est coupée"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponible car Ne pas déranger est activé"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponible car Ne pas déranger est activé"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponible, car le mode <xliff:g id="MODE">%s</xliff:g> est activé"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponible"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Appuyez pour ne plus ignorer."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Appuyez pour mettre en mode vibreur. Vous pouvez ignorer les services d\'accessibilité."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Découvrir les gestes au pavé tactile"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviguer à l\'aide de votre clavier et de votre pavé tactile"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Découvrir les gestes au pavé tactile, les raccourcis clavier et plus encore"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Retour"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Retour à l\'accueil"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Balayez vers la gauche ou la droite avec trois doigts sur le pavé tactile"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bravo !"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Vous avez appris le geste pour revenir en arrière."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Retour à l\'accueil"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Balayez vers le haut avec trois doigts sur le pavé tactile"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bravo !"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Vous avez appris le geste pour revenir à l\'écran d\'accueil"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Avec trois doigts, balayez le pavé tactile vers le haut et maintenez la position"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bravo !"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez appris le geste pour afficher les applis récentes"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Afficher toutes les applications"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Appuyez sur la touche d\'action de votre clavier"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bravo !"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Vous avez appris le geste pour afficher toutes les applis"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index dc0f216..1d2c785 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartindo audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"configurar o uso compartido de audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"A música e os vídeos deste dispositivo reproduciranse nos dous pares de auriculares"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Compartir o audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Cambiar a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activo • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Sen configurar"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Xestionar na configuración"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Non hai ningún modo activo}=1{{mode} está activo}other{Hai # modos activos}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Non te molestará ningún son nin vibración, agás os procedentes de alarmas, recordatorios, eventos e os emisores de chamada especificados. Seguirás escoitando todo aquilo que decidas reproducir, mesmo a música, os vídeos e os xogos."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non dispoñible: o son está silenciado"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Non dispoñible: Non molestar está activado"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Non dispoñible: Non molestar está activado"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non está dispoñible porque se activou o modo <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non dispoñible"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar o son."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende a usar os xestos do panel táctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega co teclado e o panel táctil"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende a usar os xestos do panel táctil, atallos de teclado e moito máis"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir ao inicio"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Consultar aplicacións recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Feito"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pasa tres dedos cara á esquerda ou cara á dereita no panel táctil"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Excelente!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaches o xesto de retroceso."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir ao inicio"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pasa tres dedos cara arriba no panel táctil"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelente traballo."</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaches o titorial do xesto de ir ao inicio"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Consultar aplicacións recentes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pasa tres dedos cara arriba e mantenos premidos no panel táctil"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Moi ben!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaches o titorial do xesto de consultar aplicacións recentes."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as aplicacións"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Preme a tecla de acción do teclado"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben feito!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Completaches o titorial do xesto de ver todas as aplicacións"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 866535e..c6014b1 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ઑડિયો શેર કરો"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ઑડિયો શેર કરી રહ્યાં છીએ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ઑડિયો શેરિંગ સેટિંગ દાખલ કરો"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"આ ડિવાઇસનું મ્યુઝિક અને વીડિયો હૅડફોનની બન્ને જોડીઓ પર ચાલશે"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"તમારો ઑડિયો શેર કરો"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> અને <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> પર સ્વિચ કરો"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ચાલુ"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ચાલુ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"બંધ"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"સેટઅપ કર્યું નથી"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"સેટિંગમાં જઈને મેનેજ કરો"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{કોઈ સક્રિય મોડ નથી}=1{{mode} સક્રિય છે}one{# મોડ સક્રિય છે}other{# મોડ સક્રિય છે}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"અલાર્મ, રિમાઇન્ડર, ઇવેન્ટ અને તમે ઉલ્લેખ કરો તે કૉલર સિવાય તમને ધ્વનિ કે વાઇબ્રેશન દ્વારા ખલેલ પહોંચાડવામાં આવશે નહીં. સંગીત, વીડિઓ અને રમતો સહિત તમે જે કંઈપણ ચલાવવાનું પસંદ કરશો તે સંભળાતું રહેશે."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"રિંગ મ્યૂટ કરી હોવાના કારણે અનુપલબ્ધ છે"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ઉપલબ્ધ નથી, કારણ કે ખલેલ પાડશો નહીં મોડ ચાલુ છે"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"ઉપલબ્ધ નથી, કારણ કે ખલેલ પાડશો નહીં મોડ ચાલુ છે"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ચાલુ હોવાથી અનુપલબ્ધ છે"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"અનુપલબ્ધ છે"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. અનમ્યૂટ કરવા માટે ટૅપ કરો."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. વાઇબ્રેટ પર સેટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ટચપૅડના સંકેતો વિશે જાણો"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"તમારા કીબોર્ડ અને ટચપૅડ વડે નૅવિગેટ કરો"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ટચપૅડના સંકેતો અને કીબોર્ડના શૉર્ટકટ જેવું બીજું ઘણું જાણો"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"પાછા જાઓ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"હોમ પર જાઓ"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"તાજેતરની ઍપ જુઓ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"થઈ ગયું"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"પાછા જાઓ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"તમારા ટચપૅડ પર ત્રણ આંગળીનો ઉપયોગ કરીને ડાબે કે જમણે સ્વાઇપ કરો"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"સરસ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"તમે પાછા જવાનો સંકેત પૂર્ણ કર્યો છે."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"હોમ પર જાઓ"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"તમારા ટચપૅડ પર ત્રણ આંગળી વડે ઉપરની તરફ સ્વાઇપ કરો"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ખૂબ સરસ કામ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"તમે હોમ સ્ક્રીન પર જવાનો સંકેત પૂર્ણ કર્યો"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"તાજેતરની ઍપ જુઓ"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"તમારા ટચપૅડ પર ત્રણ આંગળીઓનો ઉપયોગ કરીને ઉપર સ્વાઇપ કરો અને દબાવી રાખો"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ખૂબ સરસ કામ!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"તમે \"તાજેતરની ઍપ જુઓ\" સંકેત પૂર્ણ કર્યો."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"બધી ઍપ જુઓ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"વાહ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"તમે \"બધી ઍપ જુઓ\" સંકેત પૂર્ણ કર્યો"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 7a1bf83a..28a4f2b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडियो शेयर करें"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडियो शेयर किया जा रहा है"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ऑडियो शेयर करने की सेटिंग जोड़ें"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"इस डिवाइस के संगीत और वीडियो, दोनों हेडफ़ोन पर चलेंगे"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"अपना ऑडियो शेयर करें"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> और <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> पर स्विच करें"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"चालू है"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • पर"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"बंद है"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट नहीं है"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग में जाकर मैनेज करें"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कोई मोड चालू नहीं है}=1{{mode} चालू है}one{# मोड चालू है}other{# मोड चालू हैं}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"आपको अलार्म, रिमाइंडर, इवेंट और चुनिंदा कॉल करने वालों के अलावा किसी और तरह से (आवाज़ करके और थरथरा कर ) परेशान नहीं किया जाएगा. आप फिर भी संगीत, वीडियो और गेम सहित अपना चुना हुआ सब कुछ सुन सकते हैं."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"आवाज़ नहीं आएगी, क्योंकि रिंग म्यूट है"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"सुविधा बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"आवाज़ बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> चालू होने की वजह से यह सुविधा उपलब्ध नहीं है"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"उपलब्ध नहीं है"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करने के लिए टैप करें."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. कंपन पर सेट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string>
@@ -1317,7 +1322,7 @@
     <string name="log_access_confirmation_title" msgid="4843557604739943395">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> को डिवाइस लॉग ऐक्सेस करने की अनुमति देनी है?"</string>
     <string name="log_access_confirmation_allow" msgid="752147861593202968">"एक बार ऐक्सेस करने की अनुमति दें"</string>
     <string name="log_access_confirmation_deny" msgid="2389461495803585795">"अनुमति न दें"</string>
-    <string name="log_access_confirmation_body" msgid="6883031912003112634">"डिवाइस लॉग में आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए कर सकते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर मौजूद अपने लॉग ऐक्सेस कर सकता है. डिवाइस को बनाने वाली कंपनी फिर भी डिवाइस के कुछ लॉग या जानकारी ऐक्सेस कर सकती है."</string>
+    <string name="log_access_confirmation_body" msgid="6883031912003112634">"डिवाइस लॉग में आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए कर सकते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर मौजूद अपने लॉग ऐक्सेस कर सकता है. इसके अलावा, डिवाइस को बनाने वाली कंपनी भी डिवाइस के कुछ लॉग या जानकारी ऐक्सेस कर सकती है."</string>
     <string name="log_access_confirmation_learn_more" msgid="3134565480986328004">"ज़्यादा जानें"</string>
     <string name="log_access_confirmation_learn_more_at" msgid="5635666259505215905">"ज़्यादा जानने के लिए <xliff:g id="URL">%s</xliff:g> पर जाएं"</string>
     <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> खोलें"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपैड पर हाथ के जेस्चर के बारे में जानें"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"कीबोर्ड और टचपैड का इस्तेमाल करके नेविगेट करें"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपैड पर हाथ के जेस्चर, कीबोर्ड शॉर्टकट वगैरह के बारे में जानें"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"वापस जाएं"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रीन पर जाएं"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"हो गया"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"वापस जाएं"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"अपने टचपैड पर तीन उंगलियों का इस्तेमाल करके, बाईं या दाईं ओर स्वाइप करें"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"बढ़िया!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"अब आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके, पिछली स्क्रीन पर वापस कैसे जाएं."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होम स्क्रीन पर जाएं"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"बहुत बढ़िया!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"अब आपको इस बारे में जानकारी है कि हाथ के जेस्चर का इस्तेमाल करके होम स्क्रीन पर कैसे जाते हैं"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और फिर होल्ड करें"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"बहुत बढ़िया!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"आपने हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने के लिए, हाथ के जेस्चर के बारे में जान लिया है."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"सभी ऐप्लिकेशन देखें"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"अपने कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"बहुत खूब!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"अब आपको इस बारे में जानकारी है कि सभी ऐप्लिकेशन देखने के लिए, हाथ के जेस्चर का इस्तेमाल कैसे किया जाता है"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9607120..cc19bea 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dijeli zvuk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zajedničko slušanje"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"unesite postavke zajedničkog slušanja"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Glazba i videozapisi s ovog uređaja reproducirat će se na oba para slušalica"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Podijelite zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Prijeđi na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uklj. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nije postavljeno"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte u postavkama"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nema aktivnih načina}=1{Aktivno: {mode}}one{# način je aktivan}few{# načina su aktivna}other{# načina je aktivno}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivatelja koje navedete. I dalje ćete čuti sve što želite reproducirati, uključujući glazbu, videozapise i igre."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvono utišano"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je uključen način Ne uznemiravaj"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je uključen način Ne uznemiravaj"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nedostupno jer je uključen način <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupno"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste postavili na vibraciju. Usluge pristupačnosti možda neće imati zvuk."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Saznajte više o pokretima za dodirnu podlogu"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tipkovnice i dodirne podloge"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima za dodirnu podlogu, tipkovnim prečacima i ostalom"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Natrag"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Na početni zaslon"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Pregled nedavnih aplikacija"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Prijeđite ulijevo ili udesno trima prstima na dodirnoj podlozi"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Odlično!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Izvršili ste pokret za povratak."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Na početnu stranicu"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prijeđite prema gore trima prstima na dodirnoj podlozi"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Sjajno!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Izvršili ste pokret za otvaranje početnog zaslona"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Pregled nedavnih aplikacija"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prijeđite prema gore trima prstima na dodirnoj podlozi i zadržite pritisak"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavno korištenih aplikacija."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pritisnite tipku za radnju na tipkovnici"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Izvrsno!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Izvršili ste pokret za prikaz svih aplikacija"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 3a9735b..9ee748b 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Hang megosztása"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Hang megosztása…"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"a hangmegosztási beállítások megadásához"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Az eszközön lejátszott zene és videó a fejhallgatópárt alkotó mindkét eszközön hallható"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Hang megosztása"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> és <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Váltás erre: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Be"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Be • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Ki"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nincs beállítva"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"A Beállítások között kezelheti"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nincs aktív mód}=1{A(z) {mode} aktív}other{# mód aktív}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Az Ön által meghatározott ébresztéseken, emlékeztetőkön, eseményeken és hívókon kívül nem fogja Önt más hang vagy rezgés megzavarni. Továbbra is lesz hangjuk azoknak a tartalmaknak, amelyeket Ön elindít, például zenék, videók és játékok."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nem lehetséges, a csörgés le van némítva"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nem áll rendelkezésre, mert a(z) <xliff:g id="MODE">%s</xliff:g> be van kapcsolva"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nem áll rendelkezésre"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Koppintson a némítás megszüntetéséhez."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Érintőpad-kézmozdulatok megismerése"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigálás a billentyűzet és az érintőpad használatával"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Érintőpad-kézmozdulatok, billentyűparancsok és egyebek megismerése"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Vissza"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ugrás a főoldalra"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Legutóbbi alkalmazások megtekintése"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kész"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Vissza"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Csúsztassa gyorsan három ujját balra vagy jobbra az érintőpadon."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Remek!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Teljesítette a visszalépési kézmozdulatot."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ugrás a főoldalra"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Csúsztasson gyorsan felfelé három ujjával az érintőpadon."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Kiváló!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Teljesítette a kezdőképernyőre lépés kézmozdulatát."</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Legutóbbi alkalmazások megtekintése"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Csúsztasson gyorsan felfelé három ujjal az érintőpadon, és tartsa rajta lenyomva az ujjait."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kiváló!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Teljesítette a legutóbbi alkalmazások megtekintésének kézmozdulatát."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Összes alkalmazás megtekintése"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nyomja meg a műveletbillentyűt az érintőpadon."</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Szép munka!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Teljesítette az összes alkalmazás megtekintésének kézmozdulatát."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 804550c..b07a785 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիոն"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Աուդիոյի փոխանցում"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"անցնել աուդիոյի փոխանցման կարգավորումներ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Այս սարքի երաժշտությունն ու տեսանյութերը երկու ականջակալներում էլ կնվագարկվեն"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Կիսվեք ձեր աուդիոյով"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> և <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Անցնել <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> սարքին"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Միացված է"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Միաց․ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Անջատված է"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Կարգավորված չէ"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Կառավարել կարգավորումներում"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ակտիվ ռեժիմներ չկան}=1{{mode} ռեժիմ ակտիվ է}one{# ռեժիմ ակտիվ է}other{# ռեժիմ ակտիվ է}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ձայները և թրթռոցները չեն անհանգստացնի ձեզ, բացի ձեր կողմից նշված զարթուցիչները, հիշեցումները, միջոցառումների ծանուցումները և զանգերը։ Դուք կլսեք ձեր ընտրածի նվագարկումը, այդ թվում՝ երաժշտություն, տեսանյութեր և խաղեր:"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Հասանելի չէ, երբ զանգի ձայնն անջատված է"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Հասանելի չէ, քանի որ «<xliff:g id="MODE">%s</xliff:g>» ռեժիմը միացված է"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Հասանելի չէ"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռոցը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Սովորեք օգտագործել հպահարթակի ժեստերը"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Կողմնորոշվեք ստեղնաշարի և հպահարթակի օգնությամբ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Սովորեք օգտագործել հպահարթակի ժեստերը, ստեղնային դյուրանցումները և ավելին"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Ինչպես հետ գնալ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ինչպես անցնել հիմնական էկրան"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Դիտել վերջին հավելվածները"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Պատրաստ է"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Հետ գնալ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Հպահարթակի վրա երեք մատով սահեցրեք ձախ կամ աջ"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Գերազանց է"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Դուք սովորեցիք հետ գնալու ժեստը։"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Անցում հիմնական էկրան"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Հպահարթակի վրա երեք մատով սահեցրեք վերև"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Կեցցե՛ք"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Դիտել վերջին հավելվածները"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Երեք մատով սահեցրեք վերև և սեղմած պահեք հպահարթակին"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Կեցցե՛ք։"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Դուք կատարեցիք վերջին օգտագործված հավելվածների դիտման ժեստը։"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ինչպես դիտել բոլոր հավելվածները"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Հիանալի՛ է"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Դուք սովորեցիք բոլոր հավելվածները դիտելու ժեստը"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 98bbf4f..84a45c6 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -98,7 +98,7 @@
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Batas kiri <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Batas kanan <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string>
     <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Disimpan di <xliff:g id="APP">%1$s</xliff:g> di profil kerja"</string>
-    <string name="screenshot_private_profile_notification" msgid="1704440899154243171">"Disimpan di <xliff:g id="APP">%1$s</xliff:g> di profil pribadi"</string>
+    <string name="screenshot_private_profile_notification" msgid="1704440899154243171">"Disimpan di <xliff:g id="APP">%1$s</xliff:g> di profil privasi"</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"File"</string>
     <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> mendeteksi screenshot ini."</string>
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan aplikasi terbuka lainnya mendeteksi screenshot ini."</string>
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bagikan audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Berbagi audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"masuk ke setelan berbagi audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik dan video di perangkat ini akan diputar di kedua pasang headphone"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Bagikan audio Anda"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> dan <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Alihkan ke <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Aktif"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktif • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Nonaktif"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Tidak disetel"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Kelola di setelan"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Tidak ada mode yang aktif}=1{{mode} aktif}other{# mode aktif}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Anda tidak akan terganggu oleh suara dan getaran, kecuali dari alarm, pengingat, acara, dan penelepon yang Anda tentukan. Anda akan tetap mendengar apa pun yang telah dipilih untuk diputar, termasuk musik, video, dan game."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia - Volume dering dibisukan"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Tidak tersedia karena <xliff:g id="MODE">%s</xliff:g> aktif"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Tidak tersedia"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketuk untuk menyuarakan."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketuk untuk menyetel agar bergetar. Layanan aksesibilitas mungkin dibisukan."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Pelajari gestur touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Menavigasi menggunakan keyboard dan touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Pelajari gestur touchpad, pintasan keyboard, dan lainnya"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kembali"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Buka layar utama"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat aplikasi terbaru"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Geser ke kiri atau kanan menggunakan tiga jari di touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bagus!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Anda telah menyelesaikan gestur kembali."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Buka layar utama"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Geser ke atas dengan tiga jari di touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bagus!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Anda telah menyelesaikan gestur buka layar utama"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Lihat aplikasi terbaru"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Geser ke atas dan tahan menggunakan tiga jari di touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bagus!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda telah menyelesaikan gestur untuk melihat aplikasi terbaru."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Lihat semua aplikasi"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan tombol tindakan di keyboard"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bagus!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah menyelesaikan gestur untuk melihat semua aplikasi"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index b12a475..f27790c 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deila hljóði"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deilir hljóði"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"slá inn stillingar hljóðdeilingar"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Tónlist og vídeó í tækinu munu spilast í báðum heyrnartólum"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Deildu hljóðinu þínu"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> og <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Skipta yfir í <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Kveikt"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Kveikt • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Slökkt"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Ekki stillt"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Stjórna í stillingum"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Engar virkar stillingar}=1{{mode} er virk}one{# stilling er virk}other{# stillingar eru virkar}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Þú verður ekki fyrir truflunum frá hljóðmerkjum og titringi, fyrir utan vekjara, áminningar, viðburði og símtöl frá þeim sem þú leyfir fyrirfram. Þú heyrir áfram í öllu sem þú velur að spila, svo sem tónlist, myndskeiðum og leikjum."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ekki í boði þar sem hringing er þögguð"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ekki í boði því að kveikt er á „Ónáðið ekki“"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Ekki í boði því að kveikt er á „Ónáðið ekki“"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ekki í boði vegna þess að kveikt er á <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ekki í boði"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ýttu til að hætta að þagga."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ýttu til að stilla á titring. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Nánar um bendingar á snertifleti"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Flettu með því að nota lyklaborðið og snertiflötinn"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Kynntu þér bendingar á snertifleti, flýtilykla og fleira"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Til baka"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Fara á heimaskjá"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Sjá nýleg forrit"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Lokið"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Til baka"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Strjúktu til hægri eða vinstri á snertifletinum með þremur fingrum"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Flott!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Þú laukst við að kynna þér bendinguna „til baka“."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Heim"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Strjúktu upp á snertifletinum með þremur fingrum"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Vel gert!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Þú framkvæmdir bendinguna „Fara á heimaskjá“"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Sjá nýleg forrit"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Strjúktu upp og haltu þremur fingrum inni á snertifletinum."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Vel gert!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Þú framkvæmdir bendinguna til að sjá nýleg forrit."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Sjá öll forrit"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Ýttu á aðgerðalykilinn á lyklaborðinu"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vel gert!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Þú framkvæmdir bendinguna „Sjá öll forrit“"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 4fdf01d..7ace302 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Condividi audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Condivisione audio in corso…"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"inserisci le impostazioni di condivisione audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"La musica e i video di questo dispositivo verranno riprodotti su entrambe le coppie di cuffie"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Condividi l\'audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Passa a <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"On"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Non impostata"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestisci nelle impostazioni"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nessuna modalità attiva}=1{{mode} è attiva}many{# di modalità sono attive}other{# modalità sono attive}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Suoni e vibrazioni non ti disturberanno, ad eccezione di sveglie, promemoria, eventi, chiamate da contatti da te specificati ed elementi che hai scelto di continuare a riprodurre, inclusi video, musica e giochi."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non disponibile con l\'audio disattivato"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Non disponibile: modalità Non disturbare attiva"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Non disponibili con \"Non disturbare\" attiva"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Non disponibile perché la modalità <xliff:g id="MODE">%s</xliff:g> è attiva"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Non disponibile"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tocca per riattivare l\'audio."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Impara i gesti con il touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviga usando la tastiera e il touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Scopri gesti con il touchpad, scorciatoie da tastiera e altro ancora"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Indietro"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Vai alla schermata Home"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Visualizza app recenti"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fine"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Indietro"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Scorri verso sinistra o destra con tre dita sul touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bene!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Hai completato il gesto Indietro."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Vai alla schermata Home"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Scorri in alto con tre dita sul touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Ottimo lavoro."</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Hai completato il gesto Vai alla schermata Home"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Visualizza app recenti"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Scorri verso l\'alto e tieni premuto con tre dita sul touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ottimo lavoro."</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Hai completato il gesto Visualizza app recenti."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Visualizza tutte le app"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Premi il tasto azione sulla tastiera"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Ben fatto!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hai completato il gesto Visualizza tutte le app."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 78d60e5..b5783cf 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"שיתוף האודיו"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"מתבצע שיתוף של האודיו"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"להזנת הרשאות השיתוף של האודיו"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"המוזיקה והסרטונים של המכשיר הזה יופעלו בשני זוגות האוזניות"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"שיתוף האודיו שלך"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> וגם <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"מעבר אל <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"מצב מופעל"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"פועל • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"מצב מושבת"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"לא הוגדר"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"שינוי ב\'הגדרות\'"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{אין מצבים פעילים}=1{מצב פעיל אחד ({mode})}one{‫# מצבים פעילים}two{‫# מצבים פעילים}other{‫# מצבים פעילים}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות, תזכורות, אירועים ושיחות ממתקשרים מסוימים לבחירתך. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"לא זמין כי הצלצול מושתק"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"לא זמין כי המצב <xliff:g id="MODE">%s</xliff:g> מופעל"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"לא זמין"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏%1$s. יש להקיש כדי לבטל את ההשתקה."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏%1$s. צריך להקיש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏%1$s. יש להקיש כדי להשתיק. ייתכן ששירותי הנגישות יושתקו."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"מידע על התנועות בלוח המגע"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ניווט באמצעות המקלדת ולוח המגע"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"מידע על התנועות בלוח המגע, מקשי קיצור ועוד"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"חזרה"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"חזרה לדף הבית"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"הצגת האפליקציות האחרונות"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"סיום"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"חזרה"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"מחליקים שמאלה או ימינה עם שלוש אצבעות על לוח המגע"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"איזה יופי!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"השלמת את התנועה \'הקודם\'."</string>
-    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"מעבר לדף הבית"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"מעבר למסך הבית"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"מחליקים כלפי מעלה עם שלוש אצבעות על לוח המגע"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"מעולה!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"השלמת את תנועת החזרה למסך הבית"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"הצגת האפליקציות האחרונות"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"מעולה!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"השלמת את התנועה להצגת האפליקציות האחרונות."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"צפייה בכל האפליקציות"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"צריך להקיש על מקש הפעולה במקלדת"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"כל הכבוד!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"השלמת את התנועה להצגת כל האפליקציות"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏רמה %1$d מתוך %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 2a35e17c..8427f67 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"音声を共有"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"音声を共有中"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"音声の共有設定を開く"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"このデバイスの音楽と動画は、両方のヘッドフォンで再生されます"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"音声の共有"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>、<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> に切り替える"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string>
@@ -672,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"着信音がミュートされているため利用できません"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"サイレント モードが ON のため利用できません"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"サイレント モードが ON のため利用できません"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g>が ON のため使用できません"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"使用不可"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。タップしてミュートを解除します。"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 05a7e42..3b0732d 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"აუდიოს გაზიარება"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"მიმდინარებოს აუდიოს გაზიარება"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"აუდიო გაზიარების პარამეტრების შეყვანა"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ამ მოწყობილობის მუსიკა და ვიდეოები დაუკრავს ორივე ყურსასმენში"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"თქვენი აუდიოს გაზიარება"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> და <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>-ზე გადართვა"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -672,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"ზარის დადუმების გამო ხელმისაწვდომი არაა"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"მიუწვდომელია, ჩართულია „არ შემაწუხოთ“ რეჟიმი"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"მიუწვდომელია, ჩართულია „არ შემაწუხოთ“ რეჟიმი"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> რეჟიმის გამო მიუწვდომელია"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"მიუწვდომელია"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. შეეხეთ დადუმების გასაუქმებლად."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 328c82f..6511558 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудионы бөлісу"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио беріліп жатыр"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"аудио бөлісу параметрлерін енгізу"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Бұл құрылғыдағы музыка мен бейнелер қос құлақаспапта ойнатылады."</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Аудиоңызды бөлісіңіз"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> және <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> құрылғысына ауысу"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Қосулы"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Қосулы • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Өшірулі"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Орнатылмаған"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"\"Параметрлер\" бөлімінде реттеу"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Қосулы режим жоқ}=1{{mode} қосулы}other{# режим қосулы}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Оятқыш, еске салғыш, іс-шара мен өзіңіз көрсеткен контактілердің қоңырауларынан басқа дыбыстар мен дірілдер мазаламайтын болады. Музыка, бейне және ойын сияқты медиафайлдарды қоссаңыз, оларды естисіз."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Қолжетімді емес, шылдырлату өшірулі."</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Қолжетімді емес, себебі <xliff:g id="MODE">%s</xliff:g> режимі қосулы."</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Қолжетімді емес"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дыбысын қосу үшін түртіңіз."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Діріл режимін орнату үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Сенсорлық тақта қимылдарын үйреніңіз."</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Пернетақтамен және сенсорлық тақтамен жұмыс істеңіз"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсорлық тақта қимылдарын, перне тіркесімдерін және т.б. үйреніңіз."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артқа"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Негізгі бетке өту"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Соңғы қолданбаларды көру"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Дайын"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артқа"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Сенсорлық тақтада үш саусақпен оңға немесе солға сырғытыңыз."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Керемет!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Артқа қайту қимылын аяқтадыңыз."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Негізгі экранға өту"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Сенсорлық тақтада үш саусақпен жоғары сырғытыңыз."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Жарайсыз!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Негізгі экранға қайту қимылын орындадыңыз."</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Соңғы қолданбаларды көру"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Сенсорлық тақтада үш саусақпен жоғары сырғытып, басып тұрыңыз."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Жарайсыз!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Соңғы қолданбаларды көру қимылын орындадыңыз."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Барлық қолданбаны көру"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Пернетақтадағы әрекет пернесін басыңыз."</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Жарайсыз!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Барлық қолданбаны көру қимылын орындадыңыз."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index dce1adc..7b7f912 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ស្ដាប់សំឡេងរួមគ្នា"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"បញ្ចូលការកំណត់ការស្ដាប់សំឡេងរួមគ្នា"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"តន្ត្រី និងវីដេអូរបស់ឧបករណ៍នេះនឹងចាក់នៅលើកាសទាំងពីរគូ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ស្ដាប់សំឡេង​របស់អ្នករួមគ្នា"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> និង <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"ប្ដូរទៅ <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"បើក"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"បើក • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"បិទ"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"មិនបានកំណត់"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"គ្រប់គ្រង​នៅ​ក្នុង​ការកំណត់"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{គ្មានមុខងារ​ដែលកំពុងដំណើរការទេ}=1{{mode} កំពុង​ដំណើរការ}other{មុខងារ # កំពុងដំណើរការ}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"សំឡេង និងរំញ័រនឹងមិន​រំខានដល់អ្នកឡើយ លើកលែងតែម៉ោងរោទ៍ ការរំលឹក ព្រឹត្តិការណ៍ និងអ្នកហៅទូរសព្ទដែលអ្នកបញ្ជាក់ប៉ុណ្ណោះ។ អ្នកនឹងនៅតែឮសំឡេងសកម្មភាពគ្រប់យ៉ាងដែលអ្នកលេងដដែល រួមទាំងតន្រ្តី វីដេអូ និងហ្គេម។"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"មិន​អាច​ប្រើ​បាន​ទេ​ ព្រោះ​សំឡេង​រោទ៍​ត្រូវ​បាន​បិទ"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"មិនអាចប្រើបានទេ ដោយសារមុខងារកុំ​រំខានត្រូវបានបើក"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"មិនអាចប្រើបានទេ ដោយសារមុខងារកុំ​រំខានត្រូវបានបើក"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"មិនអាចប្រើបានទេ ដោយសារបើក <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"មិនមានទេ"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s។ ប៉ះដើម្បីបើកសំឡេង។"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s។ ប៉ះដើម្បីកំណត់ឲ្យញ័រ។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"រុករកដោយប្រើក្ដារចុច និងផ្ទាំងប៉ះរបស់អ្នក"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ ផ្លូវកាត់​ក្ដារ​ចុច និងអ្វីៗជាច្រើនទៀត"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ថយ​ក្រោយ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ទៅទំព័រដើម"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"មើលកម្មវិធីថ្មីៗ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"រួចរាល់"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ថយ​ក្រោយ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"អូសទៅឆ្វេង ឬស្ដាំដោយប្រើ​ម្រាមដៃបីនៅលើផ្ទាំងប៉ះរបស់អ្នក"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ល្អ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"អ្នក​បានបញ្ចប់​ចលនា​ថយក្រោយ​ហើយ។"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ទៅទំព័រដើម"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"អូសឡើងលើដោយប្រើម្រាមដៃបី​នៅលើផ្ទាំងប៉ះរបស់អ្នក"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ធ្វើបានល្អ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"អ្នក​បានបញ្ចប់​ចលនា​ចូលទៅកាន់​ទំព័រដើម​ហើយ"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"មើលកម្មវិធីថ្មីៗ"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"អូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបីលើផ្ទាំងប៉ះរបស់អ្នក"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ធ្វើបានល្អ!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"អ្នកបានបញ្ចប់ការមើលចលនាកម្មវិធីថ្មីៗ។"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"មើល​កម្មវិធី​ទាំងអស់"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ចុចគ្រាប់ចុចសកម្មភាពលើក្ដារចុចរបស់អ្នក"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ធ្វើបាន​ល្អ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"អ្នកបានបញ្ចប់ចលនាមើលកម្មវិធីទាំងអស់ហើយ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រង​ផ្ទះ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 5ac9102..c4417b7 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನಮೂದಿಸಿ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ಈ ಸಾಧನದ ಸಂಗೀತ ಮತ್ತು ವೀಡಿಯೊಗಳು ಎರಡೂ ಜೋಡಿ ಹೆಡ್‌ಫೋನ್‌ಗಳಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತವೆ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ನಿಮ್ಮ ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ಮತ್ತು <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್‌ಸೆಟ್"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ಆನ್ ಆಗಿದೆ"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • ನಲ್ಲಿ"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ಆಫ್ ಆಗಿದೆ"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"ಸೆಟ್ ಮಾಡಿಲ್ಲ"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ನಿರ್ವಹಿಸಿ"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ಯಾವುದೇ ಸಕ್ರಿಯ ಮೋಡ್‌ಗಳಿಲ್ಲ}=1{{mode} ಸಕ್ರಿಯವಾಗಿದೆ}one{# ಮೋಡ್‌ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ}other{# ಮೋಡ್‌ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"ಅಲಾರಾಂಗಳು, ಜ್ಞಾಪನೆಗಳು, ಈವೆಂಟ್‌ಗಳು ಹಾಗೂ ನೀವು ಸೂಚಿಸಿರುವ ಕರೆದಾರರನ್ನು ಹೊರತುಪಡಿಸಿ ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್‌ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ನೀವು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"ರಿಂಗ್ ಮ್ಯೂಟ್ ಆಗಿರುವ ಕಾರಣ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಫೀಚರ್ ಆನ್ ಆಗಿರುವುದರಿಂದ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಫೀಚರ್ ಆನ್ ಆಗಿರುವುದರಿಂದ ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ಆನ್ ಆಗಿರುವ ಕಾರಣ ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ಅನ್‌ಮ್ಯೂಟ್‌ ಮಾಡುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ಕಂಪನಕ್ಕೆ ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್‌ ಮಾಡಬಹುದು."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್‌ ಮಾಡಬಹುದು."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ಟಚ್‌ಪ್ಯಾಡ್ ಗೆಸ್ಚರ್‌ಗಳನ್ನು ಕಲಿಯಿರಿ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಮತ್ತು ಟಚ್‌ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ಟಚ್‌ಪ್ಯಾಡ್ ಗೆಸ್ಚರ್‌ಗಳು, ಕೀಬೋರ್ಡ್‌ಗಳ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ತಿಳಿಯಿರಿ"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ಹಿಂದಿರುಗಿ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ಮುಗಿದಿದೆ"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ಹಿಂತಿರುಗಿ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಎಡ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ಚೆನ್ನಾಗಿದೆ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ನೀವು ಗೋ ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ಭೇಷ್!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ಭೇಷ್‌!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ನೀವು ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳ ಗೆಸ್ಚರ್‌ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿ"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ಭೇಷ್!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ನೀವು ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳ ಗೆಸ್ಚರ್‌ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್‌ಲೈಟ್"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b7d81b4..5602ee7 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"오디오 공유"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"오디오 공유 중"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"오디오 공유 설정으로 이동"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"이 기기의 음악 및 동영상이 헤드폰 양쪽에서 재생됩니다."</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"오디오 공유"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> 및 <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"기기(<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>)로 전환"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"사용"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"켜짐 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"사용 안함"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"설정되지 않음"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"설정에서 관리"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{활성화된 모드 없음}=1{{mode} 모드가 활성화됨}other{모드 #개가 활성화됨}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"알람, 알림, 일정 및 지정한 발신자로부터 받은 전화를 제외한 소리와 진동을 끕니다. 음악, 동영상, 게임 등 재생하도록 선택한 소리는 정상적으로 들립니다."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"벨소리가 음소거되어 있으므로 사용할 수 없음"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"방해 금지 모드로 설정되어 있어 사용할 수 없음"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"방해 금지 모드로 설정되어 있어 사용할 수 없음"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> 모드가 사용 설정되어 있어 사용할 수 없음"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"사용할 수 없음"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. 탭하여 음소거를 해제하세요."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. 탭하여 진동으로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"터치패드 동작 알아보기"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"키보드와 터치패드를 사용하여 이동"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"터치패드 동작, 단축키 등 알아보기"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"뒤로 이동"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"홈으로 이동"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"최근 앱 보기"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"완료"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"뒤로"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"세 손가락을 사용해 터치패드에서 왼쪽 또는 오른쪽으로 스와이프하세요."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"훌륭합니다"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"돌아가기 동작을 완료했습니다."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"홈으로 이동"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"세 손가락을 사용해 터치패드에서 위로 스와이프하세요."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"아주 좋습니다"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"홈으로 이동 동작을 완료했습니다."</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"최근 앱 보기"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"세 손가락을 사용해 터치패드에서 위로 스와이프한 후 잠시 기다리세요."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"아주 좋습니다"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"최근 앱 보기 동작을 완료했습니다."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"모든 앱 보기"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"키보드의 작업 키를 누르세요."</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"잘하셨습니다"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"모든 앱 보기 동작을 완료했습니다."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index da4d4c3..93777db 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Чогуу угуу"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Чогуу угулууда"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"чогуу угуу параметрлерин киргизүү"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Бул түзмөктөгү музыка жана видеолор гарнитуранын эки кулагынан тең угулат"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Чогуу угуңуз"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> жана <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> түзмөгүнө которулуу"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Күйүк"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Күйүк • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Өчүк"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Туураланган эмес"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Параметрлерден тескөө"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Жигердүү режимдер жок}=1{{mode} иштеп жатат}other{# режим иштеп жатат}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ойготкучтардан, эскертүүлөрдөн, жылнаамадагы иш-чараларды эстеткичтерден жана белгиленген байланыштардын чалууларынан тышкары башка үндөр жана дирилдөөлөр тынчыңызды албайт. Бирок ойнотулуп жаткан музыканы, видеолорду жана оюндарды мурдагыдай эле уга бересиз."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Үнсүз режимде жеткиликсиз"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\"Тынчымды алба\" режими күйүк болгондуктан, жеткиликсиз"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"\"Тынчымды алба\" режими күйүк болгондуктан, жеткиликсиз"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> күйүп тургандыктан жеткиликсиз"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Жеткиликсиз"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Үнүн чыгаруу үчүн таптап коюңуз."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дирилдөөгө коюу үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Сенсордук тактадагы жаңсоолорду үйрөнүп алыңыз"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Нерселерге баскычтоп жана сенсордук такта аркылуу өтүңүз"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсордук тактадагы жаңсоолор, ыкчам баскычтар жана башкалар жөнүндө билип алыңыз"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артка кайтуу"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Башкы бетке өтүү"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Акыркы колдонмолорду көрүү"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Бүттү"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артка кайтуу"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Сенсордук тактаны үч манжаңыз менен солго же оңго сүрүңүз"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Сонун!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"\"Артка\" жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Башкы бетке өтүү"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Сенсордук тактаны үч манжаңыз менен жогору сүрүңүз"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Азаматсыз!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Акыркы колдонмолорду көрүү"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Сенсордук тактаны үч манжаңыз менен өйдө сүрүп, кармап туруңуз"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Азаматсыз!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Акыркы колдонмолорду көрүү жаңсоосун аткардыңыз."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Бардык колдонмолорду көрүү"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Баскычтобуңуздагы аракет баскычын басыңыз"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Эң жакшы!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Бардык колдонмолорду көрүү жаңсоосун аткардыңыз"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үйдөгү түзмөктөрдү тескөө"</string>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index db526b1..5d5b955 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -25,6 +25,15 @@
 
     <integer name="quick_settings_num_columns">4</integer>
 
+    <!-- The number of rows in the paginated grid QuickSettings -->
+    <integer name="quick_settings_paginated_grid_num_rows">2</integer>
+
+    <!-- The number of rows in the paginated grid QuickQuickSettings -->
+    <integer name="quick_qs_paginated_grid_num_rows">1</integer>
+
+    <!-- The number of columns in the infinite grid QuickSettings -->
+    <integer name="quick_settings_infinite_grid_num_columns">8</integer>
+
     <!-- The number of columns that the top level tiles span in the QuickSettings -->
     <integer name="quick_settings_user_time_settings_tile_span">2</integer>
 
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 92b3974..07f3853 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ແບ່ງປັນສຽງ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ກຳລັງແບ່ງປັນສຽງ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ເຂົ້າສູ່ການຕັ້ງຄ່າການແບ່ງປັນສຽງ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ເພງ ແລະ ວິດີໂອຂອງອຸປະກອນເຄື່ອງນີ້ຈະຫຼິ້ນຢູ່ຫູຟັງທັງສອງຄູ່"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ແບ່ງປັນສຽງຂອງທ່ານ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ແລະ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"ປ່ຽນເປັນ <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string>
@@ -672,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"ບໍ່ມີໃຫ້ໃຊ້ເນື່ອງຈາກມີການປິດສຽງໂທເຂົ້າ"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ບໍ່ມີໃຫ້ຍ້ອນວ່າຫ້າມລົບກວນເປີດຢູ່"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"ບໍ່ມີໃຫ້ຍ້ອນວ່າຫ້າມລົບກວນເປີດຢູ່"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"ບໍ່ສາມາດໃຊ້ໄດ້ເນື່ອງຈາກ <xliff:g id="MODE">%s</xliff:g> ເປີດຢູ່"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ບໍ່ສາມາດໃຊ້ໄດ້"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ແຕະເພື່ອເຊົາປິດສຽງ."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ແຕະເພື່ອປິດສຽງ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a5e325e..605b8dd 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bendrinti garsą"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Bendrinamas garsas"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"įvesti garso įrašų bendrinimo nustatymus"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Šio įrenginio muzika ir vaizdo įrašai bus leidžiami abiejose ausinėse"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Garso įrašo bendrinimas"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ir <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Perjungti į <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Įjungta"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Įjungta • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Išjungta"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nenustatyta"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Tvarkyti skiltyje „Nustatymai“"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nėra aktyvių režimų}=1{Aktyvus režimas „{mode}“}one{# aktyvus režimas}few{# aktyvūs režimai}many{# aktyvaus režimo}other{# aktyvių režimų}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Jūsų netrikdys garsai ir vibravimas, išskyrus nurodytų signalų, priminimų, įvykių ir skambintojų garsus. Vis tiek girdėsite viską, ką pasirinksite leisti, įskaitant muziką, vaizdo įrašus ir žaidimus."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nepasiekiama, nes skambutis nutildytas"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nepasiekiama, nes įjungtas netrukdymo režimas"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nepasiekiama, nes įjungtas netrukdymo režimas"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nepasiekiama, nes įjungtas režimas „<xliff:g id="MODE">%s</xliff:g>“"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nepasiekiama"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Palieskite, kad įjungtumėte garsą."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Palieskite, kad nustatytumėte vibravimą. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Sužinokite jutiklinės dalies gestus"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naršykite naudodamiesi klaviatūra ir jutikline dalimi"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sužinokite jutiklinės dalies gestus, sparčiuosius klavišus ir kt."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Grįžti"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Eikite į pagrindinį puslapį"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Peržiūrėti naujausias programas"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Atlikta"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Grįžti"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Braukite kairėn arba dešinėn trimis pirštais bet kur jutiklinėje dalyje"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Šaunu!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Atlikote grįžimo atgal gestą."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Eikite į pagrindinį ekraną"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Braukite viršun trimis pirštais bet kur jutiklinėje dalyje"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Puiku!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Atlikote perėjimo į pagrindinį ekraną gestą"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Peržiūrėti naujausias programas"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Braukite viršun trimis pirštais bet kur jutiklinėje dalyje ir palaikykite"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Puiku!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Atlikote naujausių programų peržiūros gestą."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Žr. visas programas"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paspauskite klaviatūros veiksmų klavišą"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Puikiai padirbėta!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Atlikote visų programų peržiūros gestą"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index b570ad6..0393ad5 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Kopīgot audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Notiek audio kopīgošana…"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"atvērt audio kopīgošanas iestatījumus"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Mūzika un videoklipi no šīs ierīces tiks atskaņoti abās austiņās"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audio kopīgošana"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> un <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Pārslēgties uz šādu ierīci: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Ieslēgts"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ieslēgts • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Izslēgts"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nav iestatīts"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Pārvaldīt iestatījumos"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nav aktīvu režīmu}=1{Režīms “{mode}” ir aktīvs}zero{# režīmi ir aktīvi}one{# režīms ir aktīvs}other{# režīmi ir aktīvi}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Jūs netraucēs skaņas un vibrācija, izņemot signālus, atgādinājumus, pasākumus un zvanītājus, ko būsiet norādījis. Jūs joprojām dzirdēsiet atskaņošanai izvēlētos vienumus, tostarp mūziku, videoklipus un spēles."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nevar mainīt, jo izslēgts skaņas signāls"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nav pieejams, jo ir ieslēgts režīms Netraucēt"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nav pieejams, jo ir ieslēgts režīms Netraucēt"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nav pieejams, jo ir ieslēgts režīms <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nav pieejams"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Pieskarieties, lai ieslēgtu skaņu."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Pieskarieties, lai iestatītu uz vibrozvanu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Pieskarieties, lai izslēgtu skaņu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Rādīt demonstrācijas režīmu"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Tīkls Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Signāls"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"Režīms “<xliff:g id="MODENAME">%1$s</xliff:g>” ir ieslēgts"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Maks"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Iestatiet, lai ātrāk un drošāk veiktu pirkumus, izmantojot tālruni"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Rādīt visu"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Apgūstiet skārienpaliktņa žestus."</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Pārvietošanās, izmantojot tastatūru un skārienpaliktni"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Uzziniet par skārienpaliktņa žestiem, īsinājumtaustiņiem un citām iespējām."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atpakaļ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pāriet uz sākuma ekrānu"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Skatīt nesen izmantotās lietotnes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gatavs"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atpakaļ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Skārienpaliktnī ar trīs pirkstiem velciet pa kreisi vai pa labi."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Lieliski!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Jūs sekmīgi veicāt atgriešanās žestu."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Pāreja uz sākuma ekrānu"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Skārienpaliktnī ar trīs pirkstiem velciet augšup."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Lieliski!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu."</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Nesen izmantoto lietotņu skatīšana"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Skārienpaliktnī ar trīs pirkstiem velciet augšup un turiet."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Lieliski!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jūs sekmīgi veicāt nesen izmantoto lietotņu skatīšanas žestu."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Skatīt visas lietotnes"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tastatūrā nospiediet darbību taustiņu."</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Lieliski!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Jūs sekmīgi veicāt visu lietotņu skatīšanas žestu."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 708135d..6b956ec 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделувај аудио"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Се споделува аудио"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"внесете ги поставките за „Споделување аудио“"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музиката и видеата на уредов ќе се пуштаaт на двата пара слушалки"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Споделете го аудиото"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Префрли на <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Вклучено"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Вклучено: <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Исклучено"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Не е поставено"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управувајте во поставките"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Нема активни режими}=1{Активен е {mode}}one{Активни се # режим}other{Активни се # режими}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Нема да ве вознемируваат звуци и вибрации, освен од аларми, потсетници, настани и повикувачи што ќе ги наведете. Сѐ уште ќе слушате сѐ што ќе изберете да пуштите, како музика, видеа и игри."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недостапно бидејќи звукот е исклучен"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недостапно бидејќи има вклучено <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недостапно"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Допрете за да вклучите звук."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Прикажи демо-режим"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Етернет"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Аларм"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"Режимот „<xliff:g id="MODENAME">%1$s</xliff:g>“ е вклучен"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Поставете за да купувате побрзо и побезбедно преку вашиот телефон"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Прикажи ги сите"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научете движења за допирната подлога"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Движете се со користење на тастатурата и допирната подлога"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете движења за допирната подлога, кратенки од тастатурата и друго"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Врати се назад"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Оди на почетниот екран"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прегледајте ги неодамнешните апликации"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Повлечете налево или надесно со три прста на допирната подлога"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Одлично!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Го научивте движењето за враќање назад."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Одете на почетниот екран"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Повлечете нагоре со три прсти на допирната подлога"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Одлично!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Го завршивте движењето за враќање на почетниот екран"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прегледајте ги неодамнешните апликации"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Повлечете нагоре и задржете со три прста на допирната подлога"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Прегледајте ги сите апликации"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притиснете го копчето за дејство на тастатурата"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Браво!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Го завршивте движењето за прегледување на сите апликации"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 28fd77d..cd0a0442d3 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ഓഡിയോ പങ്കിടുക"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ഓഡിയോ പങ്കിടുന്നു"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ഓഡിയോ പങ്കിടൽ ക്രമീകരണം നൽകുക"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"രണ്ട് ജോടി ഹെഡ്‌ഫോണുകളിലൂടെയും ഈ ഉപകരണത്തിലെ സംഗീതവും വീഡിയോകളും പ്ലേ ചെയ്യും"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"നിങ്ങളുടെ ഓഡിയോ പങ്കിടുക"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> എന്നതിലേക്ക് മാറുക"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്‌സെറ്റ്"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ഓണാണ്"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ഓണാണ് • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ഓഫാണ്"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"സജ്ജീകരിച്ചിട്ടില്ല"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ക്രമീകരണത്തിൽ മാനേജ് ചെയ്യുക"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{സജീവ മോഡുകൾ ഒന്നുമില്ല}=1{{mode} സജീവമാണ്}other{# മോഡുകൾ സജീവമാണ്}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"നിങ്ങൾ സജ്ജീകരിച്ച അലാറങ്ങൾ, റിമൈൻഡറുകൾ, ഇവന്റുകൾ, കോളർമാർ എന്നിവയിൽ നിന്നുള്ള ശബ്‌ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് ‌തുടർന്നും കേൾക്കാൻ കഴിയും."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"റിംഗ് മ്യൂട്ട് ചെയ്തതിനാൽ ലഭ്യമല്ല"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ശല്യപ്പെടുത്തരുത് ഓണായതിനാൽ ലഭ്യമല്ല"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"ശല്യപ്പെടുത്തരുത് ഓണായതിനാൽ ലഭ്യമല്ല"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ഓണായതിനാൽ ലഭ്യമല്ല"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ലഭ്യമല്ല"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. അൺമ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ടച്ച്പാഡ് ജെസ്ച്ചറുകൾ മനസ്സിലാക്കുക"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"നിങ്ങളുടെ കീപാഡ്, ടച്ച്‌പാഡ് എന്നിവ ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ടച്ച്‌പാഡ് ജെസ്ച്ചറുകൾ, കീബോർഡ് കുറുക്കുവഴികൾ എന്നിവയും മറ്റും മനസ്സിലാക്കുക"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"മടങ്ങുക"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ഹോമിലേക്ക് പോകുക"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"പൂർത്തിയായി"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"മടങ്ങുക"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ടച്ച്‌പാഡിൽ മൂന്ന് വിരലുകൾ കൊണ്ട് ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യുക"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"കൊള്ളാം!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"മടങ്ങുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ഹോമിലേക്ക് പോകൂ"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ടച്ച്‌പാഡിൽ മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"കൊള്ളാം!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ഹോം ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"നിങ്ങളുടെ ടച്ച്പാഡിൽ മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"കൊള്ളാം!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക എന്ന ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"എല്ലാ ആപ്പുകളും കാണുക"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"നിങ്ങളുടെ കീബോർഡിലെ ആക്ഷൻ കീ അമർത്തുക"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"അഭിനന്ദനങ്ങൾ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"\'എല്ലാ ആപ്പുകളും കാണുക\' ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്‌ലൈറ്റ്"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index dfd2398..e4f1746 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудио хуваалцах"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио хуваалцаж байна"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"аудио хуваалцах тохиргоог оруулах"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Энэ төхөөрөмжийн хөгжим, видеонуудыг чихэвчийн хоёр талд тоглуулна"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Аудиогоо хуваалцах"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> руу сэлгэх"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Асаалттай"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Асаасан • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Унтраалттай"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Тохируулаагүй"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Тохиргоонд удирдах"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ямар ч идэвхтэй горим байхгүй}=1{{mode} идэвхтэй байна}other{# горим идэвхтэй байна}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Танд сэрүүлэг, сануулга, арга хэмжээ, таны сонгосон дуудлага илгээгчээс бусад дуу, чичиргээ саад болохгүй. Та хөгжим, видео, тоглоом зэрэг тоглуулахыг хүссэн бүх зүйлээ сонсох боломжтой хэвээр байна."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Хонхны дууг хаасан тул боломжгүй"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Бүү саад бол асаалттай тул боломжгүй"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Бүү саад бол асаалттай тул боломжгүй"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> асаалттай тул боломжгүй"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Боломжгүй"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дууг нь нээхийн тулд товшино уу."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Чичиргээнд тохируулахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Мэдрэгч самбарын зангааг мэдэж аваарай"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Гар эсвэл мэдрэгч самбараа ашиглан шилжээрэй"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Мэдрэгч самбарын зангаа, товчлуурын шууд холбоос болон бусад зүйлийг мэдэж аваарай"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Буцах"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Нүүр хуудас руу очих"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Саяхны аппуудыг харах"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Болсон"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Буцах"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Мэдрэгч самбар дээрээ гурван хуруугаа ашиглан зүүн эсвэл баруун тийш шударна уу"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Янзтай!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Та буцах зангааг гүйцэтгэлээ."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Үндсэн нүүр лүү очих"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Мэдрэгч самбар дээрээ гурван хуруугаараа дээш шударна уу"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Үнэхээр сайн ажиллалаа!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Та үндсэн нүүр лүү очих зангааг гүйцэтгэлээ"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Саяхны аппуудыг харах"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Мэдрэгч самбар дээрээ гурван хуруугаа ашиглан дээш шудраад, удаан дарна уу"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Сайн байна!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Та саяхны аппуудыг харах зангааг гүйцэтгэсэн."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Бүх аппыг харах"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээр тань байх тусгай товчийг дарна уу"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Сайн байна!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Та бүх аппыг харах зангааг гүйцэтгэлээ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index db2ed93..458fe2c 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडिओ शेअर करा"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडिओ शेअर करत आहे"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ऑडिओ शेअरिंग सेटिंग्ज एंटर करा"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"या डिव्हाइसचे संगीत आणि व्हिडिओ हेडफोनच्या दोन्ही पेअरवर प्ले होतील"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"तुमचा ऑडिओ शेअर करा"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> आणि <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> वर स्विच करा"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"सुरू आहे"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"सुरू • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"बंद आहे"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट केलेले नाही"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग्जमध्ये व्यवस्थापित करा"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कोणतेही अ‍ॅक्टिव्ह मोड नाहीत}=1{{mode} अ‍ॅक्टिव्ह आहे}other{# मोड अ‍ॅक्टिव्ह आहेत}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"अलार्म, रिमाइंडर, इव्‍हेंट आणि तुम्ही निश्चित केलेल्या कॉलर व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"रिंग म्यूट केल्यामुळे उपलब्ध नाही"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"व्यत्यय आणू नका हे सुरू असल्यामुळे उपलब्ध नाही"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"व्यत्यय आणू नका हे सुरू असल्यामुळे उपलब्ध नाही"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> सुरू असल्यामुळे उपलब्ध नाही"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"उपलब्ध नाही"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करण्यासाठी टॅप करा."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा म्यूट केल्या जाऊ शकतात."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपॅड जेश्चर जाणून घ्या"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"तुमचा कीबोर्ड आणि टचपॅड वापरून नेव्हिगेट करा"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपॅड जेश्चर, कीबोर्ड शॉर्टकट आणि आणखी बरेच काही जाणून घ्या"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"मागे जा"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होमवर जा"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"अलीकडील अ‍ॅप्स पहा"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"पूर्ण झाले"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"मागे जा"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"तुमच्या टचपॅडवर तीन बोटांनी डावीकडे किंवा उजवीकडे स्‍वाइप करा"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"छान!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तुम्ही गो बॅक जेश्चर पूर्ण केले."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमवर जा"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"तुमच्या टचपॅडवर तीन बोटांनी वर स्वाइप करा"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"उत्तम कामगिरी!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"तुम्ही गो होम जेश्चर पूर्ण केले आहे"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"अलीकडील अ‍ॅप्स पहा"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"तुमच्या टचपॅडवर तीन बोटांनी वर स्वाइप करून धरून ठेवा"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"उत्तम कामगिरी!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तुम्ही अलीकडील ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"सर्व अ‍ॅप्स पहा"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"तुमच्या कीबोर्डवर अ‍ॅक्शन की प्रेस करा"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"खूप छान!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तुम्ही ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index abdb32f..8912366 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Kongsi audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Perkongsian audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"masukkan tetapan perkongsian audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzik dan video peranti ini akan dimainkan pada kedua-dua pasang fon kepala"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Kongsi audio anda"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> dan <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Beralih kepada <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string>
@@ -672,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia kerana deringan diredam"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia kerana Jangan Ganggu dihidupkan"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Tidak tersedia kerana Jangan Ganggu dihidupkan"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Tidak tersedia kerana <xliff:g id="MODE">%s</xliff:g> dihidupkan"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Tidak tersedia"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketik untuk menyahredam."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketik untuk menetapkan pada getar. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketik untuk meredam. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
@@ -1410,7 +1416,7 @@
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat apl terbaharu"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
-    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Leret ke kiri atau kanan menggunakan tiga jari pada pad sentuh anda"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Leret ke kiri atau ke kanan menggunakan tiga jari pada pad sentuh anda"</string>
     <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bagus!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Anda telah melengkapkan gerak isyarat undur."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Akses laman utama"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index f4f337b..e388001 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"အသံမျှဝေရန်"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"အသံမျှဝေနေသည်"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"အော်ဒီယို မျှဝေခြင်း ဆက်တင်များ ထည့်ရန်"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ဤစက်၏ သီချင်းနှင့် ဗီဒီယိုများကို နားကြပ်နှစ်ဖက်စလုံးတွင် ဖွင့်မည်"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"သင့်အသံ မျှဝေရန်"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> နှင့် <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> သို့ ပြောင်းရန်"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ဖွင့်"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ဖွင့် • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ပိတ်"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"သတ်မှတ်မထားပါ"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ဆက်တင်များတွင် စီမံရန်"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{သုံးနေသော မုဒ်မရှိပါ}=1{{mode} ကို သုံးနေသည်}other{မုဒ် # ခု သုံးနေသည်}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"နှိုးစက်သံ၊ သတိပေးချက်အသံများ၊ ပွဲစဉ်သတိပေးသံများနှင့် သင်ခွင့်ပြုထားသူများထံမှ ဖုန်းခေါ်မှုများမှလွဲ၍ အခြားအသံများနှင့် တုန်ခါမှုများက သင့်ကို အနှောင့်အယှက်ပြုမည် မဟုတ်ပါ။ သို့သော်လည်း သီချင်း၊ ဗီဒီယိုနှင့် ဂိမ်းများအပါအဝင် သင်ကရွေးချယ်ဖွင့်ထားသည့် အရာတိုင်း၏ အသံကိုမူ ကြားနေရဆဲဖြစ်ပါလိမ့်မည်။"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"ဖုန်းမြည်သံပိတ်ထားသဖြင့် မရနိုင်ပါ"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"‘မနှောင့်ယှက်ရ’ ဖွင့်ထားသောကြောင့် မရနိုင်ပါ"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"‘မနှောင့်ယှက်ရ’ ဖွင့်ထားသောကြောင့် မရနိုင်ပါ"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ကို ဖွင့်ထားသဖြင့် မရနိုင်ပါ"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"မရနိုင်ပါ"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s။ အသံပြန်ဖွင့်ရန် တို့ပါ။"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s။ တုန်ခါမှုကို သတ်မှတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"တာ့ချ်ပက်လက်ဟန်များကို လေ့လာပါ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"သင်၏ ကီးဘုတ်နှင့် တာ့ချ်ပက်တို့ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"တာ့ချ်ပက်လက်ဟန်များ၊ လက်ကွက်ဖြတ်လမ်းများ စသည်တို့ကို လေ့လာပါ"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"နောက်သို့"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ပင်မစာမျက်နှာသို့ သွားရန်"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"မကြာသေးမီကအက်ပ်များကို ကြည့်ရန်"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ပြီးပြီ"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ပြန်သွားရန်"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"သင့်တာ့ချ်ပက်တွင် လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ကောင်းပါသည်။"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"နောက်သို့လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ပင်မစာမျက်နှာသို့ သွားရန်"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"တာ့ချ်ပက်ပေါ်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"တော်ပါပေသည်။"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်း"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"သင့်တာ့ချ်ပက်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"တော်ပါပေသည်။"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ။"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"အက်ပ်အားလုံးကို ကြည့်ရန်"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ကီးဘုတ်တွင် လုပ်ဆောင်ချက်ကီး နှိပ်ပါ"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"အလွန်ကောင်းပါသည်။"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"အက်ပ်အားလုံးကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index f98799a..49c004b 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"åpne innstillingene for lyddeling"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musikk og videoer fra denne enheten spilles av via begge hodetelefonparene"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Del lyd"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> og <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Bytt til <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"På"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"På • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Av"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Ikke angitt"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrer i innstillingene"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ingen aktive moduser}=1{{mode} er aktiv}other{# moduser er aktive}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Du blir ikke forstyrret av lyder og vibrasjoner, med unntak av alarmer, påminnelser, aktiviteter og oppringere du angir. Du kan fremdeles høre alt du velger å spille av, for eksempel musikk, videoer og spill."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Utilgjengelig fordi ringelyden er kuttet"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Utilgjengelig fordi «Ikke forstyrr» er på"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Utilgjengelig fordi «Ikke forstyrr» er på"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Utilgjengelig fordi <xliff:g id="MODE">%s</xliff:g> er på"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Utilgjengelig"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trykk for å slå på lyden."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trykk for å angi vibrasjon. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Vis demo-modus"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> er på"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Legg til en betalingsmåte for å gjennomføre kjøp raskere og sikrere med telefonen"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Vis alle"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Lær deg styreflatebevegelser"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviger med tastaturet og styreflaten"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Lær deg styreflatebevegelser, hurtigtaster med mer"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbake"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se nylige apper"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Ferdig"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbake"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Sveip til venstre eller høyre med tre fingre på styreflaten"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bra!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har fullført bevegelsen for å gå tilbake."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gå til startsiden"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Sveip opp med tre fingre på styreflaten"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bra jobbet!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du har fullført bevegelsen for å gå til startskjermen"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Se nylige apper"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Sveip opp og hold med tre fingre på styreflaten"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bra jobbet!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har fullført bevegelsen for å se nylige apper."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apper"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Trykk på handlingstasten på tastaturet"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bra!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Du har fullført bevegelsen for å se alle apper"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string>
@@ -1465,7 +1457,7 @@
     <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Snarveiene for ekstra dimming er fjernet"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Tilkobling"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Tilgjengelighet"</string>
-    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Systemverktøy"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Verktøy"</string>
     <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Personvern"</string>
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Levert av apper"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjerm"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 2c8778f..e364e9e 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"अडियो सेयर गर्नुहोस्"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"अडियो सेयर गरिँदै छ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"अडियो सेयर गर्ने सुविधासम्बन्धी सेटिङ हाल्न"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"यो डिभाइसका सङ्गीत र भिडियोहरू दुवै जोडी हेडफोनमा प्ले हुने छन्"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"आफ्नो अडियो सेयर गर्नुहोस्"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> र <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> चलाउनुहोस्"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"अन छ"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"अन छ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"अफ छ"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट गरिएको छैन"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिङमा गई व्यवस्थापन गर्नुहोस्"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कुनै पनि सक्रिय छैन}=1{{mode} सक्रिय छ}other{# मोड सक्रिय छन्}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"तपाईंलाई अलार्म, रिमाइन्डर, कार्यक्रम र तपाईंले निर्दिष्ट गर्नुभएका कलरहरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"डिभाइस म्युट गरिएकाले यो सुविधा उपलब्ध छैन"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Do Not Disturb अन भएकाले भोल्युम बढाउन मिल्दैन"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Do Not Disturb अन भएकाले भोल्युम बढाउन मिल्दैन"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> अन भएकाले यो सुविधा उपलब्ध छैन"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"उपलब्ध छैन"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। अनम्यूट गर्नाका लागि ट्याप गर्नुहोस्।"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। कम्पनमा सेट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। म्यूट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचप्याड जेस्चर प्रयोग गर्न सिक्नुहोस्"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"किबोर्ड र टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचप्याड जेस्चर, किबोर्डका सर्टकट र अन्य कुरा प्रयोग गर्न सिक्नुहोस्"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"पछाडि जानुहोस्"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रिनमा जानुहोस्"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"सम्पन्न भयो"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"तीन वटा औँला प्रयोग गरी टचप्याडमा बायाँ वा दायाँतिर स्वाइप गर्नुहोस्"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"राम्रो!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तपाईंले \'पछाडि जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमपेजमा जानुहोस्"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"टचप्याडमा तीन वटा औँलाले माथितिर स्वाइप गर्नुहोस्"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"अद्भुत!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"तीन वटा औँला प्रयोग गरी टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"अद्भुत!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले हालसालै चलाइएका एपहरू हेर्ने जेस्चर पूरा गर्नुभएको छ।"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"सबै एपहरू हेर्नुहोस्"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"स्याबास!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले \"सबै एपहरू हेर्नुहोस्\" नामक जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index cd20a293..0f8eb11 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audio delen"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio delen"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"instellingen voor audio delen openen"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"De muziek en video\'s van dit apparaat worden op beide koptelefoons afgespeeld"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Je audio delen"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> en <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Overschakelen naar <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aan • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Uit"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Niet ingesteld"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Beheren via instellingen"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Geen actieve modi}=1{{mode} is actief}other{# modi zijn actief}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers, herinneringen, afspraken en specifieke bellers die je selecteert. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niet beschikbaar, belgeluid staat uit"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Niet beschikbaar omdat Niet storen aanstaat"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Niet beschikbaar omdat Niet storen aanstaat"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Niet beschikbaar omdat <xliff:g id="MODE">%s</xliff:g> aanstaat"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Niet beschikbaar"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om dempen op te heffen."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Leer touchpadgebaren die je kunt gebruiken"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeren met je toetsenbord en touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer meer over onder andere touchpadgebaren en sneltoetsen"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Terug"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Naar startscherm"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Recente apps bekijken"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Terug"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe met 3 vingers naar links of rechts op de touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Goed zo!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Je weet nu hoe je het gebaar voor terug maakt."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Naar startscherm"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe met 3 vingers omhoog op de touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Goed werk!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Je weet nu hoe je het gebaar Naar startscherm maakt"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Recente apps bekijken"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe met 3 vingers omhoog en houd vast op de touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed werk!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Je weet nu hoe je het gebaar Recente apps bekijken maakt."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle apps bekijken"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Goed gedaan!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Je weet nu hoe je het gebaar Alle apps bekijken maakt"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 59e6462..16127eb 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ଅଡିଓ ସେୟାର କରନ୍ତୁ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ଅଡିଓ ସେୟାରିଂ ସେଟିଂସରେ ପ୍ରବେଶ କରନ୍ତୁ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ଏହି ଡିଭାଇସର ମ୍ୟୁଜିକ ଏବଂ ଭିଡିଓଗୁଡ଼ିକ ଉଭୟ ପେୟାର ହେଡଫୋନରେ ପ୍ଲେ ହେବ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ଆପଣଙ୍କ ଅଡିଓ ସେୟାର କରନ୍ତୁ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ଏବଂ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>କୁ ସୁଇଚ କରନ୍ତୁ"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍‍"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ଚାଲୁ ଅଛି"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ଚାଲୁ ଅଛି • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ବନ୍ଦ ଅଛି"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"ସେଟ କରାଯାଇନାହିଁ"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ସେଟିଂସରେ ପରିଚାଳନା କରନ୍ତୁ"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{କୌଣସି ସକ୍ରିୟ ମୋଡ ନାହିଁ}=1{{mode} ସକ୍ରିୟ ଅଛି}other{# ମୋଡ ସକ୍ରିୟ ଅଛି}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"ଆଲାର୍ମ, ରିମାଇଣ୍ଡର୍‌, ଇଭେଣ୍ଟ ଏବଂ ଆପଣ ନିର୍ଦ୍ଦିଷ୍ଟ କରିଥିବା କଲର୍‌ଙ୍କ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍‌ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଏବଂ ଗେମ୍‌ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"ରିଂକୁ ମ୍ୟୁଟ କରାଯାଇଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ଅନମ୍ୟୁଟ୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ଭାଇବ୍ରେଟ୍‍ ସେଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍‌ ମ୍ୟୁଟ୍‍ କରାଯାଇପାରେ।"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ମ୍ୟୁଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍‌ ମ୍ୟୁଟ୍‍ କରାଯାଇପାରେ।"</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"ଡେମୋ ମୋଡ୍‍ ଦେଖାନ୍ତୁ"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"ଇଥରନେଟ୍‌"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"ଆଲାରାମ"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> ଚାଲୁ ଅଛି"</string>
     <string name="wallet_title" msgid="5369767670735827105">"ୱାଲେଟ୍"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"ଆପଣଙ୍କ ଫୋନ୍ ମାଧ୍ୟମରେ ଆହୁରି ଶୀଘ୍ର, ଅଧିକ ସୁରକ୍ଷିତ କ୍ରୟ କରିବା ପାଇଁ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"ସବୁ ଦେଖାନ୍ତୁ"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ଟଚପେଡର ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ଆପଣଙ୍କ କୀବୋର୍ଡ ଏବଂ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ଟଚପେଡ ଜେଶ୍ଚର, କୀବୋର୍ଡ ସର୍ଟକଟ ଏବଂ ଆହୁରି ଅନେକ କିଛି ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ହୋମକୁ ଯାଆନ୍ତୁ"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ହୋଇଗଲା"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ପଛକୁ ଫେରନ୍ତୁ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ବାମ କିମ୍ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ବଢ଼ିଆ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ଆପଣ \'ପଛକୁ ଫେରନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ହୋମକୁ ଯାଆନ୍ତୁ"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ବଢ଼ିଆ କାମ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ଆପଣ \'ହୋମକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିକୁ ବ୍ୟବହାର କରି ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ବଢ଼ିଆ କାମ!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ଆପଣ ବର୍ତ୍ତମାନର ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"ସମସ୍ତ ଆପ ଭ୍ୟୁ କରନ୍ତୁ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ବହୁତ ବଢ଼ିଆ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ଆପଣ ସମସ୍ତ ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 4c6697d..63de1a6 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕਰੋ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ ਸੈਟਿੰਗਾਂ ਦਾਖਲ ਕਰੋ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਸੰਗੀਤ ਅਤੇ ਵੀਡੀਓ ਹੈੱਡਫ਼ੋਨਾਂ ਦੇ ਦੋਵਾਂ ਜੋੜਿਆਂ \'ਤੇ ਚੱਲਣਗੇ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ਆਪਣਾ ਆਡੀਓ ਸਾਂਝਾ ਕਰੋ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ਅਤੇ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ਚਾਲੂ"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"<xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g> • \'ਤੇ"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ਬੰਦ"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"ਸੈੱਟ ਨਹੀਂ ਹੈ"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ਕੋਈ ਕਿਰਿਆਸ਼ੀਲ ਮੋਡ ਨਹੀਂ ਹੈ}=1{{mode} ਕਿਰਿਆਸ਼ੀਲ ਹੈ}other{# ਮੋਡ ਕਿਰਿਆਸ਼ੀਲ ਹਨ}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"ਧੁਨੀਆਂ ਅਤੇ ਥਰਥਰਾਹਟਾਂ ਤੁਹਾਨੂੰ ਪਰੇਸ਼ਾਨ ਨਹੀਂ ਕਰਨਗੀਆਂ, ਸਿਵਾਏ ਅਲਾਰਮਾਂ, ਯਾਦ-ਦਹਾਨੀਆਂ, ਵਰਤਾਰਿਆਂ, ਅਤੇ ਤੁਹਾਡੇ ਵੱਲੋਂ ਨਿਰਧਾਰਤ ਕੀਤੇ ਕਾਲਰਾਂ ਦੀ ਸੂਰਤ ਵਿੱਚ। ਤੁਸੀਂ ਅਜੇ ਵੀ ਸੰਗੀਤ, ਵੀਡੀਓ ਅਤੇ ਗੇਮਾਂ ਸਮੇਤ ਆਪਣੀ ਚੋਣ ਅਨੁਸਾਰ ਕੁਝ ਵੀ ਸੁਣ ਸਕਦੇ ਹੋ।"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਘੰਟੀ ਮਿਊਟ ਕੀਤੀ ਹੋਈ ਹੈ"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਵਿਸ਼ੇਸ਼ਤਾ ਚਾਲੂ ਹੈ"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਵਿਸ਼ੇਸ਼ਤਾ ਚਾਲੂ ਹੈ"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ਚਾਲੂ ਹੋਣ ਕਾਰਨ ਇਹ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ਉਪਲਬਧ ਨਹੀਂ"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ਅਣਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ਥਰਥਰਾਹਟ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ਟੱਚਪੈਡ ਇਸ਼ਾਰਿਆਂ ਬਾਰੇ ਜਾਣੋ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਅਤੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ਟੱਚਪੈਡ ਇਸ਼ਾਰੇ, ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਬਾਰੇ ਜਾਣੋ"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ਵਾਪਸ ਜਾਓ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ਹੋ ਗਿਆ"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ਵਾਪਸ ਜਾਓ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ਵਧੀਆ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ਤੁਸੀਂ \'ਵਾਪਸ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ਬਹੁਤ ਵਧੀਆ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ਬਹੁਤ ਵਧੀਆ!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ਤੁਸੀਂ \'ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ।"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ਬਹੁਤ ਵਧੀਆ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ਤੁਸੀਂ \'ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index f4efe0b..a5dbc06 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Udostępnij dźwięk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Udostępnia dźwięk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"aby otworzyć ustawienia udostępniania dźwięku"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzyka i filmy z tego urządzenia będą odtwarzane przez obie pary słuchawek"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Udostępnij dźwięk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> i <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Przełącz na: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Wł."</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Włączone • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Wył."</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nie ustawiono"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Zarządzaj w ustawieniach"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Brak aktywnych trybów}=1{Tryb {mode} jest aktywny}few{# tryby są aktywne}many{# trybów jest aktywnych}other{# trybu jest aktywne}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nie będą Cię niepokoić żadne dźwięki ani wibracje z wyjątkiem alarmów, przypomnień, wydarzeń i połączeń od wybranych osób. Będziesz słyszeć wszystkie odtwarzane treści, takie jak muzyka, filmy czy gry."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niedostępne, bo dzwonek jest wyciszony"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Niedostępne, włączone jest „Nie przeszkadzać”"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Niedostępne, włączone jest „Nie przeszkadzać”"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Niedostępne, bo włączony jest tryb <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Niedostępne"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Kliknij, by wyłączyć wyciszenie."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Kliknij, by włączyć wibracje. Ułatwienia dostępu mogą być wyciszone."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Poznaj gesty na touchpada"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Nawiguj za pomocą klawiatury i touchpada"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Poznaj gesty na touchpada, skróty klawiszowe i inne funkcje"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Wróć"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Otwórz stronę główną"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Wyświetlanie ostatnich aplikacji"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotowe"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Wróć"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Przesuń 3 palcami w prawo lub w lewo na touchpadzie"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Super!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Gest przejścia wstecz został opanowany."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Otwórz stronę główną"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Przesuń 3 palcami w górę na touchpadzie"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Dobra robota!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Gest przechodzenia na ekran główny został opanowany"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Wyświetlanie ostatnich aplikacji"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Przesuń w górę za pomocą 3 palców na touchpadzie i przytrzymaj"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Brawo!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Znasz już gest wyświetlania ostatnio używanych aplikacji."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Wyświetl wszystkie aplikacje"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Naciśnij klawisz działania na klawiaturze"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Brawo!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Znasz już gest wyświetlania wszystkich aplikacji"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b272422..da192e3 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartilhar áudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acessar configurações de compartilhamento de áudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"As músicas e os vídeos deste dispositivo serão reproduzidos nos dois pares de fones de ouvido"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Compartilhamento de áudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Mudar para <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Não definido"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerenciar nas configurações"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nenhum modo ativo}=1{{mode} está ativo}one{# modo está ativo}many{# de modos estão ativos}other{# modos estão ativos}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque silenciado"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível com o Não perturbe ativado"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com o Não perturbe ativado"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponível porque o modo <xliff:g id="MODE">%s</xliff:g> está ativado"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponível"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demonstração"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"O modo <xliff:g id="MODENAME">%1$s</xliff:g> está ativado"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Prepare tudo para fazer compras mais rápidas e seguras com seu smartphone"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue usando o teclado e o touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Deslize para a esquerda ou direita com 3 dedos no touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Legal!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Você concluiu o gesto para voltar."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir para a página inicial"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Deslize para cima com 3 dedos no touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Muito bem!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Você concluiu o gesto para acessar a tela inicial"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver os apps recentes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Deslize para cima e pressione com 3 dedos no touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index cca6d72..127a97ea 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partilhar áudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"A partilhar áudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"aceder às definições de partilha de áudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Os vídeos e as músicas deste dispositivo vão tocar em ambos os pares de auscultadores"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Partilhe o áudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Mudar para <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Não definido"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerir nas definições"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nenhum modo ativo}=1{{mode} está ativo}many{# modos estão ativos}other{# modos estão ativos}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Não é incomodado por sons e vibrações, exceto de alarmes, lembretes, eventos e autores de chamadas que especificar. Continua a ouvir tudo o que optar por reproduzir, incluindo música, vídeos e jogos."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com toque desativado"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível porque Não incomodar está ativado"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com Não incomodar ativado"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponível porque o modo <xliff:g id="MODE">%s</xliff:g> está ativado"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponível"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para reativar o som."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para ativar a vibração. Os serviços de acessibilidade podem ser silenciados."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue com o teclado e o touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos de teclado e muito mais"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Aceder ao ecrã principal"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluir"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Deslize rapidamente para a esquerda ou direita com 3 dedos no touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Boa!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Concluiu o gesto para retroceder."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Aceder ao ecrã principal"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Deslize para cima com 3 dedos no touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"É assim mesmo!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Concluiu o gesto para aceder ao ecrã principal"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver apps recentes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Deslize rapidamente para cima e mantenha premido com 3 dedos no touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Concluiu o gesto para ver as apps recentes."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Prima a tecla de ação no teclado"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Concluiu o gesto para ver todas as apps"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b272422..da192e3 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartilhar áudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"acessar configurações de compartilhamento de áudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"As músicas e os vídeos deste dispositivo serão reproduzidos nos dois pares de fones de ouvido"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Compartilhamento de áudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> e <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Mudar para <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Não definido"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerenciar nas configurações"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nenhum modo ativo}=1{{mode} está ativo}one{# modo está ativo}many{# de modos estão ativos}other{# modos estão ativos}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque silenciado"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível com o Não perturbe ativado"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com o Não perturbe ativado"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponível porque o modo <xliff:g id="MODE">%s</xliff:g> está ativado"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponível"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demonstração"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"O modo <xliff:g id="MODENAME">%1$s</xliff:g> está ativado"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Prepare tudo para fazer compras mais rápidas e seguras com seu smartphone"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue usando o teclado e o touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Deslize para a esquerda ou direita com 3 dedos no touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Legal!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Você concluiu o gesto para voltar."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir para a página inicial"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Deslize para cima com 3 dedos no touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Muito bem!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Você concluiu o gesto para acessar a tela inicial"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver os apps recentes"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Deslize para cima e pressione com 3 dedos no touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pressione a tecla de ação no teclado"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Muito bem!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Você concluiu o gesto para ver todos os apps"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 4222b3d..5c97ab5 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Trimite audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Se permite accesul la conținutul audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"accesa setările de permitere a accesului la audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzica și videoclipurile de pe acest dispozitiv se vor reda pe ambele perechi de căști"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Permite accesul la conținutul tău audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> și <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Comută la <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activat • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Dezactivat"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nesetat"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestionează în setări"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Niciun mod activ}=1{{mode} este activ}few{# moduri sunt active}other{# de moduri sunt active}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de tine. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponibil; soneria este dezactivată"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponibil; funcția Nu deranja e activată"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponibil; funcția Nu deranja e activată"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Indisponibil, deoarece <xliff:g id="MODE">%s</xliff:g> este activat"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Indisponibil"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atinge pentru a activa sunetul."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atinge pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atinge pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Învață gesturi pentru touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navighează folosind tastatura și touchpadul"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Învață gesturi pentru touchpad, comenzi rapide de la tastatură și altele"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Înapoi"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Înapoi la pagina de pornire"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Vezi aplicațiile recente"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gata"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Înapoi"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Glisează la stânga sau la dreapta cu trei degete pe touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bravo!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ai finalizat gestul Înapoi."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Înapoi la pagina de pornire"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Glisează în sus cu trei degete oriunde pe touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelent!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ai finalizat gestul „accesează ecranul de pornire”"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Vezi aplicațiile recente"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Glisează în sus și ține apăsat cu trei degete pe touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Excelent!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ai finalizat gestul pentru afișarea aplicațiilor recente."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Vezi toate aplicațiile"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Apasă tasta de acțiuni de pe tastatură"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Felicitări!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ai finalizat gestul pentru afișarea tuturor aplicațiilor"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 0340a1e..5ae021d 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Отправить аудио"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Отправка аудио"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"перейти в настройки передачи аудио"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музыка и звук видео на этом устройстве будут воспроизводиться через обе пары наушников."</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Передача аудио"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Переключиться на <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Включено"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Вкл. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Отключено"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Не задано"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Открыть настройки"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Включено 0 режимов}=1{Включен режим \"{mode}\"}one{Включен # режим}few{Включено # режима}many{Включено # режимов}other{Включено # режима}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будут отвлекать звуки и вибрация, за исключением сигналов будильника, напоминаний, уведомлений о мероприятиях и звонков от помеченных контактов. Вы по-прежнему будете слышать включенную вами музыку, видео, игры и т. д."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно в беззвучном режиме"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно при включенном режиме \"Не беспокоить\"."</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно при включенном режиме \"Не беспокоить\"."</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недоступно, так как включен режим \"<xliff:g id="MODE">%s</xliff:g>\""</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недоступно"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Нажмите, чтобы включить звук."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Нажмите, чтобы включить вибрацию. Специальные возможности могут прекратить работу."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Узнайте о жестах на сенсорной панели."</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигация с помощью клавиатуры и сенсорной панели"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Узнайте о жестах на сенсорной панели, сочетаниях клавиш и многом другом."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главную"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Жест \"Просмотр недавних приложений\""</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Проведите тремя пальцами влево или вправо по сенсорной панели."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Отлично!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы выполнили жест для перехода назад."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На главный экран"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Проведите тремя пальцами вверх по сенсорной панели."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отличная работа!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Вы выполнили жест для перехода на главный экран."</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Просмотр недавних приложений"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Проведите вверх по сенсорной панели тремя пальцами и удерживайте."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы выполнили жест для просмотра недавних приложений."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Все приложения"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Нажмите клавишу действия на клавиатуре."</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Блестяще!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Вы выполнили жест для просмотра всех приложений."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 93f7258..03c2ba4 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ශ්‍රව්‍ය බෙදා ගන්න"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ශ්‍රව්‍ය බෙදා ගැනීම"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ශ්‍රව්‍ය බෙදා ගැනීමේ සැකසීම් ඇතුළු කරන්න"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"මෙම උපාංගයේ සංගීතය සහ වීඩියෝ හෙඩ්ෆෝන් යුගල දෙකෙහිම වාදනය වනු ඇත"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ඔබේ ශ්‍රව්‍ය බෙදා ගන්න"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> සහ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> වෙත මාරු වන්න"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්‍රව්‍ය"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ක්‍රියාත්මකයි"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ක්‍රියාත්මකයි • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ක්‍රියාවිරහිතයි"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"සකසා නැත"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"සැකසීම් තුළ කළමනාකරණය කරන්න"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{සක්‍රිය ප්‍රකාර නොමැත}=1{{mode} සක්‍රියයි}one{ප්‍රකාර #ක් සක්‍රියයි}other{ප්‍රකාර #ක් සක්‍රියයි}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"එලාම සිහිකැඳවීම්, සිදුවීම්, සහ ඔබ සඳහන් කළ අමතන්නන් හැර, ශබ්ද සහ කම්පනවලින් ඔබට බාධා නොවනු ඇත. සංගීතය, වීඩියෝ, සහ ක්‍රීඩා ඇතුළු ඔබ වාදනය කිරීමට තෝරන ලද සියලු දේ ඔබට තවම ඇසෙනු ඇත."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"නාදය නිහඬ කර ඇති නිසා නොලැබේ"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"බාධා නොකරන්න ක්‍රියාත්මකව ඇති බැවින් ලද නොහැක"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"බාධා නොකරන්න ක්‍රියාත්මකව ඇති බැවින් ලද නොහැක"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ක්‍රියාත්මක නිසා ලබා ගත නොහැක"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"නොමැත"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. නිහඬ කිරීම ඉවත් කිරීමට තට්ටු කරන්න."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. කම්පනය කිරීමට තට්ටු කරන්න. ප්‍රවේශ්‍යතා සේවා නිහඬ කළ හැකිය."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්‍රවේශ්‍යතා සේවා නිහඬ කළ හැකිය."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"ආදර්ශන ප්‍රකාරය පෙන්වන්න"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"එලාමය"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> ක්‍රියාත්මකයි"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"ඔබගේ දුරකථනය සමඟ වඩා වේගවත්, වඩා සුරක්ෂිත මිලදී ගැනීම් සිදු කිරීමට සූදානම් වන්න"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"සියල්ල පෙන්වන්න"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ස්පර්ශක පුවරු අභිනයන් ඉගෙන ගන්න"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ඔබේ යතුරු පුවරුව සහ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ස්පර්ශ පෑඩ් අභිනයන්, යතුරුපුවරු කෙටිමං සහ තවත් දේ ඉගෙන ගන්න"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ආපසු යන්න"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"මුල් පිටුවට යන්න"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"මෑත යෙදුම් බලන්න"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"නිමයි"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ආපස්සට යන්න"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ඔබේ ස්පර්ශ පුවරුව මත ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"කදිමයි!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ඔබ ආපසු යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"මුල් පිටුවට යන්න"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ඔබේ ස්පර්ශ පුවරුවේ ඇඟිලි තුනකින් ඉහළට ස්වයිප් කරන්න"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"අනර්ඝ වැඩක්!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"මෑත යෙදුම් බලන්න"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ඉහළට ස්වයිප් කර ඔබේ ස්පර්ශ පුවරුව මත ඇඟිලි තුනක් භාවිත කර රඳවාගෙන සිටින්න"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"අනර්ඝ වැඩක්!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ඔබ මෑත යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"සියලු යෙදුම් බලන්න"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"ඔබේ යතුරු පුවරුවේ ක්‍රියාකාරී යතුර ඔබන්න"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"හොඳින් කළා!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"ඔබ සියලු යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index e7c9263..ac80ae2 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Zdieľať zvuk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zdieľa sa zvuk"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"prejsť do nastavení zdieľania zvuku"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Hudba a videá z tohto zariadenia sa budú prehrávať v oboch pároch slúchadiel"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Zdieľanie zvuku"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> a <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Prepnúť na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuté"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Zapnuté • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuté"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nenastavené"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Správa v nastaveniach"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Žiadne aktívne režimy}=1{{mode} je aktívny}few{# režimy sú aktívne}many{# modes are active}other{# režimov je aktívnych}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudú vás vyrušovať zvuky ani vibrácie, iba budíky, pripomenutia, udalosti a volajúci, ktorých určíte. Budete naďalej počuť všetko, čo sa rozhodnete prehrať, ako napríklad hudbu, videá a hry."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, pretože je vypnuté zvonenie"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupné, pretože je zapnutý režim bez vyrušení"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupné, zapnutý režim bez vyrušení"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nedostupné, pretože je zapnutý režim <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nedostupné"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnite zvuk."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujte režim vibrovania. Služby dostupnosti je možné stlmiť."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučte sa gestá touchpadu"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Prechádzajte pomocou klávesnice a touchpadu"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte sa gestá touchpadu, klávesové skratky a ďalšie funkcie"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Prejsť späť"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Prejsť na plochu"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazenie nedávnych aplikácií"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Prejsť späť"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Potiahnite troma prstami na touchpade doľava alebo doprava"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Výborne!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dokončili ste gesto na prechod späť."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Prechod na plochu"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Potiahnite troma prstami na touchpade nahor"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Skvelé!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dokončili ste gesto na prechod na plochu"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazenie nedávnych aplikácií"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Potiahnite troma prstami na touchpade nahor a pridržte"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Skvelé!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dokončili ste gesto na zobrazenie nedávnych aplikácií."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazenie všetkých aplikácií"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stlačte na klávesnici akčný kláves"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborne!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dokončili ste gesto na zobrazenie všetkých aplikácií"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 701cdd1..f953a94 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deli zvok"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Poteka deljenje zvoka"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"odpiranje nastavitev deljenja zvoka"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Glasba in videoposnetki v tej napravi bodo predvajani v obeh parih slušalk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Deljenje zvoka"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> in <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Preklopi na <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -672,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ni na voljo, ker je zvonjenje izklopljeno"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ni na voljo, ker je vklopljen način »Ne moti«"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Ni na voljo, ker je vklopljen način »Ne moti«"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ni na voljo, ker je vklopljen način <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ni na voljo"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dotaknite se, če želite vklopiti zvok."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dotaknite se, če želite nastaviti vibriranje. V storitvah za dostopnost bo morda izklopljen zvok."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za dostopnost bo morda izklopljen zvok."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 20f7e82..0166acc 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Ndaj audion"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioja po ndahet"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"për të hyrë te cilësimet e ndarjes së audios"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Muzika dhe videot e kësaj pajisjeje do të luhen në të dyja palët e kufjeve"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Ndaj audion tënde"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> dhe <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Kalo te profili \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktiv • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Joaktiv"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Nuk është caktuar"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Menaxho te cilësimet"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nuk ka modalitete aktive}=1{\"{mode}\" është aktiv}other{# modalitete janë aktive}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nuk do të shqetësohesh nga tingujt dhe dridhjet, përveç alarmeve, alarmeve rikujtuese, ngjarjeve dhe telefonuesve që specifikon. Do të vazhdosh të dëgjosh çdo gjë që zgjedh të luash duke përfshirë muzikën, videot dhe lojërat."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nuk ofrohet; ziles i është hequr zëri"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nuk ofrohet; \"Mos shqetëso\" është aktiv"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Nuk ofrohet; \"Mos shqetëso\" është aktiv"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Nuk ofrohet sepse \"<xliff:g id="MODE">%s</xliff:g>\" është aktiv"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Nuk ofrohet"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trokit për të aktivizuar."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trokit për ta caktuar te dridhja. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Shfaq modalitetin e demonstrimit"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Eternet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Alarmi"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"\"<xliff:g id="MODENAME">%1$s</xliff:g>\" është aktiv"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Portofoli"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Konfiguro për të kryer pagesa më të shpejta dhe më të sigurta përmes telefonit"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Shfaqi të gjitha"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Mëso gjestet e bllokut me prekje"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigo duke përdorur tastierën dhe bllokun me prekje"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Mëso gjestet e bllokut me prekje, shkurtoret e tastierës etj."</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kthehu prapa"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Shko tek ekrani bazë"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Shiko aplikacionet e fundit"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"U krye"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kthehu prapa"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Rrëshqit shpejt majtas ose djathtas duke përdorur tre gishta në bllokun me prekje."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bukur!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"E ke përfunduar gjestin e kthimit prapa."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Shko tek ekrani bazë"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Rrëshqit shpejt lart me tre gishta në bllokun me prekje"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Punë e shkëlqyer!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"E ke përfunduar gjestin e kalimit tek ekrani bazë"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Shiko aplikacionet e fundit"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Rrëshqit shpejt lart dhe mbaj shtypur me tre gishta në bllokun me prekje"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Punë e shkëlqyer!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Përfundove gjestin për shikimin e aplikacioneve të fundit."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Shiko të gjitha aplikacionet"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Shtyp tastin e veprimit në tastierë"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Shumë mirë!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Përfundove gjestin për shikimin e të gjitha aplikacioneve"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index f757cdc..d7075ad 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Дели звук"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Дели се звук"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"уђите у подешавања дељења звука"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музика и видеи са овог уређаја се репродукују на пару слушалица"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Делите звук"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> и <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Пређи на <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Укључено"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Укљ. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Искључено"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Није подешено"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управљајте у подешавањима"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Нема активних режима}=1{Активан је {mode} режим}one{Активан је # режим}few{Активна су # режима}other{Активно је # режима}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Неће вас узнемиравати звукови и вибрације осим за аларме, подсетнике, догађаје и позиваоце које наведете. И даље ћете чути све што одаберете да пустите, укључујући музику, видео снимке и игре."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно јер је звук искључен"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно јер је укључен режим Не узнемиравај"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно јер је укључен режим Не узнемиравај"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Није доступно јер је укључено: <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недоступно"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Додирните да бисте укључили звук."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Додирните да бисте подесили на вибрацију. Звук услуга приступачности ће можда бити искључен."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научите покрете за тачпед"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Крећите се помоћу тастатуре и тачпeда"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научите покрете за тачпед, тастерске пречице и друго"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Иди на почетни екран"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи недавно коришћене апликације"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Превуците улево или удесно са три прста на тачпеду"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Свака част!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Довршили сте покрет за повратак."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Иди на почетни екран"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Превуците нагоре са три прста на тачпеду"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Одлично!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Довршили сте покрет за повратак на почетну страницу."</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прикажи недавно коришћене апликације"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Превуците нагоре и задржите са три прста на тачпеду"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Довршили сте покрет за приказивање недавно коришћених апликација."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Прикажи све апликације"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Притисните тастер радњи на тастатури"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Одлично!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Довршили сте покрет за приказивање свих апликација."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c8ece13..e03fc27 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dela ljud"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Delar ljud"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"öppna inställningarna för ljuddelning"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Musik och videor från den här enheten spelas upp i båda paren hörlurar"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Dela ljudet"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> och <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Byt till <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -672,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Otillgängligt eftersom ringljudet är av"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Inte tillgängligt eftersom Stör ej är aktiverat"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Inte tillgängligt eftersom Stör ej är aktiverat"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Inte tillgängligt eftersom <xliff:g id="MODE">%s</xliff:g> är på"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Inte tillgängligt"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryck här om du vill slå på ljudet."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryck här om du vill sätta på vibrationen. Tillgänglighetstjänster kanske inaktiveras."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 17a8823..0ab6f7d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sikiliza pamoja na wengine"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Mnasikiliza pamoja"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"uweke mipangilio ya kusikiliza pamoja"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Faili za muziki na video katika kifaa hiki zitacheza kwenye jozi zote mbili za vipokea sauti vya kichwani"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Kusikiliza maudhui yako ya sauti pamoja na wengine"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> na <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Badilisha utumie <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Imewashwa"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Imewashwa • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Imezimwa"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Haijawekwa"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Dhibiti katika mipangilio"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Hakuna hali zinazotumika}=1{Unatumia {mode}}other{Unatumia hali #}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele, vikumbusho, matukio na simu zinazopigwa na watu uliobainisha. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Halipatikani kwa sababu sauti imezimwa"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Haipatikani kwa sababu umewasha hali ya <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Haipatikani"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Gusa ili urejeshe."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Jifunze kuhusu miguso ya padi ya kugusa"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Kusogeza kwa kutumia kibodi na padi yako ya kugusa"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Jifunze kuhusu miguso ya padi ya kugusa, mikato ya kibodi na mengineyo"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Rudi nyuma"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Nenda kwenye ukurasa wa mwanzo"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Angalia programu za hivi majuzi"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Nimemaliza"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Rudi nyuma"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Telezesha vidole vitatu kushoto au kulia kwenye padi yako ya kugusa"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Safi!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Umekamilisha ishara ya kurudi nyuma."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Nenda kwenye skrini ya kwanza"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Telezesha vidole vitatu juu kwenye padi yako ya kugusa"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Kazi nzuri!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Umeweka ishara ya kwenda kwenye skrini ya kwanza"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Angalia programu za hivi majuzi"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Telezesha vidole vitatu juu kisha ushikilie kwenye padi yako ya kugusa"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kazi nzuri!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Umekamilisha mafunzo ya mguso wa kuangalia programu za hivi majuzi."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Angalia programu zote"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Bonyeza kitufe cha vitendo kwenye kibodi yako"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Vizuri sana!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Umekamilisha mafunzo ya mguso wa kuangalia programu zote"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index fc6d20e..c661846 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -27,6 +27,12 @@
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">true</bool>
 
+    <!-- The number of rows in the paginated grid QuickSettings -->
+    <integer name="quick_settings_paginated_grid_num_rows">3</integer>
+
+    <!-- The number of rows in the paginated grid QuickQuickSettings -->
+    <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
     <!-- The number of columns in the QuickSettings -->
     <integer name="quick_settings_num_columns">2</integer>
 
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index 857e162..f556b97 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -21,9 +21,18 @@
     <!-- The maximum number of rows in the QuickSettings -->
     <integer name="quick_settings_max_rows">3</integer>
 
+    <!-- The number of rows in the paginated grid QuickSettings -->
+    <integer name="quick_settings_paginated_grid_num_rows">3</integer>
+
+    <!-- The number of rows in the paginated grid QuickQuickSettings -->
+    <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
     <!-- The number of columns in the QuickSettings -->
     <integer name="quick_settings_num_columns">3</integer>
 
+    <!-- The number of columns in the infinite grid QuickSettings -->
+    <integer name="quick_settings_infinite_grid_num_columns">6</integer>
+
     <integer name="power_menu_lite_max_columns">2</integer>
     <integer name="power_menu_lite_max_rows">3</integer>
 
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 2381e1b..f930d84 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ஆடியோவைப் பகிர்"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ஆடியோ பகிரப்படுகிறது"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ஆடியோ பகிர்வு அமைப்புகளுக்குச் செல்லும்"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"இந்தச் சாதனத்தின் இசையும் வீடியோக்களும் இரண்டு ஜோடி ஹெட்ஃபோன்களிலும் பிளே ஆகும்"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"ஆடியோவைப் பகிர்தல்"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>க்கு மாற்று"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"இயக்கப்பட்டுள்ளது"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ஆன் • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"முடக்கப்பட்டுள்ளது"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"அமைக்கப்படவில்லை"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"அமைப்புகளில் நிர்வகியுங்கள்"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{செயலிலுள்ள பயன்முறைகள் எதுவுமில்லை}=1{{mode} செயலில் உள்ளது}other{# பயன்முறைகள் செயலில் உள்ளன}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"அலாரங்கள், நினைவூட்டல்கள், நிகழ்வுகள் மற்றும் குறிப்பிட்ட அழைப்பாளர்களைத் தவிர்த்து, பிற ஒலிகள் மற்றும் அதிர்வுகளின் தொந்தரவு இருக்காது. எனினும், நீங்கள் எதையேனும் (இசை, வீடியோக்கள், கேம்ஸ் போன்றவை) ஒலிக்கும்படி தேர்ந்தெடுத்திருந்தால், அவை வழக்கம் போல் ஒலிக்கும்."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"\'ரிங்\' மியூட்டில் உள்ளதால் கிடைக்கவில்லை"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'தொந்தரவு செய்ய வேண்டாம்\' ஆனில் இருப்பதால் இல்லை"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"\'தொந்தரவு செய்ய வேண்டாம்\' ஆனில் இருப்பதால் இல்லை"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> பயன்முறை இயக்கத்தில் இருப்பதால் கிடைக்கவில்லை"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"கிடைக்கவில்லை"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ஒலி இயக்க, தட்டவும்."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
@@ -972,7 +977,7 @@
     <string name="notification_channel_screenshot" msgid="7665814998932211997">"ஸ்கிரீன் ஷாட்டுகள்"</string>
     <string name="notification_channel_instant" msgid="7556135423486752680">"இன்ஸ்டண்ட் ஆப்ஸ்"</string>
     <string name="notification_channel_setup" msgid="7660580986090760350">"அமைவு"</string>
-    <string name="notification_channel_storage" msgid="2720725707628094977">"சேமிப்பிடம்"</string>
+    <string name="notification_channel_storage" msgid="2720725707628094977">"சேமிப்பகம்"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"குறிப்புகள்"</string>
     <string name="notification_channel_accessibility" msgid="8956203986976245820">"மாற்றுத்திறன் வசதி"</string>
     <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"டச்பேட் சைகைள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"உங்கள் டச்பேட் மற்றும் கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"டச்பேட் சைகைகள், கீபோர்டு ஷார்ட்கட்கள் மற்றும் பலவற்றைத் தெரிந்துகொள்ளுங்கள்"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"பின்செல்"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"முகப்பிற்குச் செல்"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"முடிந்தது"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"பின்செல்"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"உங்கள் டச்பேடில் மூன்று விரல்களால் இடது அல்லது வலதுபுறம் ஸ்வைப் செய்யவும்"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"அருமை!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"பின்செல்வதற்கான சைகையை நிறைவுசெய்துவிட்டீர்கள்."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"முகப்பிற்குச் செல்"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்யவும்"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"அருமை!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"முகப்புக்குச் செல்வதற்கான சைகைப் பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"உங்கள் டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும்"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"அருமை!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"சமீபத்தில் பயன்படுத்திய ஆப்ஸுக்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"அனைத்து ஆப்ஸையும் காட்டு"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"உங்கள் கீபோர்டில் ஆக்‌ஷன் பட்டனை அழுத்தவும்"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"அருமை!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"அனைத்து ஆப்ஸுக்கான சைகை பயிற்சியையும் நிறைவுசெய்துவிட்டீர்கள்"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index fcc344b..85973df 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ఆడియోను షేర్ చేయండి"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ఆడియోను షేర్ చేస్తున్నారు"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"ఆడియో షేరింగ్ సెట్టింగ్‌లను ఎంటర్ చేయండి"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"ఈ పరికరం మ్యూజిక్, వీడియోలు హెడ్‌ఫోన్స్‌కు సంబంధించిన పెయిర్‌ల రెండింటిలోనూ ప్లే అవుతాయి"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"మీ ఆడియోను షేర్ చేయండి"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>, <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>‌కు మారండి"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్‌సెట్"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"ఆన్‌లో ఉంది"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"ఆన్ • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ఆఫ్‌లో ఉంది"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"సెట్ చేసి లేదు"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"సెట్టింగ్‌లలో మేనేజ్ చేయండి"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{మోడ్స్ ఏవీ యాక్టివ్‌గా లేవు}=1{{mode} యాక్టివ్‌గా ఉంది}other{# మోడ్స్ యాక్టివ్‌గా ఉన్నాయి}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"మీరు పేర్కొనే అలారాలు, రిమైండర్‌లు, ఈవెంట్‌లు మరియు కాలర్‌ల నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్‌లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"వాల్యూమ్ మ్యూట్ అయినందున అందుబాటులో లేదు"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"అంతరాయంకలిగించవద్దు ఆన్‌లో ఉన్నందున అందుబాటులోలేదు"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"అంతరాయంకలిగించవద్దు ఆన్‌లో ఉన్నందున అందుబాటులోలేదు"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> ఆన్‌లో ఉన్నందున అందుబాటులో లేదు"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"అందుబాటులో లేదు"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. అన్‌మ్యూట్ చేయడానికి నొక్కండి."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. వైబ్రేషన్‌కు సెట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"టచ్‌ప్యాడ్ సంజ్ఞ గురించి తెలుసుకోండి"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"మీ కీబోర్డ్, టచ్‌ప్యాడ్‌ను ఉపయోగించి నావిగేట్ చేయండి"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"టచ్‌ప్యాడ్ సంజ్ఞలు, కీబోర్డ్ షార్ట్‌కట్‌లు, అలాగే మరిన్నింటిని గురించి తెలుసుకోండి"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"వెనుకకు వెళ్లండి"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"మొదటి ట్యాబ్‌కు వెళ్లండి"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ఇటీవలి యాప్‌లను చూడండి"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"పూర్తయింది"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"వెనుకకు"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"మీ టచ్‌ప్యాడ్‌లో మూడు వేళ్లను ఉపయోగించి ఎడమ వైపునకు లేదా కుడి వైపునకు స్వైప్ చేయండి"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"పనితీరు బాగుంది!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"తిరిగి వెనుకకు వెళ్ళడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"మొదటి ట్యాబ్‌కు వెళ్లండి"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"మీ టచ్‌ప్యాడ్‌పై మూడు వేళ్లతో పైకి స్వైప్ చేయండి"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"చక్కగా పూర్తి చేశారు!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"మీరు మొదటి స్క్రీన్‌కు వెళ్లే సంజ్ఞను పూర్తి చేశారు"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ఇటీవలి యాప్‌లను చూడండి"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"మీ టచ్‌ప్యాడ్‌లో మూడు వేళ్లను ఉపయోగించి పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"పనితీరు అద్భుతంగా ఉంది!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"మీరు ఇటీవలి యాప్‌ల వీక్షణ సంజ్ఞను పూర్తి చేశారు."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"అన్ని యాప్‌లను చూడండి"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"చక్కగా చేశారు!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"మీరు అన్ని యాప్‌ల వీక్షణ సంజ్ఞను పూర్తి చేశారు"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్‌లైట్"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 06c2661..67006b6 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"แชร์เสียง"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"กำลังแชร์เสียง"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"เข้าสู่การตั้งค่าการแชร์เสียง"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"เพลงและวิดีโอของอุปกรณ์เครื่องนี้จะเล่นในหูฟังทั้ง 2 คู่"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"แชร์เสียงของคุณ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> และ <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"เปลี่ยนเป็น <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"เปิด"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"เปิด • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"ปิด"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"ไม่ได้ตั้งค่า"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"จัดการในการตั้งค่า"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ไม่มีโหมดที่ใช้งานอยู่}=1{ใช้งานอยู่ {mode} โหมด}other{ใช้งานอยู่ # โหมด}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นเสียงนาฬิกาปลุก การช่วยเตือน กิจกรรม และผู้โทรที่ระบุไว้ คุณจะยังคงได้ยินสิ่งที่คุณเลือกเล่น เช่น เพลง วิดีโอ และเกม"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"เปลี่ยนไม่ได้เนื่องจากปิดเสียงเรียกเข้า"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"ใช้งานไม่ได้เนื่องจากเปิด<xliff:g id="MODE">%s</xliff:g>อยู่"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"ไม่พร้อมใช้งาน"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s แตะเพื่อเปิดเสียง"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s แตะเพื่อตั้งค่าให้สั่น อาจมีการปิดเสียงบริการการเข้าถึง"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s แตะเพื่อปิดเสียง อาจมีการปิดเสียงบริการการเข้าถึง"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์และทัชแพด"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด แป้นพิมพ์ลัด และอื่นๆ"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ย้อนกลับ"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ไปที่หน้าแรก"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ดูแอปล่าสุด"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"เสร็จสิ้น"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาบนทัชแพด"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ดีมาก"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ไปที่หน้าแรก"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"เก่งมาก"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ดูแอปล่าสุด"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"เยี่ยมมาก"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"คุณทำท่าทางสัมผัสเพื่อดูแอปล่าสุดสำเร็จแล้ว"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"ดูแอปทั้งหมด"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ยอดเยี่ยม"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดเสร็จแล้ว"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 8241b72..e41c53f 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Ibahagi ang audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ibinabahagi ang audio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"pumasok sa mga setting sa pag-share ng audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Magpe-play ang musika at mga video ng device na ito sa parehong pares ng headphones"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Ibahagi ang iyong audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> at <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Lumipat sa <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Naka-on"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Naka-on • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Naka-off"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Hindi nakatakda"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Pamahalaan sa mga setting"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Walang aktibong mode}=1{Aktibo ang {mode}}one{# mode ang aktibo}other{# na mode ang aktibo}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Hindi ka maiistorbo ng mga tunog at pag-vibrate, maliban mula sa mga alarm, paalala, event, at tumatawag na tutukuyin mo. Maririnig mo pa rin ang kahit na anong piliin mong i-play kabilang ang mga musika, video, at laro."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Hindi available dahil naka-mute ang ring"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Hindi available dahil naka-on ang Huwag Istorbohin"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Hindi available dahil naka-on ang Huwag Istorbohin"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Hindi available dahil naka-on ang <xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Hindi available"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. I-tap upang i-unmute."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. I-tap upang itakda na mag-vibrate. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. I-tap upang i-mute. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Matuto ng mga galaw sa touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Mag-navigate gamit ang iyong keyboard at touchpad"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Matuto ng mga galaw sa touchpad, keyboard shortcut, at higit pa"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Bumalik"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pumunta sa home"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Tingnan ang mga kamakailang app"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tapos na"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Bumalik"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Mag-swipe pakaliwa o pakanan gamit ang tatlong daliri sa iyong touchpad"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Magaling!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Nakumpleto mo na ang galaw para bumalik."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Pumunta sa home"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Mag-swipe pataas gamit ang tatlong daliri sa iyong touchpad"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Magaling!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Nakumpleto mo na ang galaw para pumunta sa home"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Tingnan ang mga kamakailang app"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Mag-swipe pataas at i-hold gamit ang tatlong daliri sa iyong touchpad"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Magaling!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Nakumpleto mo ang galaw sa pag-view ng mga kamakailang app."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Tingnan ang lahat ng app"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Pindutin ang action key sa iyong keyboard"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Magaling!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Nakumpleto mo ang galaw sa pag-view ng lahat ng app"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 966c6ef..1dab64a 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sesi paylaş"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ses paylaşılıyor"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"Ses paylaşımı ayarlarına gitmek için"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Bu cihazdaki müzikler ve videolar iki kulaklıkta da oynatılacak"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Sesi paylaşın"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ve <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> cihazına geç"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Açık"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Açık • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Kapalı"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Ayarlanmadı"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Ayarlarda yönet"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Etkin mod yok}=1{{mode} etkin}other{# mod etkin}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Alarmlar, hatırlatıcılar, etkinlikler ve sizin seçtiğiniz kişilerden gelen çağrılar dışında hiçbir ses ve titreşimle rahatsız edilmeyeceksiniz. O sırada çaldığınız müzik, seyrettiğiniz video ya da oynadığınız oyunların sesini duymaya devam edeceksiniz."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zil sesi kapatıldığı için kullanılamıyor"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Rahatsız Etmeyin açık olduğu için kullanılamıyor"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Rahatsız Etmeyin açık olduğu için kullanılamıyor"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> açık olduğu için kullanılamıyor"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Yok"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sesi açmak için dokunun."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Titreşime ayarlamak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sesi kapatmak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Demo modunu göster"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> açık"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Cüzdan"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Telefonunuzla daha hızlı ve güvenli satın alma işlemleri gerçekleştirmek için gerekli ayarları yapın"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Tümünü göster"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Dokunmatik alan hareketlerini öğrenin"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klavyenizi ve dokunmatik alanınızı kullanarak gezinin"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Dokunmatik alan hareketlerini, klavye kısayollarını ve daha fazlasını öğrenin"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri dön"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ana sayfaya git"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son uygulamaları görüntüle"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Bitti"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri dön"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Dokunmatik alanda üç parmağınızla sola veya sağa kaydırın"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Güzel!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Geri dön hareketini tamamladınız."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ana sayfaya gidin"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Dokunmatik alanda üç parmağınızla yukarı kaydırın"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Tebrikler!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ana ekrana git hareketini tamamladınız"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Son uygulamaları görüntüle"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Dokunmatik alanda üç parmağınızla yukarı doğru kaydırıp basılı tutun"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tebrikler!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son uygulamaları görüntüleme hareketini tamamladınız."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Tüm uygulamaları göster"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klavyenizde eylem tuşuna basın"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Tebrikler!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Tüm uygulamaları görüntüleme hareketini tamamladınız"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c146021..fcdccdb 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Поділитись аудіо"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Надсилання аудіо"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"відкрити налаштування надсилання аудіо"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музика й відео із цього пристрою відтворюватимуться на обох парах навушників"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Поділитися аудіо"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"Пристрої \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\" і \"<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>\""</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Перемкнути на \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Увімкнено"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Увімк. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Вимкнено"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Не налаштовано"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Керувати в налаштуваннях"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Немає активних режимів}=1{Активовано режим \"{mode}\"}one{Активовано # режим}few{Активовано # режими}many{Активовано # режимів}other{Активовано # режиму}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ви отримуватиме звукові та вібросигнали лише для вибраних будильників, нагадувань, подій і абонентів. Однак ви чутимете все, що захочете відтворити, зокрема музику, відео й ігри."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно: звук дзвінків вимкнено"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно: увімкнено режим \"Не турбувати\""</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно: увімкнено режим \"Не турбувати\""</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Недоступно, оскільки ввімкнено режим \"<xliff:g id="MODE">%s</xliff:g>\""</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Недоступно"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Торкніться, щоб увімкнути звук."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Показати демонстраційний режим"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"Будильник"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"Увімкнено режим \"<xliff:g id="MODENAME">%1$s</xliff:g>\""</string>
     <string name="wallet_title" msgid="5369767670735827105">"Гаманець"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Швидше й безпечніше сплачуйте за покупки за допомогою телефона"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Показати все"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Жести для сенсорної панелі: докладніше"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігація за допомогою клавіатури й сенсорної панелі"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Жести для сенсорної панелі, комбінації клавіш тощо: докладніше"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Перейти на головний екран"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Переглянути нещодавні додатки"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Проведіть трьома пальцями вліво чи вправо по сенсорній панелі"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Чудово!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ви виконали жест \"Назад\"."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Перейти на головний екран"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Проведіть трьома пальцями вгору на сенсорній панелі"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Чудово!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ви виконали жест переходу на головний екран"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Переглянути нещодавні додатки"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Проведіть трьома пальцями вгору й утримуйте їх на сенсорній панелі"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Чудово!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ви виконали жест для перегляду нещодавно відкритих додатків."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Переглянути всі додатки"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Натисніть клавішу дії на клавіатурі"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Чудово!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Ви виконали жест для перегляду всіх додатків"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index a0e9347..a091d30 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"آڈیو کا اشتراک کریں"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"آڈیو کا اشتراک ہو رہا ہے"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"آڈیو کے اشتراک کی ترتیبات درج کریں"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"اس آلہ کی موسیقی اور ویڈیوز ہیڈ فونز کے دونوں جوڑے پر چلیں گی"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"اپنی آڈیو کا اشتراک کریں"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> اور <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> پر سوئچ کریں"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"آن ہے"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"آن ہے • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"آف ہے"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"سیٹ نہیں ہے"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ترتیبات میں نظم کریں"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{کوئی فعال موڈ نہیں ہے}=1{{mode} فعال ہے}other{# موڈز فعال ہیں}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"الارمز، یاددہانیوں، ایونٹس اور آپ کے متعین کردہ کالرز کے علاوہ، آپ آوازوں اور وائبریشنز سے ڈسٹرب نہیں ہوں گے۔ موسیقی، ویڈیوز اور گیمز سمیت آپ ابھی بھی ہر وہ چیز سنیں گے جسے چلانے کا آپ انتخاب کرتے ہیں۔"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"دستیاب نہیں ہے کیونکہ رنگ خاموش ہے"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"غیر دستیاب ہے کیونکہ ڈسٹرب نہ کریں آن ہے"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"غیر دستیاب ہے کیونکہ ڈسٹرب نہ کریں آن ہے"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> کے آن ہونے کی وجہ سے غیر دستیاب ہے"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"غیر دستیاب ہے"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"‏‎%1$s۔ آواز چالو کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"‏‎%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏‎%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ٹچ پیڈ کے اشارے کو جانیں"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"اپنے کی بورڈ اور ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ٹچ پیڈ کے اشارے، کی بورڈ شارٹ کٹس اور مزید جانیں"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"واپس جائیں"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"گھر جائیں"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"حالیہ ایپس دیکھیں"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ہو گیا"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"اپنے ٹچ پیڈ پر تین انگلیوں کا استعمال کرتے ہوئے دائیں یا بائیں طرف سوائپ کریں"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"عمدہ!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"آپ نے واپس جائیں اشارے کو مکمل کر لیا۔"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ہوم پر جائیں"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"اپنے ٹچ پیڈ پر تین انگلیوں کی مدد سے اوپر کی طرف سوائپ کریں"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"بہترین!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"حالیہ ایپس دیکھیں"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"اپنے ٹچ پیڈ پر تین انگلیوں کا استعمال کرتے ہوئے اوپر کی طرف سوائپ کریں اور دبائے رکھیں"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"بہترین!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس کا اشارہ مکمل کر لیا ہے۔"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"سبھی ایپس دیکھیں"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"بہت خوب!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس کا اشارہ مکمل کر لیا ہے"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏%2$d میں سے ‎%1$d کا لیول"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 0594313..51dcb29 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioni ulashish"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio ulashuvi yoniq"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"audio ulashuv sozlamalarini kiritish"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Bu qurilmadagi musiqa va videolar quloqliklarning ikkala tomonida ijro etiladi"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Audioni ulashish"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> va <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Bunga almashish: <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Yoniq"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Yoniq • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Yoqilmagan"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Sozlanmagan"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Sozlamalarda boshqarish"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{0 ta rejim faol}=1{{mode} faol}other{# ta rejim faol}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan chaqiruvlar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Jiringlash ovozsizligi uchun ishlamaydi"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Bezovta qilinmasin yoniqligi sababli ishlamaydi"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Bezovta qilinmasin yoniqligi sababli ishlamaydi"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g> yoniqligi uchun ishlamaydi"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ishlamaydi"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ovozini yoqish uchun ustiga bosing."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tebranishni yoqish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Sensorli panel ishoralari haqida"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura va sensorli panel yordamida kezing"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sensorli panel ishoralari, tezkor tugmalar va boshqalar haqida"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Orqaga"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Oxirgi ilovalarni koʻrish"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Sensorli panelda uchta barmoq bilan chapga yoki oʻngga suring"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Yaxshi!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ortga qaytish ishorasi darsini tamomladingiz."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Boshiga"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Sensorli panelda uchta barmoq bilan tepaga suring"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Barakalla!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Bosh ekranni ochish ishorasi darsini tamomladingiz"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Oxirgi ilovalarni koʻrish"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Sensorli panelda uchta barmoq bilan tepaga surib, bosib turing"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Barakalla!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Oxirgi ilovalarni koʻrish ishorasini tugalladingiz."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Barcha ilovalarni koʻrish"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Klaviaturadagi amal tugmasini bosing"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Barakalla!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Hamma ilovalarni koʻrish ishorasini tugalladingiz"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c38f99f..98fc467 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Chia sẻ âm thanh"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Đang chia sẻ âm thanh"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"mở chế độ cài đặt chia sẻ âm thanh"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Nhạc và video trên thiết bị này sẽ phát trên cả hai bộ tai nghe"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Chia sẻ âm thanh của bạn"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> và <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Chuyển sang <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Đang bật"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Bật • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Đang tắt"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Chưa đặt"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Quản lý trong phần cài đặt"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Không có chế độ nào đang hoạt động}=1{{mode} đang hoạt động}other{# chế độ đang hoạt động}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Bạn sẽ không bị làm phiền bởi âm thanh và tiếng rung, ngoại trừ báo thức, lời nhắc, sự kiện và người gọi mà bạn chỉ định. Bạn sẽ vẫn nghe thấy mọi thứ bạn chọn phát, bao gồm nhạc, video và trò chơi."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Không hoạt động vì chuông bị tắt"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Bị tắt vì đang bật chế độ Không làm phiền"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Bị tắt vì đang bật chế độ Không làm phiền"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Không dùng được vì <xliff:g id="MODE">%s</xliff:g> đang bật"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Không dùng được"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Nhấn để bật tiếng."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Nhấn để đặt chế độ rung. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Tìm hiểu về cử chỉ trên bàn di chuột"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Di chuyển bằng bàn phím và bàn di chuột"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Tìm hiểu về cử chỉ trên bàn di chuột, phím tắt và nhiều mục khác"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Quay lại"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Chuyển đến màn hình chính"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Xem các ứng dụng gần đây"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Xong"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Quay lại"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Dùng 3 ngón tay vuốt sang trái hoặc sang phải trên bàn di chuột"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Tuyệt vời!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Bạn đã thực hiện xong cử chỉ quay lại."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Chuyển đến màn hình chính"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Dùng 3 ngón tay vuốt lên trên bàn di chuột"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Tuyệt vời!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Bạn đã thực hiện xong cử chỉ chuyển đến màn hình chính"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Xem các ứng dụng gần đây"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Dùng 3 ngón tay vuốt lên và giữ trên bàn di chuột"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tuyệt vời!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Bạn đã hoàn tất cử chỉ xem ứng dụng gần đây."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Xem tất cả các ứng dụng"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Nhấn phím hành động trên bàn phím"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Rất tốt!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Bạn đã hoàn tất cử chỉ xem tất cả các ứng dụng"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 60e6c4f..cf80402 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音频"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音频"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"进入音频分享设置"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"此设备上的音乐和视频会同时通过两副耳机播放"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"分享您的音频"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"“<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>”和“<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>”"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"切换到“<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>”"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"已开启"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"已开启 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"已关闭"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"未设置"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在设置中管理"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{未启用任何模式}=1{已启用“{mode}”模式}other{已启用 # 个模式}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"您将不会受到声音和振动的打扰(闹钟、提醒、活动和所指定来电者的相关提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"该功能无法使用,因为铃声被静音"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"“勿扰”模式已开启,因此无法调整音量"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"“勿扰”模式已开启,因此无法调整音量"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"不可用,因为已开启<xliff:g id="MODE">%s</xliff:g>"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"不可用"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。点按即可取消静音。"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。点按即可设为振动,但可能会同时将无障碍服务设为静音。"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"了解触控板手势"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用键盘和触控板进行导航"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"了解触控板手势、键盘快捷键等"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"前往主屏幕"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近用过的应用"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在触控板上用三根手指向左或向右滑动"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"太棒了!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"您完成了“返回”手势教程。"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"前往主屏幕"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在触控板上用三根手指向上滑动"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太棒了!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"您已完成“前往主屏幕”手势"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近用过的应用"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"在触控板上用三根手指向上滑动并按住"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"您已完成“查看最近用过的应用”的手势教程。"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有应用"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常棒!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index a838da4..39cf982 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"輸入音訊分享功能設定"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"兩對耳機都會播放此裝置的音樂和影片"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"分享音訊"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」和「<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>」"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"切換至「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"開 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"關閉"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"未設定"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{沒有啟用模式}=1{已啟用{mode}}other{已啟用 # 個模式}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"你不會受到聲音和震動騷擾 (鬧鐘、提醒、活動和你指定的來電者鈴聲除外)。當你選擇播放音樂、影片和遊戲等,仍可以聽到該內容的聲音。"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設定為靜音,因此無法使用"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"「請勿騷擾」已開啟,因此無法使用"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"「請勿騷擾」已開啟,因此無法使用"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"<xliff:g id="MODE">%s</xliff:g>已開啟,因此無法使用"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"未有提供"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕按即可取消靜音。"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕按即可設為震動。無障礙功能服務可能已經設為靜音。"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"瞭解觸控板手勢"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用鍵盤和觸控板導覽"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"瞭解觸控板手勢、鍵盤快速鍵等等"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在觸控板上用三隻手指向左或向右滑動"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"很好!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"你已完成「返回」手勢的教學課程。"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"返回主畫面"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在觸控板上用三隻手指向上滑動"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太好了!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"你已完成「返回主畫面」手勢的教學課程"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近使用的應用程式"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"在觸控板上用三隻手指向上滑動並按住"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"做得好!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢的教學課程。"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"做得好!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢的教學課程"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 158b68a..cbde8c3 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"進入音訊分享設定"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"這部裝置會同時在兩對耳機上播放音樂和影片"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"分享音訊"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」和「<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>」"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"切換至「<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>」"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"開啟"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"已開啟 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"關閉"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"未設定"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{未啟用任何模式}=1{已啟用 {mode} 個模式}other{已啟用 # 個模式}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"裝置不會發出音效或震動造成干擾,但是會保留與鬧鐘、提醒、活動和指定來電者有關的設定。如果你選擇播放音樂、影片和遊戲等內容,還是可以聽見相關音訊。"</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設為靜音,因此無法使用"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"「零打擾」模式已開啟,因此無法調整音量"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"「零打擾」模式已開啟,因此無法調整音量"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"「<xliff:g id="MODE">%s</xliff:g>」功能已開啟,因此無法調整音量"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"無法使用"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕觸即可取消靜音。"</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕觸即可設為震動,但系統可能會將無障礙服務一併設為靜音。"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string>
@@ -1406,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"學習觸控板手勢"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用鍵盤和觸控板操作"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"學習觸控板手勢、鍵盤快速鍵等"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在觸控板上用三指向左或向右滑動"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"很好!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"你已完成「返回」手勢的教學課程。"</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"返回主畫面"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在觸控板上用三指向上滑動"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太棒了!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"你已完成「返回主畫面」手勢教學課程"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近使用的應用程式"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"在觸控板上用三指向上滑動並按住"</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢教學課程。"</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按下鍵盤上的快捷操作鍵"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常好!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"你已完成「查看所有應用程式」手勢教學課程"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a351b1b..2e668c5 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -312,6 +312,10 @@
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Yabelana ngomsindo"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Yabelana ngomsindo"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"faka amasethingi okwabelana ngokuqoshiwe"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Umculo namavidiyo ale divayisi azodlala kukho kokubili ukubhangqwa kwamaheadphone"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Yabelana ngomsindo wakho"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"I-<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> ne-<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Shintshela ku-<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -440,8 +444,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Vuliwe"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Vuliwe • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Valiwe"</string>
-    <!-- no translation found for zen_mode_set_up (8231201163894922821) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="8231201163894922821">"Akusethiwe"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Phatha kumasethingi"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Awekho amamodi asebenzayo}=1{I-{mode} iyasebenza}one{Amamodi angu-# ayasebenza}other{Amamodi angu-# ayasebenza}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ngeke uphazanyiswe imisindo nokudlidliza, ngaphandle kusukela kuma-alamu, izikhumbuzi, imicimbi, nabafonayo obacacisayo. Usazozwa noma yini okhetha ukuyidlala okufaka umculo, amavidiyo, namageyimu."</string>
@@ -673,6 +676,8 @@
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ayitholakali ngoba ukukhala kuthulisiwe"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ayitholakali ngoba okuthi Ungaphazamisi kuvuliwe"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Ayitholakali ngoba okuthi Ungaphazamisi kuvuliwe"</string>
+    <string name="stream_unavailable_by_modes" msgid="3674139029490353683">"Ayitholakali ngoba i-<xliff:g id="MODE">%s</xliff:g> ivuliwe"</string>
+    <string name="stream_unavailable_by_unknown" msgid="6908434629318171588">"Ayitholakali"</string>
     <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Thepha ukuze ususe ukuthula."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Thepha ukuze usethe ukudlidliza. Amasevisi okufinyelela angathuliswa."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string>
@@ -706,8 +711,7 @@
     <string name="show_demo_mode" msgid="3677956462273059726">"Bonisa imodi yedemo"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"I-Ethernet"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"I-alamu"</string>
-    <!-- no translation found for active_mode_content_description (1627555562186515927) -->
-    <skip />
+    <string name="active_mode_content_description" msgid="1627555562186515927">"I-<xliff:g id="MODENAME">%1$s</xliff:g> ivuliwe"</string>
     <string name="wallet_title" msgid="5369767670735827105">"I-wallet"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Lungela ukuthenga ngokushesha, ngokuphepha ngefoni yakho"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Bonisa konke"</string>
@@ -1407,38 +1411,26 @@
     <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Funda ukunyakaza kwephedi lokuthinta"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Funa usebenzisa ikhibhodi yakho nephedi yokuthinta"</string>
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Funda ukunyakaza kwephedi yokuthinta, izinqamuleli zamakhibhodi, nokuningi"</string>
-    <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) -->
-    <skip />
-    <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) -->
-    <skip />
+    <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Iya emuva"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Iya ekhasini lokuqala"</string>
     <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Buka ama-app akamuva"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kwenziwe"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Buyela emuva"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) -->
-    <skip />
+    <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu kuphedi yokuthinta"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Kuhle!"</string>
     <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ukuqedile ukuthinta kokubuyela emuva."</string>
     <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Iya ekhasini lokuqala"</string>
-    <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) -->
-    <skip />
-    <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) -->
-    <skip />
+    <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swayiphela phezulu ngeminwe emithathu ephedini yakho yokuthinta"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Umsebenzi omuhle!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ukuqedile ukunyakaza kokuya ekhaya"</string>
     <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Buka ama-app akamuva"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swayiphela phezulu bese ubamba usebenzisa iminwe emithathu ephedini yokuthinta."</string>
     <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Umsebenzi omuhle!"</string>
     <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Uqedele ukubuka ukuthinta kwama-app akamuva."</string>
-    <!-- no translation found for tutorial_action_key_title (8172535792469008169) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) -->
-    <skip />
-    <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) -->
-    <skip />
+    <string name="tutorial_action_key_title" msgid="8172535792469008169">"Buka wonke ama-app"</string>
+    <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
+    <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Wenze kahle!"</string>
+    <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Uqedele ukunyakazisa kokubuka onke ama-app."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 38ef0e9..f96a0b9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -70,9 +70,21 @@
     <!-- The number of rows in the QuickSettings -->
     <integer name="quick_settings_max_rows">4</integer>
 
+    <!-- The number of rows in the paginated grid QuickSettings -->
+    <integer name="quick_settings_paginated_grid_num_rows">4</integer>
+
+    <!-- The number of rows in the paginated grid QuickQuickSettings -->
+    <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
     <!-- The number of columns in the infinite grid QuickSettings -->
     <integer name="quick_settings_infinite_grid_num_columns">4</integer>
 
+    <!-- The number of columns in the Dual Shade QuickSettings -->
+    <integer name="quick_settings_dual_shade_num_columns">4</integer>
+
+    <!-- The number of columns in the Split Shade QuickSettings -->
+    <integer name="quick_settings_split_shade_num_columns">4</integer>
+
     <!-- Override column number for quick settings.
     For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
     TODO (b/293252410) - change this comment/resource when flag is enabled -->
@@ -125,17 +137,19 @@
     <!-- Use collapsed layout for media player in landscape QQS -->
     <bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool>
 
-    <!-- For hearing devices related tool list. Need to be in ComponentName format (package/class).
-         Should be activity to be launched.
-         Already contains tool that holds intent: "com.android.settings.action.live_caption".
-         Maximum number is 3. -->
+    <!-- Hearing devices related tool list. Each entry must be in ComponentName format
+         (package/class), indicating the specific application component to launch.
+         Already contains tool that handles intent: "com.android.settings.action.live_caption" by
+         default. You can add up to 2 additional related tools. -->
     <string-array name="config_quickSettingsHearingDevicesRelatedToolName" translatable="false">
     </string-array>
 
-    <!-- The drawable resource names. If provided, it will replace the corresponding icons in
-         config_quickSettingsHearingDevicesRelatedToolName. Can be empty to use original icons.
-         Already contains tool that holds intent: "com.android.settings.action.live_caption".
-         Maximum number is 3. -->
+    <!-- Hearing devices related tool icon list. Provide drawable resource names in the same order
+         as the component names in config_quickSettingsHearingDevicesRelatedToolName. The icons
+         should be monochrome and will be tinted according to the system's material color. Ensure
+         the number of icon resources matches the number of components specified in
+         config_quickSettingsHearingDevicesRelatedToolName. If this array is empty or the sizes
+         don't match, the original application icons will be used. -->
     <string-array name="config_quickSettingsHearingDevicesRelatedToolIcon" translatable="false">
     </string-array>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1727a5f..209a971 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -591,7 +591,7 @@
 
     <dimen name="volume_dialog_panel_width_half">28dp</dimen>
 
-    <dimen name="volume_dialog_slider_width">42dp</dimen>
+    <dimen name="volume_dialog_slider_width_legacy">42dp</dimen>
 
     <dimen name="volume_dialog_slider_corner_radius">21dp</dimen>
 
@@ -622,10 +622,6 @@
 
     <dimen name="volume_tool_tip_arrow_corner_radius">2dp</dimen>
 
-    <!-- Volume panel slices dimensions -->
-    <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
-    <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
-
     <dimen name="bottom_sheet_corner_radius">28dp</dimen>
 
     <!-- Size of each item in the ringer selector drawer. -->
@@ -1802,8 +1798,6 @@
     <dimen name="hearing_devices_preset_spinner_text_padding_vertical">15dp</dimen>
     <dimen name="hearing_devices_preset_spinner_arrow_size">24dp</dimen>
     <dimen name="hearing_devices_preset_spinner_background_radius">28dp</dimen>
-    <dimen name="hearing_devices_tool_icon_frame_width">94dp</dimen>
-    <dimen name="hearing_devices_tool_icon_frame_height">64dp</dimen>
     <dimen name="hearing_devices_tool_icon_size">28dp</dimen>
 
     <!-- Height percentage of the parent container occupied by the communal view -->
@@ -2050,4 +2044,22 @@
 
     <dimen name="contextual_edu_dialog_bottom_margin">80dp</dimen>
     <dimen name="contextual_edu_dialog_elevation">2dp</dimen>
+
+    <!-- Volume start -->
+    <dimen name="volume_dialog_background_corner_radius">30dp</dimen>
+    <dimen name="volume_dialog_width">60dp</dimen>
+    <dimen name="volume_dialog_vertical_padding">6dp</dimen>
+    <dimen name="volume_dialog_components_spacing">8dp</dimen>
+    <dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen>
+    <dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen>
+    <dimen name="volume_dialog_floating_sliders_horizontal_padding">4dp</dimen>
+    <dimen name="volume_dialog_spacing">4dp</dimen>
+    <dimen name="volume_dialog_button_size">48dp</dimen>
+    <dimen name="volume_dialog_floating_sliders_bottom_padding">48dp</dimen>
+    <dimen name="volume_dialog_slider_width">52dp</dimen>
+    <dimen name="volume_dialog_slider_height">254dp</dimen>
+
+    <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
+    <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
+    <!-- Volume end -->
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 96a85d7..2c5fb56 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -765,6 +765,14 @@
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing audio</string>
     <!-- QuickSettings: Bluetooth dialog audio sharing button text accessibility label. Used as part of the string "Double tap to enter audio sharing settings". [CHAR LIMIT=50]-->
     <string name="quick_settings_bluetooth_audio_sharing_button_accessibility">enter audio sharing settings</string>
+    <!-- QuickSettings: Bluetooth audio sharing dialog message. [CHAR LIMIT=NONE]-->
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_message">This device\'s music and videos will play on both pairs of headphones</string>
+    <!-- QuickSettings: Bluetooth audio sharing dialog title. [CHAR LIMIT=NONE]-->
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_title">Share your audio</string>
+    <!-- QuickSettings: Bluetooth audio sharing dialog subtitle. [CHAR LIMIT=NONE]-->
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle"><xliff:g id="available_device_name" example="device 1">%1$s</xliff:g> and <xliff:g id="active_device_name" example="device 2">%2$s</xliff:g></string>
+    <!-- QuickSettings: Bluetooth audio sharing dialog button text. [CHAR LIMIT=NONE]-->
+    <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button">Switch to <xliff:g id="available_device_name" example="device 1">%1$s</xliff:g></string>
 
     <!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
     <string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
@@ -1746,6 +1754,11 @@
     <!-- A message shown when the media volume changing is disabled because of the don't disturb mode [CHAR_LIMIT=50]-->
     <string name="stream_media_unavailable">Unavailable because Do Not Disturb is on</string>
 
+    <!-- A message shown when a specific volume (e.g. Alarms, Media, etc) is disabled because an active mode is muting that audio stream altogether [CHAR_LIMIT=50]-->
+    <string name="stream_unavailable_by_modes">Unavailable because <xliff:g id="mode" example="Bedtime">%s</xliff:g> is on</string>
+    <!-- A message shown when a specific volume (e.g. Alarms, Media, etc) is disabled but we don't know which mode (or anything else) is responsible. [CHAR_LIMIT=50]-->
+    <string name="stream_unavailable_by_unknown">Unavailable</string>
+
     <!-- Shown in the header of quick settings to indicate to the user that their phone ringer is on vibrate. [CHAR_LIMIT=NONE] -->
     <!-- Shown in the header of quick settings to indicate to the user that their phone ringer is on silent (muted). [CHAR_LIMIT=NONE] -->
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e1f25f9..7cebac2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -70,6 +70,20 @@
         <item name="android:fontWeight">700</item>
     </style>
 
+    <style name="StatusBar" />
+    <style name="StatusBar.Chip" />
+
+    <style name="StatusBar.Chip.Text">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:gravity">center|start</item>
+        <item name="android:paddingStart">@dimen/ongoing_activity_chip_icon_text_padding</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.Material.Small</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">?android:attr/colorPrimary</item>
+    </style>
+
     <style name="Chipbar" />
 
     <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
@@ -677,10 +691,12 @@
 
     <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:windowActionBar">false</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
         <item name="preferenceTheme">@style/TunerPreferenceTheme</item>
     </style>
 
     <style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
     <style name="TextAppearance.NotificationInfo.Confirmation">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 1e668b8..9a264c6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -28,6 +28,8 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
@@ -62,7 +64,7 @@
     public PluginManagerImpl(Context context,
             PluginActionManager.Factory actionManagerFactory,
             boolean debuggable,
-            UncaughtExceptionPreHandlerManager preHandlerManager,
+            @Nullable UncaughtExceptionPreHandlerManager preHandlerManager,
             PluginEnabler pluginEnabler,
             PluginPrefs pluginPrefs,
             List<String> privilegedPlugins) {
@@ -73,7 +75,9 @@
         mPluginPrefs = pluginPrefs;
         mPluginEnabler = pluginEnabler;
 
-        preHandlerManager.registerHandler(new PluginExceptionHandler());
+        if (preHandlerManager != null) {
+            preHandlerManager.registerHandler(new PluginExceptionHandler());
+        }
     }
 
     public boolean isDebuggable() {
diff --git a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
index b792db3..306d682 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
@@ -17,6 +17,7 @@
 package com.android.keyguard
 
 import android.view.MotionEvent
+import android.view.View
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.res.R
@@ -34,11 +35,10 @@
 @SysUISingleton
 class EmptyLockIconViewController
 @Inject
-constructor(
-    private val keyguardRootView: Lazy<KeyguardRootView>,
-) : LockIconViewController {
+constructor(private val keyguardRootView: Lazy<KeyguardRootView>) : LockIconViewController {
     private val deviceEntryIconViewId = R.id.device_entry_icon_view
-    override fun setLockIconView(lockIconView: LockIconView) {
+
+    override fun setLockIconView(lockIconView: View) {
         // no-op
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 4a96e9e..c7ea980 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -193,12 +193,16 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         if (!MigrateClocksToBlueprint.isEnabled()) {
-            mSmallClockFrame = findViewById(R.id.lockscreen_clock_view);
-            mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large);
+            mSmallClockFrame = findViewById(
+                    com.android.systemui.customization.R.id.lockscreen_clock_view);
+            mLargeClockFrame = findViewById(
+                    com.android.systemui.customization.R.id.lockscreen_clock_view_large);
             mStatusArea = findViewById(R.id.keyguard_status_area);
         } else {
-            removeView(findViewById(R.id.lockscreen_clock_view));
-            removeView(findViewById(R.id.lockscreen_clock_view_large));
+            removeView(findViewById(
+                    com.android.systemui.customization.R.id.lockscreen_clock_view));
+            removeView(findViewById(
+                    com.android.systemui.customization.R.id.lockscreen_clock_view_large));
         }
         onConfigChanged();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index d468f2f..ec0f582 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,8 +22,6 @@
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.Flags.smartspaceRelocateToBottom;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.annotation.Nullable;
 import android.database.ContentObserver;
@@ -36,13 +34,11 @@
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
@@ -71,7 +67,6 @@
 import java.io.PrintWriter;
 import java.util.Locale;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
@@ -114,8 +109,6 @@
 
     private boolean mShownOnSecondaryDisplay = false;
     private boolean mOnlyClock = false;
-    private boolean mIsActiveDreamLockscreenHosted = false;
-    private final FeatureFlagsClassic mFeatureFlags;
     private KeyguardInteractor mKeyguardInteractor;
     private KeyguardClockInteractor mKeyguardClockInteractor;
     private final DelayableExecutor mUiExecutor;
@@ -124,15 +117,6 @@
     private DisposableHandle mAodIconsBindHandle;
     @Nullable private NotificationIconContainer mAodIconContainer;
 
-    @VisibleForTesting
-    final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
-            (Boolean isLockscreenHosted) -> {
-                if (mIsActiveDreamLockscreenHosted == isLockscreenHosted) {
-                    return;
-                }
-                mIsActiveDreamLockscreenHosted = isLockscreenHosted;
-                updateKeyguardStatusAreaVisibility();
-            };
     private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean change) {
@@ -173,7 +157,6 @@
             @KeyguardClockLog LogBuffer logBuffer,
             KeyguardInteractor keyguardInteractor,
             KeyguardClockInteractor keyguardClockInteractor,
-            FeatureFlagsClassic featureFlags,
             InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager) {
         super(keyguardClockSwitch);
         mStatusBarStateController = statusBarStateController;
@@ -189,7 +172,6 @@
         mClockEventController = clockEventController;
         mLogBuffer = logBuffer;
         mView.setLogBuffer(mLogBuffer);
-        mFeatureFlags = featureFlags;
         mKeyguardInteractor = keyguardInteractor;
         mKeyguardClockInteractor = keyguardClockInteractor;
         mInWindowLauncherUnlockAnimationManager = inWindowLauncherUnlockAnimationManager;
@@ -241,20 +223,16 @@
         mKeyguardSliceViewController.init();
 
         if (!MigrateClocksToBlueprint.isEnabled()) {
-            mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
-            mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
+            mSmallClockFrame = mView
+                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view);
+            mLargeClockFrame = mView
+                .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large);
         }
 
         if (!mOnlyClock) {
             mDumpManager.unregisterDumpable(getClass().getSimpleName()); // unregister previous
             mDumpManager.registerDumpable(getClass().getSimpleName(), this);
         }
-
-        if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
-            mStatusArea = mView.findViewById(R.id.keyguard_status_area);
-            collectFlow(mStatusArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
-                    mIsActiveDreamLockscreenHostedCallback);
-        }
     }
 
     public KeyguardClockSwitch getView() {
@@ -666,15 +644,6 @@
         }
     }
 
-    private void updateKeyguardStatusAreaVisibility() {
-        if (mStatusArea != null) {
-            mUiExecutor.execute(() -> {
-                mStatusArea.setVisibility(
-                        mIsActiveDreamLockscreenHosted ? View.INVISIBLE : View.VISIBLE);
-            });
-        }
-    }
-
     /**
      * Sets the clipChildren property on relevant views, to allow the smartspace to draw out of
      * bounds during the unlock transition.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 63a4af9..0684824 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -534,7 +534,8 @@
         // KeyguardClockViewBinder
         if (customClockAnimation && !MigrateClocksToBlueprint.isEnabled()) {
             // Find the clock, so we can exclude it from this transition.
-            FrameLayout clockContainerView = mView.findViewById(R.id.lockscreen_clock_view_large);
+            FrameLayout clockContainerView = mView.findViewById(
+                    com.android.systemui.customization.R.id.lockscreen_clock_view_large);
 
             // The clock container can sometimes be null. If it is, just fall back to the
             // old animation rather than setting up the custom animations.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 19d918f..5a02486 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.view.View
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -98,12 +99,12 @@
             viewsIdToTranslate =
                 setOf(
                     ViewIdToTranslate(
-                        viewId = R.id.lockscreen_clock_view_large,
+                        viewId = customR.id.lockscreen_clock_view_large,
                         direction = START,
                         shouldBeAnimated = filterKeyguardAndSplitShadeOnly
                     ),
                     ViewIdToTranslate(
-                        viewId = R.id.lockscreen_clock_view,
+                        viewId = customR.id.lockscreen_clock_view,
                         direction = START,
                         shouldBeAnimated = filterKeyguard
                     ),
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8e01e04..83ab524 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -315,6 +315,7 @@
     };
     private final FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
 
+    private final Object mSimDataLockObject = new Object();
     HashMap<Integer, SimData> mSimDatas = new HashMap<>();
     HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
 
@@ -610,14 +611,26 @@
 
         // It is possible for active subscriptions to become invalid (-1), and these will not be
         // present in the subscriptionInfo list
-        Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
-        while (iter.hasNext()) {
-            Map.Entry<Integer, SimData> simData = iter.next();
-            if (!activeSubIds.contains(simData.getKey())) {
-                mSimLogger.logInvalidSubId(simData.getKey());
-                iter.remove();
+        synchronized (mSimDataLockObject) {
+            Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry<Integer, SimData> simData = iter.next();
+                if (!activeSubIds.contains(simData.getKey())) {
+                    mSimLogger.logInvalidSubId(simData.getKey());
+                    iter.remove();
 
-                SimData data = simData.getValue();
+                    SimData data = simData.getValue();
+                    for (int j = 0; j < mCallbacks.size(); j++) {
+                        KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
+                        if (cb != null) {
+                            cb.onSimStateChanged(data.subId, data.slotId, data.simState);
+                        }
+                    }
+                }
+            }
+
+            for (int i = 0; i < changedSubscriptions.size(); i++) {
+                SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
                 for (int j = 0; j < mCallbacks.size(); j++) {
                     KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
                     if (cb != null) {
@@ -625,18 +638,8 @@
                     }
                 }
             }
+            callbacksRefreshCarrierInfo();
         }
-
-        for (int i = 0; i < changedSubscriptions.size(); i++) {
-            SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
-            for (int j = 0; j < mCallbacks.size(); j++) {
-                KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
-                if (cb != null) {
-                    cb.onSimStateChanged(data.subId, data.slotId, data.simState);
-                }
-            }
-        }
-        callbacksRefreshCarrierInfo();
     }
 
     private void handleAirplaneModeChanged() {
@@ -1872,7 +1875,9 @@
                     if (posture == DEVICE_POSTURE_OPENED) {
                         mLogger.d("Posture changed to open - attempting to request active"
                                 + " unlock and run face auth");
-                        getFaceAuthInteractor().onDeviceUnfolded();
+                        if (getFaceAuthInteractor() != null) {
+                            getFaceAuthInteractor().onDeviceUnfolded();
+                        }
                         requestActiveUnlockFromWakeReason(PowerManager.WAKE_REASON_UNFOLD_DEVICE,
                                 false);
                     }
@@ -3376,12 +3381,15 @@
      * Removes all valid subscription info from the map for the given slotId.
      */
     private void invalidateSlot(int slotId) {
-        Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
-        while (iter.hasNext()) {
-            SimData data = iter.next().getValue();
-            if (data.slotId == slotId && SubscriptionManager.isValidSubscriptionId(data.subId)) {
-                mSimLogger.logInvalidSubId(data.subId);
-                iter.remove();
+        synchronized (mSimDataLockObject) {
+            Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator();
+            while (iter.hasNext()) {
+                SimData data = iter.next().getValue();
+                if (data.slotId == slotId
+                        && SubscriptionManager.isValidSubscriptionId(data.subId)) {
+                    mSimLogger.logInvalidSubId(data.subId);
+                    iter.remove();
+                }
             }
         }
     }
@@ -3408,23 +3416,25 @@
         }
 
         // TODO(b/327476182): Preserve SIM_STATE_CARD_IO_ERROR sims in a separate data source.
-        SimData data = mSimDatas.get(subId);
-        final boolean changed;
-        if (data == null) {
-            data = new SimData(state, slotId, subId);
-            mSimDatas.put(subId, data);
-            changed = true; // no data yet; force update
-        } else {
-            changed = (data.simState != state || data.subId != subId || data.slotId != slotId);
-            data.simState = state;
-            data.subId = subId;
-            data.slotId = slotId;
-        }
-        if ((changed || becameAbsent)) {
-            for (int i = 0; i < mCallbacks.size(); i++) {
-                KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-                if (cb != null) {
-                    cb.onSimStateChanged(subId, slotId, state);
+        synchronized (mSimDataLockObject) {
+            SimData data = mSimDatas.get(subId);
+            final boolean changed;
+            if (data == null) {
+                data = new SimData(state, slotId, subId);
+                mSimDatas.put(subId, data);
+                changed = true; // no data yet; force update
+            } else {
+                changed = (data.simState != state || data.subId != subId || data.slotId != slotId);
+                data.simState = state;
+                data.subId = subId;
+                data.slotId = slotId;
+            }
+            if ((changed || becameAbsent)) {
+                for (int i = 0; i < mCallbacks.size(); i++) {
+                    KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+                    if (cb != null) {
+                        cb.onSimStateChanged(subId, slotId, state);
+                    }
                 }
             }
         }
@@ -3684,9 +3694,11 @@
         callback.onKeyguardVisibilityChanged(isKeyguardVisible());
         callback.onTelephonyCapable(mTelephonyCapable);
 
-        for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
-            final SimData state = data.getValue();
-            callback.onSimStateChanged(state.subId, state.slotId, state.simState);
+        synchronized (mSimDataLockObject) {
+            for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
+                final SimData state = data.getValue();
+                callback.onSimStateChanged(state.subId, state.slotId, state.simState);
+            }
         }
     }
 
@@ -3823,19 +3835,23 @@
     }
 
     public int getSimState(int subId) {
-        if (mSimDatas.containsKey(subId)) {
-            return mSimDatas.get(subId).simState;
-        } else {
-            return TelephonyManager.SIM_STATE_UNKNOWN;
+        synchronized (mSimDataLockObject) {
+            if (mSimDatas.containsKey(subId)) {
+                return mSimDatas.get(subId).simState;
+            } else {
+                return TelephonyManager.SIM_STATE_UNKNOWN;
+            }
         }
     }
 
     private int getSlotId(int subId) {
-        if (!mSimDatas.containsKey(subId)) {
-            refreshSimState(subId, SubscriptionManager.getSlotIndex(subId));
+        synchronized (mSimDataLockObject) {
+            if (!mSimDatas.containsKey(subId)) {
+                refreshSimState(subId, SubscriptionManager.getSlotIndex(subId));
+            }
+            SimData simData = mSimDatas.get(subId);
+            return simData != null ? simData.slotId : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         }
-        SimData simData = mSimDatas.get(subId);
-        return simData != null ? simData.slotId : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     }
 
     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@@ -3876,22 +3892,24 @@
      */
     private boolean refreshSimState(int subId, int slotId) {
         int state = mTelephonyManager.getSimState(slotId);
-        SimData data = mSimDatas.get(subId);
+        synchronized (mSimDataLockObject) {
+            SimData data = mSimDatas.get(subId);
 
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            invalidateSlot(slotId);
-        }
+            if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                invalidateSlot(slotId);
+            }
 
-        final boolean changed;
-        if (data == null) {
-            data = new SimData(state, slotId, subId);
-            mSimDatas.put(subId, data);
-            changed = true; // no data yet; force update
-        } else {
-            changed = data.simState != state;
-            data.simState = state;
+            final boolean changed;
+            if (data == null) {
+                data = new SimData(state, slotId, subId);
+                mSimDatas.put(subId, data);
+                changed = true; // no data yet; force update
+            } else {
+                changed = data.simState != state;
+                data.simState = state;
+            }
+            return changed;
         }
-        return changed;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
deleted file mode 100644
index 03b13fe..0000000
--- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.VibrationAttributes;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import dagger.Lazy;
-
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen.
- *
- * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock
- * icon will show a set distance from the bottom of the device.
- */
-@SysUISingleton
-public class LegacyLockIconViewController implements Dumpable, LockIconViewController {
-    private static final String TAG = "LockIconViewController";
-    private static final float sDefaultDensity =
-            (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
-    private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
-    private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
-            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
-
-    private static final long FADE_OUT_DURATION_MS = 250L;
-
-    private final long mLongPressTimeout;
-    @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @NonNull private final KeyguardViewController mKeyguardViewController;
-    @NonNull private final StatusBarStateController mStatusBarStateController;
-    @NonNull private final KeyguardStateController mKeyguardStateController;
-    @NonNull private final FalsingManager mFalsingManager;
-    @NonNull private final AuthController mAuthController;
-    @NonNull private final AccessibilityManager mAccessibilityManager;
-    @NonNull private final ConfigurationController mConfigurationController;
-    @NonNull private final DelayableExecutor mExecutor;
-    private boolean mUdfpsEnrolled;
-    private Resources mResources;
-    private Context mContext;
-    @NonNull private CharSequence mUnlockedLabel;
-    @NonNull private CharSequence mLockedLabel;
-    @NonNull private final VibratorHelper mVibrator;
-    @Nullable private final AuthRippleController mAuthRippleController;
-    @NonNull private final FeatureFlags mFeatureFlags;
-    @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-    @NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
-    @NonNull private final KeyguardInteractor mKeyguardInteractor;
-    @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
-    @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
-
-    // Tracks the velocity of a touch to help filter out the touches that move too fast.
-    private VelocityTracker mVelocityTracker;
-    // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
-    private int mActivePointerId = -1;
-
-    private boolean mIsDozing;
-    private boolean mIsActiveDreamLockscreenHosted;
-    private boolean mIsBouncerShowing;
-    private boolean mRunningFPS;
-    private boolean mCanDismissLockScreen;
-    private int mStatusBarState;
-    private boolean mIsKeyguardShowing;
-    private Runnable mLongPressCancelRunnable;
-
-    private boolean mUdfpsSupported;
-    private float mHeightPixels;
-    private float mWidthPixels;
-    private int mBottomPaddingPx;
-    private int mDefaultPaddingPx;
-
-    private boolean mShowUnlockIcon;
-    private boolean mShowLockIcon;
-
-    // for udfps when strong auth is required or unlocked on AOD
-    private boolean mShowAodLockIcon;
-    private boolean mShowAodUnlockedIcon;
-    private final int mMaxBurnInOffsetX;
-    private final int mMaxBurnInOffsetY;
-    private float mInterpolatedDarkAmount;
-
-    private boolean mDownDetected;
-    private final Rect mSensorTouchLocation = new Rect();
-    private LockIconView mView;
-
-    @VisibleForTesting
-    final Consumer<Float> mDozeTransitionCallback = (Float value) -> {
-        mInterpolatedDarkAmount = value;
-        mView.setDozeAmount(value);
-        updateBurnInOffsets();
-    };
-
-    @VisibleForTesting
-    final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> {
-        mIsDozing = isDozing;
-        updateBurnInOffsets();
-        updateVisibility();
-    };
-
-    @VisibleForTesting
-    final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
-            (Boolean isLockscreenHosted) -> {
-                mIsActiveDreamLockscreenHosted = isLockscreenHosted;
-                updateVisibility();
-            };
-
-    @Inject
-    public LegacyLockIconViewController(
-            @NonNull StatusBarStateController statusBarStateController,
-            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
-            @NonNull KeyguardViewController keyguardViewController,
-            @NonNull KeyguardStateController keyguardStateController,
-            @NonNull FalsingManager falsingManager,
-            @NonNull AuthController authController,
-            @NonNull DumpManager dumpManager,
-            @NonNull AccessibilityManager accessibilityManager,
-            @NonNull ConfigurationController configurationController,
-            @NonNull @Main DelayableExecutor executor,
-            @NonNull VibratorHelper vibrator,
-            @Nullable AuthRippleController authRippleController,
-            @NonNull @Main Resources resources,
-            @NonNull KeyguardTransitionInteractor transitionInteractor,
-            @NonNull KeyguardInteractor keyguardInteractor,
-            @NonNull FeatureFlags featureFlags,
-            PrimaryBouncerInteractor primaryBouncerInteractor,
-            Context context,
-            Lazy<DeviceEntryInteractor> deviceEntryInteractor
-    ) {
-        mStatusBarStateController = statusBarStateController;
-        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mAuthController = authController;
-        mKeyguardViewController = keyguardViewController;
-        mKeyguardStateController = keyguardStateController;
-        mFalsingManager = falsingManager;
-        mAccessibilityManager = accessibilityManager;
-        mConfigurationController = configurationController;
-        mExecutor = executor;
-        mVibrator = vibrator;
-        mAuthRippleController = authRippleController;
-        mTransitionInteractor = transitionInteractor;
-        mKeyguardInteractor = keyguardInteractor;
-        mFeatureFlags = featureFlags;
-        mPrimaryBouncerInteractor = primaryBouncerInteractor;
-
-        mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
-        mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
-        mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
-        mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
-        mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
-        dumpManager.registerDumpable(TAG, this);
-        mResources = resources;
-        mContext = context;
-        mDeviceEntryInteractor = deviceEntryInteractor;
-
-        mAccessibilityDelegate = new View.AccessibilityDelegate() {
-            private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
-                    new AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfoCompat.ACTION_CLICK,
-                            mResources.getString(R.string.accessibility_authenticate_hint));
-            private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
-                    new AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfoCompat.ACTION_CLICK,
-                            mResources.getString(R.string.accessibility_enter_hint));
-            public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(v, info);
-                if (isActionable()) {
-                    if (mShowLockIcon) {
-                        info.addAction(mAccessibilityAuthenticateHint);
-                    } else if (mShowUnlockIcon) {
-                        info.addAction(mAccessibilityEnterHint);
-                    }
-                }
-            }
-        };
-    }
-
-    /** Sets the LockIconView to the controller and rebinds any that depend on it. */
-    @SuppressLint("ClickableViewAccessibility")
-    @Override
-    public void setLockIconView(LockIconView lockIconView) {
-        mView = lockIconView;
-        mView.setAccessibilityDelegate(mAccessibilityDelegate);
-
-        if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
-            collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD),
-                    mDozeTransitionCallback);
-            collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback);
-        }
-
-        if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
-            collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
-                    mIsActiveDreamLockscreenHostedCallback);
-        }
-
-        updateIsUdfpsEnrolled();
-        updateConfiguration();
-        updateKeyguardShowing();
-
-        mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-        mIsDozing = mStatusBarStateController.isDozing();
-        mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount();
-        mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
-        mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-        mStatusBarState = mStatusBarStateController.getState();
-
-        updateColors();
-        mDownDetected = false;
-        updateBurnInOffsets();
-        updateVisibility();
-
-        updateAccessibility();
-
-        lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
-            @Override
-            public void onViewAttachedToWindow(View view) {
-                registerCallbacks();
-            }
-
-            @Override
-            public void onViewDetachedFromWindow(View view) {
-                unregisterCallbacks();
-            }
-        });
-
-        if (lockIconView.isAttachedToWindow()) {
-            registerCallbacks();
-        }
-
-        lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
-    }
-
-    private void registerCallbacks() {
-        mConfigurationController.addCallback(mConfigurationListener);
-        mAuthController.addCallback(mAuthControllerCallback);
-        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
-        mStatusBarStateController.addCallback(mStatusBarStateListener);
-        mKeyguardStateController.addCallback(mKeyguardStateCallback);
-        mAccessibilityManager.addAccessibilityStateChangeListener(
-                mAccessibilityStateChangeListener);
-
-    }
-
-    private void unregisterCallbacks() {
-        mAuthController.removeCallback(mAuthControllerCallback);
-        mConfigurationController.removeCallback(mConfigurationListener);
-        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
-        mStatusBarStateController.removeCallback(mStatusBarStateListener);
-        mKeyguardStateController.removeCallback(mKeyguardStateCallback);
-        mAccessibilityManager.removeAccessibilityStateChangeListener(
-                mAccessibilityStateChangeListener);
-
-    }
-
-    private void updateAccessibility() {
-        if (mAccessibilityManager.isEnabled()) {
-            mView.setOnClickListener(mA11yClickListener);
-        } else {
-            mView.setOnClickListener(null);
-        }
-    }
-
-    @Override
-    public float getTop() {
-        return mView.getLocationTop();
-    }
-
-    @Override
-    public float getBottom() {
-        return mView.getLocationBottom();
-    }
-
-    private void updateVisibility() {
-        if (!mIsKeyguardShowing && !mIsDozing) {
-            mView.setVisibility(View.INVISIBLE);
-            return;
-        }
-
-        if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) {
-            mView.setVisibility(View.INVISIBLE);
-            return;
-        }
-
-        boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
-                && !mShowAodUnlockedIcon && !mShowAodLockIcon;
-        mShowLockIcon = !mCanDismissLockScreen && isLockScreen()
-                && (!mUdfpsEnrolled || !mRunningFPS);
-        mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
-        mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
-        mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
-
-        final CharSequence prevContentDescription = mView.getContentDescription();
-        if (mShowLockIcon) {
-            if (wasShowingFpIcon) {
-                // fp icon was shown by UdfpsView, and now we still want to animate the transition
-                // in this drawable
-                mView.updateIcon(ICON_FINGERPRINT, false);
-            }
-            mView.updateIcon(ICON_LOCK, false);
-            mView.setContentDescription(mLockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else if (mShowUnlockIcon) {
-            if (wasShowingFpIcon) {
-                // fp icon was shown by UdfpsView, and now we still want to animate the transition
-                // in this drawable
-                mView.updateIcon(ICON_FINGERPRINT, false);
-            }
-            mView.updateIcon(ICON_UNLOCK, false);
-            mView.setContentDescription(mUnlockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else if (mShowAodUnlockedIcon) {
-            mView.updateIcon(ICON_UNLOCK, true);
-            mView.setContentDescription(mUnlockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else if (mShowAodLockIcon) {
-            mView.updateIcon(ICON_LOCK, true);
-            mView.setContentDescription(mLockedLabel);
-            mView.setVisibility(View.VISIBLE);
-        } else {
-            mView.clearIcon();
-            mView.setVisibility(View.INVISIBLE);
-            mView.setContentDescription(null);
-        }
-
-        boolean accessibilityEnabled =
-                !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser();
-        mView.setImportantForAccessibility(
-                accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
-                        : View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-
-        if (!Objects.equals(prevContentDescription, mView.getContentDescription())
-                && mView.getContentDescription() != null && accessibilityEnabled) {
-            mView.announceForAccessibility(mView.getContentDescription());
-        }
-    }
-
-    private boolean isLockScreen() {
-        return !mIsDozing
-                && !mIsBouncerShowing
-                && mStatusBarState == StatusBarState.KEYGUARD;
-    }
-
-    private void updateKeyguardShowing() {
-        mIsKeyguardShowing = mKeyguardStateController.isShowing()
-                && !mKeyguardStateController.isKeyguardGoingAway();
-    }
-
-    private void updateColors() {
-        mView.updateColorAndBackgroundVisibility();
-    }
-
-    private void updateConfiguration() {
-        WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
-        mWidthPixels = bounds.right;
-        if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
-            // Assumed to be initially neglected as there are no left or right insets in portrait
-            // However, on landscape, these insets need to included when calculating the midpoint
-            WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
-            mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
-        }
-        mHeightPixels = bounds.bottom;
-        mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
-        mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding);
-        mUnlockedLabel = mResources.getString(
-                R.string.accessibility_unlock_button);
-        mLockedLabel = mResources.getString(R.string.accessibility_lock_icon);
-        updateLockIconLocation();
-    }
-
-    private void updateLockIconLocation() {
-        final float scaleFactor = mAuthController.getScaleFactor();
-        final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
-        if (KeyguardBottomAreaRefactor.isEnabled() || MigrateClocksToBlueprint.isEnabled()) {
-            // positioning in this case is handled by [DefaultDeviceEntrySection]
-            mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
-                    scaledPadding);
-        } else {
-            if (mUdfpsSupported) {
-                mView.setCenterLocation(mAuthController.getUdfpsLocation(),
-                        mAuthController.getUdfpsRadius(), scaledPadding);
-            } else {
-                mView.setCenterLocation(
-                        new Point((int) mWidthPixels / 2,
-                                (int) (mHeightPixels
-                                        - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
-                        sLockIconRadiusPx * scaleFactor, scaledPadding);
-            }
-        }
-    }
-
-    @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("mUdfpsSupported: " + mUdfpsSupported);
-        pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
-        pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
-        pw.println();
-        pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
-        pw.println(" mShowLockIcon: " + mShowLockIcon);
-        pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
-        pw.println();
-        pw.println(" mIsDozing: " + mIsDozing);
-        pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
-                + mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
-        pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
-        pw.println(" mRunningFPS: " + mRunningFPS);
-        pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
-        pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
-        pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
-        pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
-        pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx);
-        pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted);
-
-        if (mView != null) {
-            mView.dump(pw, args);
-        }
-    }
-
-    /** Every minute, update the aod icon's burn in offset */
-    @Override
-    public void dozeTimeTick() {
-        updateBurnInOffsets();
-    }
-
-    private void updateBurnInOffsets() {
-        float offsetX = MathUtils.lerp(0f,
-                getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
-                        - mMaxBurnInOffsetX, mInterpolatedDarkAmount);
-        float offsetY = MathUtils.lerp(0f,
-                getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
-                        - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
-
-        mView.setTranslationX(offsetX);
-        mView.setTranslationY(offsetY);
-    }
-
-    private void updateIsUdfpsEnrolled() {
-        boolean wasUdfpsSupported = mUdfpsSupported;
-        boolean wasUdfpsEnrolled = mUdfpsEnrolled;
-
-        mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
-        mView.setUseBackground(mUdfpsSupported);
-
-        mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
-        if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
-            updateVisibility();
-        }
-    }
-
-    private StatusBarStateController.StateListener mStatusBarStateListener =
-            new StatusBarStateController.StateListener() {
-                @Override
-                public void onDozeAmountChanged(float linear, float eased) {
-                    if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
-                        mInterpolatedDarkAmount = eased;
-                        mView.setDozeAmount(eased);
-                        updateBurnInOffsets();
-                    }
-                }
-
-                @Override
-                public void onDozingChanged(boolean isDozing) {
-                    if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
-                        mIsDozing = isDozing;
-                        updateBurnInOffsets();
-                        updateVisibility();
-                    }
-                }
-
-                @Override
-                public void onStateChanged(int statusBarState) {
-                    mStatusBarState = statusBarState;
-                    updateVisibility();
-                }
-            };
-
-    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onKeyguardBouncerStateChanged(boolean bouncer) {
-                    mIsBouncerShowing = bouncer;
-                    updateVisibility();
-                }
-
-                @Override
-                public void onBiometricRunningStateChanged(boolean running,
-                        BiometricSourceType biometricSourceType) {
-                    final boolean wasRunningFps = mRunningFPS;
-
-                    if (biometricSourceType == FINGERPRINT) {
-                        mRunningFPS = running;
-                    }
-
-                    if (wasRunningFps != mRunningFPS) {
-                        updateVisibility();
-                    }
-                }
-            };
-
-    private final KeyguardStateController.Callback mKeyguardStateCallback =
-            new KeyguardStateController.Callback() {
-        @Override
-        public void onUnlockedChanged() {
-            mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-            updateKeyguardShowing();
-            updateVisibility();
-        }
-
-        @Override
-        public void onKeyguardShowingChanged() {
-            // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe).
-            // If biometrics were removed, local vars mCanDismissLockScreen and
-            // mUserUnlockedWithBiometric may not be updated.
-            mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-
-            // reset mIsBouncerShowing state in case it was preemptively set
-            // onLongPress
-            mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-
-            updateKeyguardShowing();
-            updateVisibility();
-        }
-
-        @Override
-        public void onKeyguardFadingAwayChanged() {
-            updateKeyguardShowing();
-            updateVisibility();
-        }
-    };
-
-    private final ConfigurationController.ConfigurationListener mConfigurationListener =
-            new ConfigurationController.ConfigurationListener() {
-        @Override
-        public void onUiModeChanged() {
-            updateColors();
-        }
-
-        @Override
-        public void onThemeChanged() {
-            updateColors();
-        }
-
-        @Override
-        public void onConfigChanged(Configuration newConfig) {
-            updateConfiguration();
-            updateColors();
-        }
-    };
-
-    /**
-     * Handles the touch if {@link #isActionable()} is true.
-     * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
-     * area for {@link #mLongPressTimeout} ms.
-     *
-     * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
-     */
-    private boolean onTouchEvent(MotionEvent event) {
-        if (!actionableDownEventStartedOnView(event)) {
-            cancelTouches();
-            return false;
-        }
-
-        switch(event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_HOVER_ENTER:
-                if (!mDownDetected && mAccessibilityManager.isTouchExplorationEnabled()) {
-                    vibrateOnTouchExploration();
-                }
-
-                // The pointer that causes ACTION_DOWN is always at index 0.
-                // We need to persist its ID to track it during ACTION_MOVE that could include
-                // data for many other pointers because of multi-touch support.
-                mActivePointerId = event.getPointerId(0);
-                if (mVelocityTracker == null) {
-                    // To simplify the lifecycle of the velocity tracker, make sure it's never null
-                    // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
-                    mVelocityTracker = VelocityTracker.obtain();
-                } else {
-                    // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
-                    // ACTION_DOWN, in that case we should just reuse the old instance.
-                    mVelocityTracker.clear();
-                }
-                mVelocityTracker.addMovement(event);
-
-                mDownDetected = true;
-                mLongPressCancelRunnable = mExecutor.executeDelayed(
-                        this::onLongPress, mLongPressTimeout);
-                break;
-            case MotionEvent.ACTION_MOVE:
-            case MotionEvent.ACTION_HOVER_MOVE:
-                mVelocityTracker.addMovement(event);
-                // Compute pointer velocity in pixels per second.
-                mVelocityTracker.computeCurrentVelocity(1000);
-                float velocity = computePointerSpeed(mVelocityTracker,
-                        mActivePointerId);
-                if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
-                        && exceedsVelocityThreshold(velocity)) {
-                    Log.v(TAG, "lock icon long-press rescheduled due to "
-                            + "high pointer velocity=" + velocity);
-                    mLongPressCancelRunnable.run();
-                    mLongPressCancelRunnable = mExecutor.executeDelayed(
-                            this::onLongPress, mLongPressTimeout);
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_HOVER_EXIT:
-                cancelTouches();
-                break;
-        }
-
-        return true;
-    }
-
-    /**
-     * Calculate the pointer speed given a velocity tracker and the pointer id.
-     * This assumes that the velocity tracker has already been passed all relevant motion events.
-     */
-    private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
-        final float vx = tracker.getXVelocity(pointerId);
-        final float vy = tracker.getYVelocity(pointerId);
-        return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
-    }
-
-    /**
-     * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
-     */
-    private static boolean exceedsVelocityThreshold(float velocity) {
-        return velocity > 750f;
-    }
-
-    private boolean actionableDownEventStartedOnView(MotionEvent event) {
-        if (!isActionable()) {
-            return false;
-        }
-
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            return true;
-        }
-
-        return mDownDetected;
-    }
-
-    @ExperimentalCoroutinesApi
-    @VisibleForTesting
-    protected void onLongPress() {
-        cancelTouches();
-        if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
-            Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
-            return;
-        }
-
-        // pre-emptively set to true to hide view
-        mIsBouncerShowing = true;
-        if (!DeviceEntryUdfpsRefactor.isEnabled()
-                && mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
-            mAuthRippleController.showUnlockRipple(FINGERPRINT);
-        }
-        updateVisibility();
-
-        // play device entry haptic (consistent with UDFPS controller longpress)
-        vibrateOnLongPress();
-
-        if (SceneContainerFlag.isEnabled()) {
-            mDeviceEntryInteractor.get().attemptDeviceEntry();
-        } else {
-            mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
-        }
-    }
-
-
-    private void cancelTouches() {
-        mDownDetected = false;
-        if (mLongPressCancelRunnable != null) {
-            mLongPressCancelRunnable.run();
-        }
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    private boolean isActionable() {
-        if (mIsBouncerShowing) {
-            Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
-            // a long press gestures from AOD may have already triggered the bouncer to show,
-            // so this touch is no longer actionable
-            return false;
-        }
-        return mUdfpsSupported || mShowUnlockIcon;
-    }
-
-    /**
-     * Set the alpha of this view.
-     */
-    @Override
-    public void setAlpha(float alpha) {
-        mView.setAlpha(alpha);
-    }
-
-    private void updateUdfpsConfig() {
-        // must be called from the main thread since it may update the views
-        mExecutor.execute(() -> {
-            updateIsUdfpsEnrolled();
-            updateConfiguration();
-        });
-    }
-
-    @VisibleForTesting
-    void vibrateOnTouchExploration() {
-        mVibrator.performHapticFeedback(
-                mView,
-                HapticFeedbackConstants.CONTEXT_CLICK
-        );
-    }
-
-    @VisibleForTesting
-    void vibrateOnLongPress() {
-        mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS);
-    }
-
-    private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
-        @Override
-        public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
-            if (modality == TYPE_FINGERPRINT) {
-                updateUdfpsConfig();
-            }
-        }
-
-        @Override
-        public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
-            if (modality == TYPE_FINGERPRINT) {
-                updateUdfpsConfig();
-            }
-        }
-
-        @Override
-        public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {
-            updateUdfpsConfig();
-        }
-    };
-
-    /**
-     * Whether the lock icon will handle a touch while dozing.
-     */
-    @Override
-    public boolean willHandleTouchWhileDozing(MotionEvent event) {
-        // is in lock icon area
-        mView.getHitRect(mSensorTouchLocation);
-        final boolean inLockIconArea =
-                mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
-                        && mView.getVisibility() == View.VISIBLE;
-
-        return inLockIconArea && actionableDownEventStartedOnView(event);
-    }
-
-    private final View.OnClickListener mA11yClickListener = v -> onLongPress();
-
-    private final AccessibilityManager.AccessibilityStateChangeListener
-            mAccessibilityStateChangeListener = enabled -> updateAccessibility();
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
deleted file mode 100644
index ff6a3d0..0000000
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-
-import com.android.internal.graphics.ColorUtils;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
-
-import java.io.PrintWriter;
-
-/**
- * A view positioned under the notification shade.
- */
-public class LockIconView extends FrameLayout implements Dumpable {
-    @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK})
-    public @interface IconType {}
-
-    public static final int ICON_NONE = -1;
-    public static final int ICON_LOCK = 0;
-    public static final int ICON_FINGERPRINT = 1;
-    public static final int ICON_UNLOCK = 2;
-
-    private @IconType int mIconType;
-    private boolean mAod;
-
-    @NonNull private final RectF mSensorRect;
-    @NonNull private Point mLockIconCenter = new Point(0, 0);
-    private float mRadius;
-    private int mLockIconPadding;
-
-    private ImageView mLockIcon;
-    private ImageView mBgView;
-
-    private int mLockIconColor;
-    private boolean mUseBackground = false;
-    private float mDozeAmount = 0f;
-
-    @SuppressLint("ClickableViewAccessibility")
-    public LockIconView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mSensorRect = new RectF();
-
-        addBgImageView(context, attrs);
-        addLockIconImageView(context, attrs);
-    }
-
-    void setDozeAmount(float dozeAmount) {
-        mDozeAmount = dozeAmount;
-        updateColorAndBackgroundVisibility();
-    }
-
-    void updateColorAndBackgroundVisibility() {
-        if (mUseBackground && mLockIcon.getDrawable() != null) {
-            mLockIconColor = ColorUtils.blendARGB(
-                    Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
-                    Color.WHITE,
-                    mDozeAmount);
-            int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
-                    com.android.internal.R.attr.colorSurface);
-            mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor));
-            mBgView.setAlpha(1f - mDozeAmount);
-            mBgView.setVisibility(View.VISIBLE);
-        } else {
-            mLockIconColor = ColorUtils.blendARGB(
-                    Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
-                    Color.WHITE,
-                    mDozeAmount);
-            mBgView.setVisibility(View.GONE);
-        }
-
-        mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
-    }
-
-    /**
-     * Whether or not to render the lock icon background. Mainly used for UDPFS.
-     */
-    public void setUseBackground(boolean useBackground) {
-        mUseBackground = useBackground;
-        updateColorAndBackgroundVisibility();
-    }
-
-    /**
-     * Set the location of the lock icon.
-     */
-    public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
-        mLockIconCenter = center;
-        mRadius = radius;
-        mLockIconPadding = drawablePadding;
-
-        mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding,
-                mLockIconPadding);
-
-        mSensorRect.set(mLockIconCenter.x - mRadius,
-                mLockIconCenter.y - mRadius,
-                mLockIconCenter.x + mRadius,
-                mLockIconCenter.y + mRadius);
-
-        final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-        if (lp != null) {
-            lp.width = (int) (mSensorRect.right - mSensorRect.left);
-            lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
-            lp.topMargin = (int) mSensorRect.top;
-            lp.setMarginStart((int) mSensorRect.left);
-            setLayoutParams(lp);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    float getLocationTop() {
-        Rect r = new Rect();
-        mLockIcon.getGlobalVisibleRect(r);
-        return r.top;
-    }
-
-    float getLocationBottom() {
-        Rect r = new Rect();
-        mLockIcon.getGlobalVisibleRect(r);
-        return r.bottom;
-
-    }
-
-    /**
-     * Updates the icon its default state where no visual is shown.
-     */
-    public void clearIcon() {
-        updateIcon(ICON_NONE, false);
-    }
-
-    /**
-     * Transition the current icon to a new state
-     * @param icon type (ie: lock icon, unlock icon, fingerprint icon)
-     * @param aod whether to use the aod icon variant (some icons don't have aod variants and will
-     *            therefore show no icon)
-     */
-    public void updateIcon(@IconType int icon, boolean aod) {
-        mIconType = icon;
-        mAod = aod;
-
-        mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
-    }
-
-    public ImageView getLockIcon() {
-        return mLockIcon;
-    }
-
-    private void addLockIconImageView(Context context, AttributeSet attrs) {
-        mLockIcon = new ImageView(context, attrs);
-        mLockIcon.setId(R.id.lock_icon);
-        mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
-        mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon));
-        addView(mLockIcon);
-        LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
-        lp.height = MATCH_PARENT;
-        lp.width = MATCH_PARENT;
-        lp.gravity = Gravity.CENTER;
-        mLockIcon.setLayoutParams(lp);
-    }
-
-    private void addBgImageView(Context context, AttributeSet attrs) {
-        mBgView = new ImageView(context, attrs);
-        mBgView.setId(R.id.lock_icon_bg);
-        mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg));
-        mBgView.setVisibility(View.INVISIBLE);
-        addView(mBgView);
-        LayoutParams lp = (LayoutParams) mBgView.getLayoutParams();
-        lp.height = MATCH_PARENT;
-        lp.width = MATCH_PARENT;
-        mBgView.setLayoutParams(lp);
-    }
-
-    private static int[] getLockIconState(@IconType int icon, boolean aod) {
-        if (icon == ICON_NONE) {
-            return new int[0];
-        }
-
-        int[] lockIconState = new int[2];
-        switch (icon) {
-            case ICON_LOCK:
-                lockIconState[0] = android.R.attr.state_first;
-                break;
-            case ICON_FINGERPRINT:
-                lockIconState[0] = android.R.attr.state_middle;
-                break;
-            case ICON_UNLOCK:
-                lockIconState[0] = android.R.attr.state_last;
-                break;
-        }
-
-        if (aod) {
-            lockIconState[1] = android.R.attr.state_single;
-        } else {
-            lockIconState[1] = -android.R.attr.state_single;
-        }
-
-        return lockIconState;
-    }
-
-    private String typeToString(@IconType int type) {
-        switch (type) {
-            case ICON_NONE:
-                return "none";
-            case ICON_LOCK:
-                return "lock";
-            case ICON_FINGERPRINT:
-                return "fingerprint";
-            case ICON_UNLOCK:
-                return "unlock";
-        }
-
-        return "invalid";
-    }
-
-    @Override
-    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("Lock Icon View Parameters:");
-        pw.println("    Center in px (x, y)= ("
-                + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
-        pw.println("    Radius in pixels: " + mRadius);
-        pw.println("    Drawable padding: " + mLockIconPadding);
-        pw.println("    mIconType=" + typeToString(mIconType));
-        pw.println("    mAod=" + mAod);
-        pw.println("Lock Icon View actual measurements:");
-        pw.println("    topLeft= (" + getX() + ", " + getY() + ")");
-        pw.println("    width=" + getWidth() + " height=" + getHeight());
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
index 10d5a0c..c5012b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
@@ -17,13 +17,19 @@
 package com.android.keyguard
 
 import android.view.MotionEvent
+import android.view.View
 
 /** Controls the [LockIconView]. */
 interface LockIconViewController {
-    fun setLockIconView(lockIconView: LockIconView)
+    fun setLockIconView(lockIconView: View)
+
     fun getTop(): Float
+
     fun getBottom(): Float
+
     fun dozeTimeTick()
+
     fun setAlpha(alpha: Float)
+
     fun willHandleTouchWhileDozing(event: MotionEvent): Boolean
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 831543d..ef172a1 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -69,7 +69,9 @@
                         layoutInflater,
                         resources,
                         featureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION),
-                        MigrateClocksToBlueprint.isEnabled()),
+                        MigrateClocksToBlueprint.isEnabled(),
+                        com.android.systemui.Flags.clockReactiveVariants()
+                ),
                 context.getString(R.string.lockscreen_clock_id_fallback),
                 clockBuffers,
                 /* keepAllLoaded = */ false,
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a301155..40c1f0f9 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -52,7 +52,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.tuner.TunerService;
 
 import dagger.Lazy;
@@ -148,7 +148,7 @@
     @Inject Lazy<SystemUIDialogManager> mSystemUIDialogManagerLazy;
     @Inject Lazy<DialogTransitionAnimator> mDialogTransitionAnimatorLazy;
     @Inject Lazy<UserTracker> mUserTrackerLazy;
-    @Inject Lazy<StatusBarWindowController> mStatusBarWindowControllerLazy;
+    @Inject Lazy<StatusBarWindowControllerStore> mStatusBarWindowControllerStoreLazy;
 
     @Inject
     public Dependency() {
@@ -192,7 +192,8 @@
         mProviders.put(SystemUIDialogManager.class, mSystemUIDialogManagerLazy::get);
         mProviders.put(DialogTransitionAnimator.class, mDialogTransitionAnimatorLazy::get);
         mProviders.put(UserTracker.class, mUserTrackerLazy::get);
-        mProviders.put(StatusBarWindowController.class, mStatusBarWindowControllerLazy::get);
+        mProviders.put(
+                StatusBarWindowControllerStore.class, mStatusBarWindowControllerStoreLazy::get);
 
         Dependency.setInstance(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 275147e..41b9d33 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -226,7 +226,11 @@
             mBtnTargets =
                     mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
             mHandler.post(
-                    () -> handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets));
+                    () -> {
+                        // Force a refresh by destroying the menu if it exists.
+                        destroyFloatingMenu();
+                        handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
+                    });
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 158623f..1978bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -42,6 +42,7 @@
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.Space;
 import android.widget.Spinner;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -52,6 +53,7 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.HapClientProfile;
@@ -428,10 +430,16 @@
         } catch (Resources.NotFoundException e) {
             Log.i(TAG, "No hearing devices related tool config resource");
         }
-        final int listSize = toolItemList.size();
-        for (int i = 0; i < listSize; i++) {
+        for (int i = 0; i < toolItemList.size(); i++) {
             View view = createHearingToolView(context, toolItemList.get(i));
             mRelatedToolsContainer.addView(view);
+            if (i != toolItemList.size() - 1) {
+                final int spaceSize = context.getResources().getDimensionPixelSize(
+                        R.dimen.hearing_devices_layout_margin);
+                Space space = new Space(context);
+                space.setLayoutParams(new LinearLayout.LayoutParams(spaceSize, 0));
+                mRelatedToolsContainer.addView(space);
+            }
         }
     }
 
@@ -492,6 +500,10 @@
         TextView text = view.requireViewById(R.id.tool_name);
         view.setContentDescription(item.getToolName());
         icon.setImageDrawable(item.getToolIcon());
+        if (item.isCustomIcon()) {
+            icon.getDrawable().mutate().setTint(Utils.getColorAttr(context,
+                    com.android.internal.R.attr.materialColorOnPrimaryContainer).getDefaultColor());
+        }
         text.setText(item.getToolName());
         Intent intent = item.getToolIntent();
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -517,7 +529,8 @@
             return new ToolItem(
                     context.getString(R.string.quick_settings_hearing_devices_live_caption_title),
                     context.getDrawable(R.drawable.ic_volume_odi_captions),
-                    LIVE_CAPTION_INTENT);
+                    LIVE_CAPTION_INTENT,
+                    /* isCustomIcon= */ true);
         }
 
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
index 2006726..7e4c1e5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
@@ -41,7 +41,7 @@
     private static final String SPLIT_DELIMITER = "/";
     private static final String RES_TYPE = "drawable";
     @VisibleForTesting
-    static final int MAX_NUM = 3;
+    static final int MAX_NUM = 2;
 
     /**
      * Parses the string arrays to create a list of {@link ToolItem}.
@@ -82,7 +82,8 @@
                     useCustomIcons ? iconList.get(i)
                             : activityInfoList.get(i).loadIcon(packageManager),
                     new Intent(Intent.ACTION_MAIN).setComponent(
-                            activityInfoList.get(i).getComponentName())
+                            activityInfoList.get(i).getComponentName()),
+                    useCustomIcons
             ));
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
index 66bb2b5..ef03f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
@@ -23,4 +23,5 @@
     var toolName: String = "",
     var toolIcon: Drawable,
     var toolIntent: Intent,
+    var isCustomIcon: Boolean,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index cd9efaf..610e3f8a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -39,6 +39,10 @@
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
 import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
@@ -159,6 +163,13 @@
         impl: NightDisplayTileDataInteractor
     ): QSTileAvailabilityInteractor
 
+    @Binds
+    @IntoMap
+    @StringKey(HEARING_DEVICES_TILE_SPEC)
+    fun provideHearingDevicesAvailabilityInteractor(
+        impl: HearingDevicesTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
         const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
         const val COLOR_INVERSION_TILE_SPEC = "inversion"
@@ -191,7 +202,7 @@
             factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>,
             mapper: ColorCorrectionTileMapper,
             stateInteractor: ColorCorrectionTileDataInteractor,
-            userActionInteractor: ColorCorrectionUserActionInteractor
+            userActionInteractor: ColorCorrectionUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(COLOR_CORRECTION_TILE_SPEC),
@@ -223,7 +234,7 @@
             factory: QSTileViewModelFactory.Static<ColorInversionTileModel>,
             mapper: ColorInversionTileMapper,
             stateInteractor: ColorInversionTileDataInteractor,
-            userActionInteractor: ColorInversionUserActionInteractor
+            userActionInteractor: ColorInversionUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(COLOR_INVERSION_TILE_SPEC),
@@ -255,7 +266,7 @@
             factory: QSTileViewModelFactory.Static<FontScalingTileModel>,
             mapper: FontScalingTileMapper,
             stateInteractor: FontScalingTileDataInteractor,
-            userActionInteractor: FontScalingTileUserActionInteractor
+            userActionInteractor: FontScalingTileUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(FONT_SCALING_TILE_SPEC),
@@ -279,21 +290,6 @@
                 category = TileCategory.DISPLAY,
             )
 
-        @Provides
-        @IntoMap
-        @StringKey(HEARING_DEVICES_TILE_SPEC)
-        fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
-            QSTileConfig(
-                tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
-                uiConfig =
-                    QSTileUIConfig.Resource(
-                        iconRes = R.drawable.qs_hearing_devices_icon,
-                        labelRes = R.string.quick_settings_hearing_devices_label,
-                    ),
-                instanceId = uiEventLogger.getNewInstanceId(),
-                category = TileCategory.ACCESSIBILITY,
-            )
-
         /**
          * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
          * behind a flag.
@@ -305,7 +301,7 @@
             factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>,
             mapper: ReduceBrightColorsTileMapper,
             stateInteractor: ReduceBrightColorsTileDataInteractor,
-            userActionInteractor: ReduceBrightColorsTileUserActionInteractor
+            userActionInteractor: ReduceBrightColorsTileUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
@@ -339,7 +335,7 @@
             factory: QSTileViewModelFactory.Static<OneHandedModeTileModel>,
             mapper: OneHandedModeTileMapper,
             stateInteractor: OneHandedModeTileDataInteractor,
-            userActionInteractor: OneHandedModeTileUserActionInteractor
+            userActionInteractor: OneHandedModeTileUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
@@ -376,7 +372,7 @@
             factory: QSTileViewModelFactory.Static<NightDisplayTileModel>,
             mapper: NightDisplayTileMapper,
             stateInteractor: NightDisplayTileDataInteractor,
-            userActionInteractor: NightDisplayTileUserActionInteractor
+            userActionInteractor: NightDisplayTileUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
@@ -386,5 +382,43 @@
                     mapper,
                 )
             else StubQSTileViewModel
+
+        @Provides
+        @IntoMap
+        @StringKey(HEARING_DEVICES_TILE_SPEC)
+        fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+            QSTileConfig(
+                tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+                uiConfig =
+                    QSTileUIConfig.Resource(
+                        iconRes = R.drawable.qs_hearing_devices_icon,
+                        labelRes = R.string.quick_settings_hearing_devices_label,
+                    ),
+                instanceId = uiEventLogger.getNewInstanceId(),
+                category = TileCategory.ACCESSIBILITY,
+            )
+
+        /**
+         * Inject HearingDevices Tile into tileViewModelMap in QSModule. The tile is hidden behind a
+         * flag.
+         */
+        @Provides
+        @IntoMap
+        @StringKey(HEARING_DEVICES_TILE_SPEC)
+        fun provideHearingDevicesTileViewModel(
+            factory: QSTileViewModelFactory.Static<HearingDevicesTileModel>,
+            mapper: HearingDevicesTileMapper,
+            stateInteractor: HearingDevicesTileDataInteractor,
+            userActionInteractor: HearingDevicesTileUserActionInteractor,
+        ): QSTileViewModel {
+            return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
+                factory.create(
+                    TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+                    userActionInteractor,
+                    stateInteractor,
+                    mapper,
+                )
+            } else StubQSTileViewModel
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index e0f73a6..cbdb882 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -319,7 +319,7 @@
             if (item == null && active) {
                 item = new AppOpItem(code, uid, packageName, mClock.elapsedRealtime());
                 if (isOpMicrophone(code)) {
-                    item.setDisabled(isAnyRecordingPausedLocked(uid));
+                    item.setDisabled(isAllRecordingPausedLocked(uid));
                 } else if (isOpCamera(code)) {
                     item.setDisabled(mCameraDisabled);
                 }
@@ -521,18 +521,21 @@
 
     }
 
-    private boolean isAnyRecordingPausedLocked(int uid) {
+    // TODO(b/365843152) remove AudioRecordingConfiguration listening
+    private boolean isAllRecordingPausedLocked(int uid) {
         if (mMicMuted) {
             return true;
         }
         List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid);
         if (configs == null) return false;
+        // If we are aware of AudioRecordConfigs, suppress the indicator if all of them are known
+        // to be silenced.
         int configsNum = configs.size();
         for (int i = 0; i < configsNum; i++) {
             AudioRecordingConfiguration config = configs.get(i);
-            if (config.isClientSilenced()) return true;
+            if (!config.isClientSilenced()) return false;
         }
-        return false;
+        return true;
     }
 
     private void updateSensorDisabledStatus() {
@@ -543,7 +546,7 @@
 
                 boolean paused = false;
                 if (isOpMicrophone(item.getCode())) {
-                    paused = isAnyRecordingPausedLocked(item.getUid());
+                    paused = isAllRecordingPausedLocked(item.getUid());
                 } else if (isOpCamera(item.getCode())) {
                     paused = mCameraDisabled;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index c95a94e..f6cc724 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -37,11 +37,9 @@
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.core.LogLevel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.CircleReveal
@@ -87,7 +85,7 @@
     private val lightRevealScrim: LightRevealScrim,
     private val authRippleInteractor: AuthRippleInteractor,
     private val facePropertyRepository: FacePropertyRepository,
-    rippleView: AuthRippleView?
+    rippleView: AuthRippleView?,
 ) :
     ViewController<AuthRippleView>(rippleView),
     CoreStartable,
@@ -108,15 +106,13 @@
     }
 
     init {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            rippleView?.repeatWhenAttached {
-                repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
-                    authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
-                        if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
-                            showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
-                        } else {
-                            showUnlockRippleInternal(BiometricSourceType.FACE)
-                        }
+        rippleView?.repeatWhenAttached {
+            repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
+                authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
+                    if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
+                        showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
+                    } else {
+                        showUnlockRippleInternal(BiometricSourceType.FACE)
                     }
                 }
             }
@@ -134,29 +130,8 @@
         keyguardStateController.addCallback(this)
         wakefulnessLifecycle.addObserver(this)
         commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
-        if (!DeviceEntryUdfpsRefactor.isEnabled) {
-            biometricUnlockController.addListener(biometricModeListener)
-        }
     }
 
-    private val biometricModeListener =
-        object : BiometricUnlockController.BiometricUnlockEventsListener {
-            override fun onBiometricUnlockedWithKeyguardDismissal(
-                biometricSourceType: BiometricSourceType?
-            ) {
-                DeviceEntryUdfpsRefactor.assertInLegacyMode()
-                if (biometricSourceType != null) {
-                    showUnlockRippleInternal(biometricSourceType)
-                } else {
-                    logger.log(
-                        TAG,
-                        LogLevel.ERROR,
-                        "Unexpected scenario where biometricSourceType is null"
-                    )
-                }
-            }
-        }
-
     @VisibleForTesting
     public override fun onViewDetached() {
         udfpsController?.removeCallback(udfpsControllerCallback)
@@ -166,17 +141,10 @@
         keyguardStateController.removeCallback(this)
         wakefulnessLifecycle.removeObserver(this)
         commandRegistry.unregisterCommand("auth-ripple")
-        biometricUnlockController.removeListener(biometricModeListener)
 
         notificationShadeWindowController.setForcePluginOpen(false, this)
     }
 
-    @Deprecated("Update authRippleInteractor.showUnlockRipple instead of calling this.")
-    fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        showUnlockRippleInternal(biometricSourceType)
-    }
-
     private fun showUnlockRippleInternal(biometricSourceType: BiometricSourceType) {
         val keyguardNotShowing = !keyguardStateController.isShowing
         val unlockNotAllowed =
@@ -197,8 +165,8 @@
                         0,
                         Math.max(
                             Math.max(it.x, displayMetrics.widthPixels - it.x),
-                            Math.max(it.y, displayMetrics.heightPixels - it.y)
-                        )
+                            Math.max(it.y, displayMetrics.heightPixels - it.y),
+                        ),
                     )
                 logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius")
                 showUnlockedRipple()
@@ -213,8 +181,8 @@
                         0,
                         Math.max(
                             Math.max(it.x, displayMetrics.widthPixels - it.x),
-                            Math.max(it.y, displayMetrics.heightPixels - it.y)
-                        )
+                            Math.max(it.y, displayMetrics.heightPixels - it.y),
+                        ),
                     )
                 logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple")
                 showUnlockedRipple()
@@ -322,7 +290,7 @@
             override fun onBiometricAuthenticated(
                 userId: Int,
                 biometricSourceType: BiometricSourceType,
-                isStrongBiometric: Boolean
+                isStrongBiometric: Boolean,
             ) {
                 if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
                     mView.fadeDwellRipple()
@@ -337,7 +305,7 @@
 
             override fun onBiometricAcquired(
                 biometricSourceType: BiometricSourceType,
-                acquireInfo: Int
+                acquireInfo: Int,
             ) {
                 if (
                     biometricSourceType == BiometricSourceType.FINGERPRINT &&
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
deleted file mode 100644
index 242601d..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.util.AttributeSet
-
-/**
- * Class that coordinates non-HBM animations during BiometricPrompt.
- *
- * Currently doesn't draw anything.
- *
- * Note that [AuthBiometricFingerprintViewController] also shows UDFPS animations. At some point we should
- * de-dupe this if necessary.
- */
-class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) {
-
-    // Drawable isn't ever added to the view, so we don't currently show anything
-    private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
-
-    override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
deleted file mode 100644
index e0455b5..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-
-/**
- * Class that coordinates non-HBM animations for biometric prompt.
- */
-class UdfpsBpViewController(
-    view: UdfpsBpView,
-    statusBarStateController: StatusBarStateController,
-    shadeInteractor: ShadeInteractor,
-    systemUIDialogManager: SystemUIDialogManager,
-    dumpManager: DumpManager,
-    udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) : UdfpsAnimationViewController<UdfpsBpView>(
-    view,
-    statusBarStateController,
-    shadeInteractor,
-    systemUIDialogManager,
-    dumpManager,
-    udfpsOverlayInteractor,
-) {
-    override val tag = "UdfpsBpViewController"
-
-    override fun shouldPauseAuth(): Boolean {
-        return false
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a3904ca..2863e29 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -87,7 +87,6 @@
 import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.ScreenLifecycle;
@@ -98,7 +97,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -162,7 +160,6 @@
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
-    @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final SystemClock mSystemClock;
     @NonNull private final UnlockedScreenOffAnimationController
@@ -283,7 +280,6 @@
                         mKeyguardUpdateMonitor,
                         mDialogManager,
                         mDumpManager,
-                        mLockscreenShadeTransitionController,
                         mConfigurationController,
                         mKeyguardStateController,
                         mUnlockedScreenOffAnimationController,
@@ -291,10 +287,9 @@
                         requestId,
                         reason,
                         callback,
-                        (view, event, fromUdfpsView) -> onTouch(
+                        (view, event) -> onTouch(
                             requestId,
-                            event,
-                            fromUdfpsView
+                            event
                         ),
                             mActivityTransitionAnimator,
                         mPrimaryBouncerInteractor,
@@ -374,9 +369,6 @@
                 if (mOverlay == null || mOverlay.isHiding()) {
                     return;
                 }
-                if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-                    ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message);
-                }
             });
         }
 
@@ -391,7 +383,7 @@
          */
         public void debugOnTouch(MotionEvent event) {
             final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
-            UdfpsController.this.onTouch(requestId, event, true);
+            UdfpsController.this.onTouch(requestId, event);
         }
 
         /**
@@ -449,22 +441,10 @@
         if (!mOverlayParams.equals(overlayParams)) {
             mOverlayParams = overlayParams;
 
-            if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
-                    mOverlay.updateOverlayParams(mOverlayParams);
-                } else {
-                    redrawOverlay();
-                }
+            if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
+                mOverlay.updateOverlayParams(mOverlayParams);
             } else {
-                final boolean wasShowingAlternateBouncer =
-                        mAlternateBouncerInteractor.isVisibleState();
-                // When the bounds change it's always to re-create the overlay's window with new
-                // LayoutParams. If the overlay needs to be shown, this will re-create and show the
-                // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
                 redrawOverlay();
-                if (wasShowingAlternateBouncer) {
-                    mKeyguardViewManager.showBouncer(true);
-                }
             }
         }
     }
@@ -563,11 +543,7 @@
         }
     }
 
-    private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
-        if (!fromUdfpsView) {
-            Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
-            return false;
-        }
+    private boolean onTouch(long requestId, @NonNull MotionEvent event) {
         if (mOverlay == null) {
             Log.w(TAG, "ignoring onTouch with null overlay");
             return false;
@@ -591,13 +567,6 @@
             if (!mIsAodInterruptActive) {
                 mOnFingerDown = false;
             }
-        } else if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-            if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
-                    && !mAlternateBouncerInteractor.isVisibleState())
-                    || mPrimaryBouncerInteractor.isInTransit()) {
-                Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor");
-                return false;
-            }
         }
 
         final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
@@ -661,22 +630,13 @@
                         mStatusBarStateController.isDozing());
                 break;
 
-            case UNCHANGED:
-                if (mActivePointerId == MotionEvent.INVALID_POINTER_ID
-                        && mAlternateBouncerInteractor.isVisibleState()) {
-                    // No pointer on sensor, forward to keyguard if alternateBouncer is visible
-                    mKeyguardViewManager.onTouch(event);
-                }
-
             default:
                 break;
         }
         logBiometricTouch(processedTouch.getEvent(), data);
 
         // Always pilfer pointers that are within sensor area or when alternate bouncer is showing
-        if (mActivePointerId != MotionEvent.INVALID_POINTER_ID
-                || (mAlternateBouncerInteractor.isVisibleState()
-                && !DeviceEntryUdfpsRefactor.isEnabled())) {
+        if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) {
             shouldPilfer = true;
         }
 
@@ -692,14 +652,7 @@
     }
 
     private boolean shouldTryToDismissKeyguard() {
-        boolean onKeyguard = false;
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            onKeyguard = mKeyguardStateController.isShowing();
-        } else {
-            onKeyguard = mOverlay != null
-                    && mOverlay.getAnimationViewController()
-                        instanceof UdfpsKeyguardViewControllerLegacy;
-        }
+        boolean onKeyguard = mKeyguardStateController.isShowing();
         return onKeyguard
                 && mKeyguardStateController.canDismissLockScreen()
                 && !mAttemptedToDismissKeyguard;
@@ -719,7 +672,6 @@
             @NonNull FalsingManager falsingManager,
             @NonNull PowerManager powerManager,
             @NonNull AccessibilityManager accessibilityManager,
-            @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
             @NonNull ScreenLifecycle screenLifecycle,
             @NonNull VibratorHelper vibrator,
             @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@@ -769,7 +721,6 @@
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
-        mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         screenLifecycle.addObserver(mScreenObserver);
         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
         mConfigurationController = configurationController;
@@ -849,13 +800,7 @@
 
     @Override
     public void dozeTimeTick() {
-        if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) {
-            DeviceEntryUdfpsRefactor.assertInLegacyMode();
-            final View view = mOverlay.getTouchOverlay();
-            if (view != null) {
-                ((UdfpsView) view).dozeTimeTick();
-            }
-        }
+
     }
 
     private void redrawOverlay() {
@@ -915,17 +860,8 @@
         if (!isOptical()) {
             return;
         }
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            if (mUdfpsDisplayMode != null) {
-                mUdfpsDisplayMode.disable(null);
-            }
-        } else {
-            if (view != null) {
-                UdfpsView udfpsView = (UdfpsView) view;
-                if (udfpsView.isDisplayConfigured()) {
-                    udfpsView.unconfigureDisplay();
-                }
-            }
+        if (mUdfpsDisplayMode != null) {
+            mUdfpsDisplayMode.disable(null);
         }
     }
 
@@ -1118,11 +1054,7 @@
             if (mIgnoreRefreshRate) {
                 dispatchOnUiReady(requestId);
             } else {
-                if (DeviceEntryUdfpsRefactor.isEnabled()) {
                     mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId));
-                } else {
-                    ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId));
-                }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 1bac0bc..a1efc19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -23,8 +23,6 @@
 import android.graphics.Rect
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
 import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
 import android.hardware.biometrics.BiometricRequestConstants.RequestReason
@@ -42,7 +40,6 @@
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import androidx.annotation.LayoutRes
 import androidx.annotation.VisibleForTesting
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -56,7 +53,6 @@
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -64,7 +60,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -102,7 +97,6 @@
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val dialogManager: SystemUIDialogManager,
     private val dumpManager: DumpManager,
-    private val transitionController: LockscreenShadeTransitionController,
     private val configurationController: ConfigurationController,
     private val keyguardStateController: KeyguardStateController,
     private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
@@ -110,7 +104,7 @@
     val requestId: Long,
     @RequestReason val requestReason: Int,
     private val controllerCallback: IUdfpsOverlayControllerCallback,
-    private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+    private val onTouch: (View, MotionEvent) -> Boolean,
     private val activityTransitionAnimator: ActivityTransitionAnimator,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -133,23 +127,15 @@
             .map {} // map to Unit
     private var listenForCurrentKeyguardState: Job? = null
     private var addViewRunnable: Runnable? = null
-    private var overlayViewLegacy: UdfpsView? = null
-        private set
-
     private var overlayTouchView: UdfpsTouchOverlay? = null
 
     /**
-     * Get the current UDFPS overlay touch view which is a different View depending on whether the
-     * DeviceEntryUdfpsRefactor flag is enabled or not.
+     * Get the current UDFPS overlay touch view
      *
      * @return The view, when [isShowing], else null
      */
     fun getTouchOverlay(): View? {
-        return if (DeviceEntryUdfpsRefactor.isEnabled) {
-            overlayTouchView
-        } else {
-            overlayViewLegacy
-        }
+        return overlayTouchView
     }
 
     private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
@@ -161,7 +147,7 @@
         WindowManager.LayoutParams(
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                 0 /* flags set in computeLayoutParams() */,
-                PixelFormat.TRANSLUCENT
+                PixelFormat.TRANSLUCENT,
             )
             .apply {
                 title = TAG
@@ -188,10 +174,6 @@
     val isHiding: Boolean
         get() = getTouchOverlay() == null
 
-    /** The animation controller if the overlay [isShowing]. */
-    val animationViewController: UdfpsAnimationViewController<*>?
-        get() = overlayViewLegacy?.animationViewController
-
     private var touchExplorationEnabled = false
 
     private fun shouldRemoveEnrollmentUi(): Boolean {
@@ -199,7 +181,7 @@
             return Settings.Global.getInt(
                 context.contentResolver,
                 SETTING_REMOVE_ENROLLMENT_UI,
-                0 /* def */
+                0, /* def */
             ) != 0
         }
         return false
@@ -212,63 +194,43 @@
             overlayParams = params
             sensorBounds = Rect(params.sensorBounds)
             try {
-                if (DeviceEntryUdfpsRefactor.isEnabled) {
-                    overlayTouchView =
-                        (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
-                                as UdfpsTouchOverlay)
-                            .apply {
-                                // This view overlaps the sensor area
-                                // prevent it from being selectable during a11y
-                                if (requestReason.isImportantForAccessibility()) {
-                                    importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
-                                }
-
-                                addViewNowOrLater(this, null)
-                                when (requestReason) {
-                                    REASON_AUTH_KEYGUARD ->
-                                        UdfpsTouchOverlayBinder.bind(
-                                            view = this,
-                                            viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
-                                            udfpsOverlayInteractor = udfpsOverlayInteractor,
-                                        )
-                                    else ->
-                                        UdfpsTouchOverlayBinder.bind(
-                                            view = this,
-                                            viewModel = defaultUdfpsTouchOverlayViewModel.get(),
-                                            udfpsOverlayInteractor = udfpsOverlayInteractor,
-                                        )
-                                }
-                            }
-                } else {
-                    overlayViewLegacy =
-                        (inflater.inflate(R.layout.udfps_view, null, false) as UdfpsView).apply {
-                            overlayParams = params
-                            setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
-                            val animation = inflateUdfpsAnimation(this, controller)
-                            if (animation != null) {
-                                animation.init()
-                                animationViewController = animation
-                            }
+                overlayTouchView =
+                    (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
+                            as UdfpsTouchOverlay)
+                        .apply {
                             // This view overlaps the sensor area
                             // prevent it from being selectable during a11y
                             if (requestReason.isImportantForAccessibility()) {
                                 importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
                             }
 
-                            addViewNowOrLater(this, animation)
-                            sensorRect = sensorBounds
+                            addViewNowOrLater(this, null)
+                            when (requestReason) {
+                                REASON_AUTH_KEYGUARD ->
+                                    UdfpsTouchOverlayBinder.bind(
+                                        view = this,
+                                        viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
+                                        udfpsOverlayInteractor = udfpsOverlayInteractor,
+                                    )
+                                else ->
+                                    UdfpsTouchOverlayBinder.bind(
+                                        view = this,
+                                        viewModel = defaultUdfpsTouchOverlayViewModel.get(),
+                                        udfpsOverlayInteractor = udfpsOverlayInteractor,
+                                    )
+                            }
                         }
-                }
+
                 getTouchOverlay()?.apply {
                     touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
                     overlayTouchListener = TouchExplorationStateChangeListener {
                         if (accessibilityManager.isTouchExplorationEnabled) {
-                            setOnHoverListener { v, event -> onTouch(v, event, true) }
+                            setOnHoverListener { v, event -> onTouch(v, event) }
                             setOnTouchListener(null)
                             touchExplorationEnabled = true
                         } else {
                             setOnHoverListener(null)
-                            setOnTouchListener { v, event -> onTouch(v, event, true) }
+                            setOnTouchListener { v, event -> onTouch(v, event) }
                             touchExplorationEnabled = false
                         }
                     }
@@ -312,7 +274,6 @@
     }
 
     fun updateOverlayParams(updatedOverlayParams: UdfpsOverlayParams) {
-        DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
         overlayParams = updatedOverlayParams
         sensorBounds = updatedOverlayParams.sensorBounds
         getTouchOverlay()?.let {
@@ -326,108 +287,11 @@
         }
     }
 
-    fun inflateUdfpsAnimation(
-        view: UdfpsView,
-        controller: UdfpsController
-    ): UdfpsAnimationViewController<*>? {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-
-        val isEnrollment =
-            when (requestReason) {
-                REASON_ENROLL_FIND_SENSOR,
-                REASON_ENROLL_ENROLLING -> true
-                else -> false
-            }
-
-        val filteredRequestReason =
-            if (isEnrollment && shouldRemoveEnrollmentUi()) {
-                REASON_AUTH_OTHER
-            } else {
-                requestReason
-            }
-
-        return when (filteredRequestReason) {
-            REASON_ENROLL_FIND_SENSOR,
-            REASON_ENROLL_ENROLLING -> {
-                // Enroll udfps UI is handled by settings, so use empty view here
-                UdfpsFpmEmptyViewController(
-                    view.addUdfpsView(R.layout.udfps_fpm_empty_view) {
-                        updateAccessibilityViewLocation(sensorBounds)
-                    },
-                    statusBarStateController,
-                    shadeInteractor,
-                    dialogManager,
-                    dumpManager,
-                    udfpsOverlayInteractor,
-                )
-            }
-            REASON_AUTH_KEYGUARD -> {
-                UdfpsKeyguardViewControllerLegacy(
-                    view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
-                        updateSensorLocation(sensorBounds)
-                    },
-                    statusBarStateController,
-                    statusBarKeyguardViewManager,
-                    keyguardUpdateMonitor,
-                    dumpManager,
-                    transitionController,
-                    configurationController,
-                    keyguardStateController,
-                    unlockedScreenOffAnimationController,
-                    dialogManager,
-                    controller,
-                    activityTransitionAnimator,
-                    primaryBouncerInteractor,
-                    alternateBouncerInteractor,
-                    udfpsKeyguardAccessibilityDelegate,
-                    selectedUserInteractor,
-                    transitionInteractor,
-                    shadeInteractor,
-                    udfpsOverlayInteractor,
-                )
-            }
-            REASON_AUTH_BP -> {
-                // note: empty controller, currently shows no visual affordance
-                UdfpsBpViewController(
-                    view.addUdfpsView(R.layout.udfps_bp_view),
-                    statusBarStateController,
-                    shadeInteractor,
-                    dialogManager,
-                    dumpManager,
-                    udfpsOverlayInteractor,
-                )
-            }
-            REASON_AUTH_OTHER,
-            REASON_AUTH_SETTINGS -> {
-                UdfpsFpmEmptyViewController(
-                    view.addUdfpsView(R.layout.udfps_fpm_empty_view),
-                    statusBarStateController,
-                    shadeInteractor,
-                    dialogManager,
-                    dumpManager,
-                    udfpsOverlayInteractor,
-                )
-            }
-            else -> {
-                Log.e(TAG, "Animation for reason $requestReason not supported yet")
-                null
-            }
-        }
-    }
-
     /** Hide the overlay or return false and do nothing if it is already hidden. */
     fun hide(): Boolean {
         val wasShowing = isShowing
 
-        overlayViewLegacy?.apply {
-            if (isDisplayConfigured) {
-                unconfigureDisplay()
-            }
-            animationViewController = null
-        }
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            udfpsDisplayModeProvider.disable(null)
-        }
+        udfpsDisplayModeProvider.disable(null)
         getTouchOverlay()?.apply {
             if (this.parent != null) {
                 windowManager.removeView(this)
@@ -440,7 +304,6 @@
             }
         }
 
-        overlayViewLegacy = null
         overlayTouchView = null
         overlayTouchListener = null
         listenForCurrentKeyguardState?.cancel()
@@ -490,7 +353,7 @@
                         Surface.rotationToString(rot) +
                         " animation=$animation" +
                         " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
-                        " isOccluded=${keyguardStateController.isOccluded}"
+                        " isOccluded=${keyguardStateController.isOccluded}",
                 )
             } else {
                 Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot))
@@ -498,14 +361,14 @@
                     rotatedBounds,
                     overlayParams.naturalDisplayWidth,
                     overlayParams.naturalDisplayHeight,
-                    rot
+                    rot,
                 )
 
                 RotationUtils.rotateBounds(
                     sensorBounds,
                     overlayParams.naturalDisplayWidth,
                     overlayParams.naturalDisplayHeight,
-                    rot
+                    rot,
                 )
             }
         }
@@ -519,14 +382,7 @@
     }
 
     private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
-        val keyguardNotShowing =
-            if (DeviceEntryUdfpsRefactor.isEnabled) {
-                !keyguardStateController.isShowing
-            } else {
-                animation !is UdfpsKeyguardViewControllerLegacy
-            }
-
-        if (keyguardNotShowing) {
+        if (!keyguardStateController.isShowing) {
             // always rotate view if we're not on the keyguard
             return true
         }
@@ -534,16 +390,6 @@
         // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded
         return !(keyguardUpdateMonitor.isGoingToSleep || !keyguardStateController.isOccluded)
     }
-
-    private inline fun <reified T : View> UdfpsView.addUdfpsView(
-        @LayoutRes id: Int,
-        init: T.() -> Unit = {}
-    ): T {
-        val subView = inflater.inflate(id, null) as T
-        addView(subView)
-        subView.init()
-        return subView
-    }
 }
 
 @RequestReason
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
deleted file mode 100644
index 0838855..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.graphics.Rect
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import com.android.systemui.res.R
-
-/**
- * View corresponding with udfps_fpm_empty_view.xml
- *
- * Currently doesn't draw anything.
- */
-class UdfpsFpmEmptyView(
-    context: Context,
-    attrs: AttributeSet?
-) : UdfpsAnimationView(context, attrs) {
-
-    // Drawable isn't ever added to the view, so we don't currently show anything
-    private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
-
-    override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
-
-    fun updateAccessibilityViewLocation(sensorBounds: Rect) {
-        val fingerprintAccessibilityView: View =
-            requireViewById(R.id.udfps_enroll_accessibility_view)
-        val params: ViewGroup.LayoutParams = fingerprintAccessibilityView.layoutParams
-        params.width = sensorBounds.width()
-        params.height = sensorBounds.height()
-        fingerprintAccessibilityView.layoutParams = params
-        fingerprintAccessibilityView.requestLayout()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
deleted file mode 100644
index cfbbc26..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-
-/**
- * Class that coordinates non-HBM animations for non keyguard, or biometric prompt states.
- *
- * Currently doesn't draw anything.
- */
-class UdfpsFpmEmptyViewController(
-    view: UdfpsFpmEmptyView,
-    statusBarStateController: StatusBarStateController,
-    shadeInteractor: ShadeInteractor,
-    systemUIDialogManager: SystemUIDialogManager,
-    dumpManager: DumpManager,
-    udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) : UdfpsAnimationViewController<UdfpsFpmEmptyView>(
-    view,
-    statusBarStateController,
-    shadeInteractor,
-    systemUIDialogManager,
-    dumpManager,
-    udfpsOverlayInteractor,
-) {
-    override val tag = "UdfpsFpmOtherViewController"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
deleted file mode 100644
index c3d9240..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.content.res.Configuration
-import android.util.MathUtils
-import android.view.View
-import androidx.annotation.VisibleForTesting
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
-import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import java.io.PrintWriter
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-
-/** Class that coordinates non-HBM animations during keyguard authentication. */
-@ExperimentalCoroutinesApi
-open class UdfpsKeyguardViewControllerLegacy(
-    private val view: UdfpsKeyguardViewLegacy,
-    statusBarStateController: StatusBarStateController,
-    private val keyguardViewManager: StatusBarKeyguardViewManager,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    dumpManager: DumpManager,
-    private val lockScreenShadeTransitionController: LockscreenShadeTransitionController,
-    private val configurationController: ConfigurationController,
-    private val keyguardStateController: KeyguardStateController,
-    private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
-    systemUIDialogManager: SystemUIDialogManager,
-    private val udfpsController: UdfpsController,
-    private val activityTransitionAnimator: ActivityTransitionAnimator,
-    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
-    private val alternateBouncerInteractor: AlternateBouncerInteractor,
-    private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
-    private val selectedUserInteractor: SelectedUserInteractor,
-    private val transitionInteractor: KeyguardTransitionInteractor,
-    shadeInteractor: ShadeInteractor,
-    udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) :
-    UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
-        view,
-        statusBarStateController,
-        shadeInteractor,
-        systemUIDialogManager,
-        dumpManager,
-        udfpsOverlayInteractor,
-    ) {
-    private val uniqueIdentifier = this.toString()
-    private var showingUdfpsBouncer = false
-    private var udfpsRequested = false
-    private var qsExpansion = 0f
-    private var faceDetectRunning = false
-    private var statusBarState = 0
-    private var transitionToFullShadeProgress = 0f
-    private var lastDozeAmount = 0f
-    private var panelExpansionFraction = 0f
-    private var launchTransitionFadingAway = false
-    private var isLaunchingActivity = false
-    private var activityLaunchProgress = 0f
-    private var inputBouncerExpansion = 0f
-
-    private val stateListener: StatusBarStateController.StateListener =
-        object : StatusBarStateController.StateListener {
-            override fun onStateChanged(statusBarState: Int) {
-                this@UdfpsKeyguardViewControllerLegacy.statusBarState = statusBarState
-                updateAlpha()
-                updatePauseAuth()
-            }
-        }
-
-    private val configurationListener: ConfigurationController.ConfigurationListener =
-        object : ConfigurationController.ConfigurationListener {
-            override fun onUiModeChanged() {
-                view.updateColor()
-            }
-
-            override fun onThemeChanged() {
-                view.updateColor()
-            }
-
-            override fun onConfigChanged(newConfig: Configuration) {
-                updateScaleFactor()
-                view.updatePadding()
-                view.updateColor()
-            }
-        }
-
-    private val keyguardStateControllerCallback: KeyguardStateController.Callback =
-        object : KeyguardStateController.Callback {
-            override fun onUnlockedChanged() {
-                updatePauseAuth()
-            }
-
-            override fun onLaunchTransitionFadingAwayChanged() {
-                launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
-                updatePauseAuth()
-            }
-        }
-
-    private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener =
-        object : ActivityTransitionAnimator.Listener {
-            override fun onTransitionAnimationStart() {
-                isLaunchingActivity = true
-                activityLaunchProgress = 0f
-                updateAlpha()
-            }
-
-            override fun onTransitionAnimationEnd() {
-                isLaunchingActivity = false
-                updateAlpha()
-            }
-
-            override fun onTransitionAnimationProgress(linearProgress: Float) {
-                activityLaunchProgress = linearProgress
-                updateAlpha()
-            }
-        }
-
-    private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback =
-        object : KeyguardViewManagerCallback {
-            override fun onQSExpansionChanged(qsExpansion: Float) {
-                this@UdfpsKeyguardViewControllerLegacy.qsExpansion = qsExpansion
-                updateAlpha()
-                updatePauseAuth()
-            }
-        }
-
-    private val occludingAppBiometricUI: OccludingAppBiometricUI =
-        object : OccludingAppBiometricUI {
-            override fun requestUdfps(request: Boolean, color: Int) {
-                udfpsRequested = request
-                view.requestUdfps(request, color)
-                updateAlpha()
-                updatePauseAuth()
-            }
-
-            override fun dump(pw: PrintWriter) {
-                pw.println(tag)
-            }
-        }
-
-    override val tag: String
-        get() = TAG
-
-    override fun onInit() {
-        super.onInit()
-        keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
-    }
-
-    init {
-        com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        view.repeatWhenAttached {
-            // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
-            // can make the view not visible; and we still want to listen for events
-            // that may make the view visible again.
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                listenForBouncerExpansion(this)
-                listenForAlternateBouncerVisibility(this)
-                listenForOccludedToAodTransition(this)
-                listenForGoneToAodTransition(this)
-                listenForLockscreenAodTransitions(this)
-                listenForAodToOccludedTransitions(this)
-                listenForAlternateBouncerToAodTransitions(this)
-                listenForDreamingToAodTransitions(this)
-                listenForPrimaryBouncerToAodTransitions(this)
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor
-                .transition(
-                    edge = Edge.create(Scenes.Bouncer, AOD),
-                    edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD)
-                )
-                .collect { transitionStep ->
-                    view.onDozeAmountChanged(
-                        transitionStep.value,
-                        transitionStep.value,
-                        ANIMATE_APPEAR_ON_SCREEN_OFF,
-                    )
-                }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep ->
-                view.onDozeAmountChanged(
-                    transitionStep.value,
-                    transitionStep.value,
-                    ANIMATE_APPEAR_ON_SCREEN_OFF,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect {
-                transitionStep ->
-                view.onDozeAmountChanged(
-                    transitionStep.value,
-                    transitionStep.value,
-                    UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep ->
-                view.onDozeAmountChanged(
-                    1f - transitionStep.value,
-                    1f - transitionStep.value,
-                    UdfpsKeyguardViewLegacy.ANIMATION_NONE,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep ->
-                view.onDozeAmountChanged(
-                    transitionStep.value,
-                    transitionStep.value,
-                    ANIMATE_APPEAR_ON_SCREEN_OFF,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor
-                .transition(
-                    edge = Edge.create(Scenes.Gone, AOD),
-                    edgeWithoutSceneContainer = Edge.create(GONE, AOD)
-                )
-                .collect { transitionStep ->
-                    view.onDozeAmountChanged(
-                        transitionStep.value,
-                        transitionStep.value,
-                        ANIMATE_APPEAR_ON_SCREEN_OFF,
-                    )
-                }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job {
-        return scope.launch {
-            transitionInteractor.transitionValue(AOD).collect {
-                view.onDozeAmountChanged(
-                    it,
-                    it,
-                    UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
-                )
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
-        return scope.launch {
-            primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
-                inputBouncerExpansion = bouncerExpansion
-
-                panelExpansionFraction =
-                    if (keyguardViewManager.isPrimaryBouncerInTransit) {
-                        aboutToShowBouncerProgress(1f - bouncerExpansion)
-                    } else {
-                        1f - bouncerExpansion
-                    }
-                updateAlpha()
-                updatePauseAuth()
-            }
-        }
-    }
-
-    @VisibleForTesting
-    suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job {
-        return scope.launch {
-            alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
-                showUdfpsBouncer(isVisible)
-            }
-        }
-    }
-
-    public override fun onViewAttached() {
-        super.onViewAttached()
-        alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier)
-        val dozeAmount = statusBarStateController.dozeAmount
-        lastDozeAmount = dozeAmount
-        stateListener.onDozeAmountChanged(dozeAmount, dozeAmount)
-        statusBarStateController.addCallback(stateListener)
-        udfpsRequested = false
-        launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
-        keyguardStateController.addCallback(keyguardStateControllerCallback)
-        statusBarState = statusBarStateController.state
-        qsExpansion = keyguardViewManager.qsExpansion
-        keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
-        configurationController.addCallback(configurationListener)
-        updateScaleFactor()
-        view.updatePadding()
-        updateAlpha()
-        updatePauseAuth()
-        keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
-        lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this
-        activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener)
-        view.startIconAsyncInflate {
-            val animationViewInternal: View =
-                view.requireViewById(R.id.udfps_animation_view_internal)
-            animationViewInternal.accessibilityDelegate = udfpsKeyguardAccessibilityDelegate
-        }
-    }
-
-    public override fun onViewDetached() {
-        super.onViewDetached()
-        alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier)
-        faceDetectRunning = false
-        keyguardStateController.removeCallback(keyguardStateControllerCallback)
-        statusBarStateController.removeCallback(stateListener)
-        keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI)
-        configurationController.removeCallback(configurationListener)
-        if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) {
-            lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null
-        }
-        activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener)
-        keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<String>) {
-        super.dump(pw, args)
-        pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer")
-        pw.println(
-            "altBouncerInteractor#isAlternateBouncerVisible=" +
-                "${alternateBouncerInteractor.isVisibleState()}"
-        )
-        pw.println(
-            "altBouncerInteractor#canShowAlternateBouncerForFingerprint=" +
-                "${alternateBouncerInteractor.canShowAlternateBouncerForFingerprint()}"
-        )
-        pw.println("faceDetectRunning=$faceDetectRunning")
-        pw.println("statusBarState=" + StatusBarState.toString(statusBarState))
-        pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress")
-        pw.println("qsExpansion=$qsExpansion")
-        pw.println("panelExpansionFraction=$panelExpansionFraction")
-        pw.println("unpausedAlpha=" + view.unpausedAlpha)
-        pw.println("udfpsRequestedByApp=$udfpsRequested")
-        pw.println("launchTransitionFadingAway=$launchTransitionFadingAway")
-        pw.println("lastDozeAmount=$lastDozeAmount")
-        pw.println("inputBouncerExpansion=$inputBouncerExpansion")
-        view.dump(pw)
-    }
-
-    /**
-     * Overrides non-bouncer show logic in shouldPauseAuth to still show icon.
-     *
-     * @return whether the udfpsBouncer has been newly shown or hidden
-     */
-    private fun showUdfpsBouncer(show: Boolean): Boolean {
-        if (showingUdfpsBouncer == show) {
-            return false
-        }
-        val udfpsAffordanceWasNotShowing = shouldPauseAuth()
-        showingUdfpsBouncer = show
-        if (showingUdfpsBouncer) {
-            if (udfpsAffordanceWasNotShowing) {
-                view.animateInUdfpsBouncer(null)
-            }
-            view.announceForAccessibility(
-                view.context.getString(R.string.accessibility_fingerprint_bouncer)
-            )
-        }
-        updateAlpha()
-        updatePauseAuth()
-        return true
-    }
-
-    /**
-     * Returns true if the fingerprint manager is running but we want to temporarily pause
-     * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so
-     * this can be overridden with the showBouncer method.
-     */
-    override fun shouldPauseAuth(): Boolean {
-        if (showingUdfpsBouncer) {
-            return false
-        }
-        if (
-            udfpsRequested &&
-                !notificationShadeVisible &&
-                !isInputBouncerFullyVisible() &&
-                keyguardStateController.isShowing
-        ) {
-            return false
-        }
-        if (launchTransitionFadingAway) {
-            return true
-        }
-
-        // Only pause auth if we're not on the keyguard AND we're not transitioning to doze.
-        // For the UnlockedScreenOffAnimation, the statusBarState is
-        // delayed. However, we still animate in the UDFPS affordance with the
-        // unlockedScreenOffDozeAnimator.
-        if (
-            statusBarState != StatusBarState.KEYGUARD &&
-                !unlockedScreenOffAnimationController.isAnimationPlaying()
-        ) {
-            return true
-        }
-        if (isBouncerExpansionGreaterThan(.5f)) {
-            return true
-        }
-        if (
-            keyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                selectedUserInteractor.getSelectedUserId()
-            )
-        ) {
-            // If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid
-            // overlap with the LockIconView. Shortly afterwards, UDFPS will stop running.
-            return true
-        }
-        return view.unpausedAlpha < 255 * .1
-    }
-
-    fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean {
-        return inputBouncerExpansion >= bouncerExpansionThreshold
-    }
-
-    fun isInputBouncerFullyVisible(): Boolean {
-        return inputBouncerExpansion == 1f
-    }
-
-    override fun listenForTouchesOutsideView(): Boolean {
-        return true
-    }
-
-    /**
-     * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
-     * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down
-     * to expand the notification shade from the empty space in the middle of the lock screen.
-     */
-    fun setTransitionToFullShadeProgress(progress: Float) {
-        transitionToFullShadeProgress = progress
-        updateAlpha()
-    }
-
-    /**
-     * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is
-     * based on the doze amount.
-     */
-    override fun updateAlpha() {
-        // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested,
-        // then the keyguard is occluded by some application - so instead use the input bouncer
-        // hidden amount to determine the fade.
-        val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction
-        var alpha: Int =
-            if (showingUdfpsBouncer) 255
-            else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt()
-        if (!showingUdfpsBouncer) {
-            // swipe from top of the lockscreen to expand full QS:
-            alpha =
-                (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion)))
-                    .toInt()
-
-            // swipe from the middle (empty space) of lockscreen to expand the notification shade:
-            alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt()
-
-            // Fade out the icon if we are animating an activity launch over the lockscreen and the
-            // activity didn't request the UDFPS.
-            if (isLaunchingActivity && !udfpsRequested) {
-                val udfpsActivityLaunchAlphaMultiplier =
-                    1f -
-                        (activityLaunchProgress *
-                                (ActivityTransitionAnimator.TIMINGS.totalDuration / 83))
-                            .coerceIn(0f, 1f)
-                alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt()
-            }
-
-            // Fade out alpha when a dialog is shown
-            // Fade in alpha when a dialog is hidden
-            alpha = (alpha * view.dialogSuggestedAlpha).toInt()
-        }
-        view.unpausedAlpha = alpha
-    }
-
-    private fun getInputBouncerHiddenAmt(): Float {
-        return 1f - inputBouncerExpansion
-    }
-
-    /** Update the scale factor based on the device's resolution. */
-    private fun updateScaleFactor() {
-        udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
-    }
-
-    companion object {
-        const val TAG = "UdfpsKeyguardViewController"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
deleted file mode 100644
index 6d4eea8..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.util.MathUtils;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
-
-import com.android.app.animation.Interpolators;
-import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
-
-import com.airbnb.lottie.LottieAnimationView;
-import com.airbnb.lottie.LottieProperty;
-import com.airbnb.lottie.model.KeyPath;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * View corresponding with udfps_keyguard_view_legacy.xml
- */
-public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView {
-    private UdfpsDrawable mFingerprintDrawable; // placeholder
-    private LottieAnimationView mAodFp;
-    private LottieAnimationView mLockScreenFp;
-
-    // used when highlighting fp icon:
-    private int mTextColorPrimary;
-    private ImageView mBgProtection;
-    boolean mUdfpsRequested;
-
-    private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
-    private int mAlpha; // 0-255
-    private float mScaleFactor = 1;
-    private Rect mSensorBounds = new Rect();
-
-    // AOD anti-burn-in offsets
-    private final int mMaxBurnInOffsetX;
-    private final int mMaxBurnInOffsetY;
-    private float mInterpolatedDarkAmount;
-    private int mAnimationType = ANIMATION_NONE;
-    private boolean mFullyInflated;
-    private Runnable mOnFinishInflateRunnable;
-
-    public UdfpsKeyguardViewLegacy(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        mFingerprintDrawable = new UdfpsFpDrawable(context);
-
-        mMaxBurnInOffsetX = context.getResources()
-            .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
-        mMaxBurnInOffsetY = context.getResources()
-            .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
-    }
-
-    /**
-     * Inflate internal udfps view on a background thread and call the onFinishRunnable
-     * when inflation is finished.
-     */
-    public void startIconAsyncInflate(Runnable onFinishInflate) {
-        mOnFinishInflateRunnable = onFinishInflate;
-        // inflate Lottie views on a background thread in case it takes a while to inflate
-        AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext);
-        inflater.inflate(R.layout.udfps_keyguard_view_internal, this,
-                mLayoutInflaterFinishListener);
-    }
-
-    @Override
-    public UdfpsDrawable getDrawable() {
-        return mFingerprintDrawable;
-    }
-
-    @Override
-    void onSensorRectUpdated(RectF bounds) {
-        super.onSensorRectUpdated(bounds);
-        bounds.round(this.mSensorBounds);
-        postInvalidate();
-    }
-
-    @Override
-    void onDisplayConfiguring() {
-    }
-
-    @Override
-    void onDisplayUnconfigured() {
-    }
-
-    @Override
-    public boolean dozeTimeTick() {
-        updateBurnInOffsets();
-        return true;
-    }
-
-    private void updateBurnInOffsets() {
-        if (!mFullyInflated) {
-            return;
-        }
-
-        // if we're animating from screen off, we can immediately place the icon in the
-        // AoD-burn in location, else we need to translate the icon from LS => AoD.
-        final float darkAmountForAnimation = mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF
-                ? 1f : mInterpolatedDarkAmount;
-        final float burnInOffsetX = MathUtils.lerp(0f,
-            getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
-                - mMaxBurnInOffsetX, darkAmountForAnimation);
-        final float burnInOffsetY = MathUtils.lerp(0f,
-            getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
-                - mMaxBurnInOffsetY, darkAmountForAnimation);
-        final float burnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(),
-                darkAmountForAnimation);
-
-        if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) {
-            mLockScreenFp.setTranslationX(burnInOffsetX);
-            mLockScreenFp.setTranslationY(burnInOffsetY);
-            mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
-            mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount);
-        } else if (darkAmountForAnimation == 0f) {
-            // we're on the lockscreen and should use mAlpha (changes based on shade expansion)
-            mLockScreenFp.setTranslationX(0);
-            mLockScreenFp.setTranslationY(0);
-            mBgProtection.setAlpha(mAlpha / 255f);
-            mLockScreenFp.setAlpha(mAlpha / 255f);
-        } else {
-            mBgProtection.setAlpha(0f);
-            mLockScreenFp.setAlpha(0f);
-        }
-        mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount);
-
-        mAodFp.setTranslationX(burnInOffsetX);
-        mAodFp.setTranslationY(burnInOffsetY);
-        mAodFp.setProgress(burnInProgress);
-        mAodFp.setAlpha(mInterpolatedDarkAmount);
-
-        // done animating
-        final boolean doneAnimatingBetweenAodAndLS =
-                mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
-                        && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f);
-        final boolean doneAnimatingUnlockedScreenOff =
-                mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF
-                        && (mInterpolatedDarkAmount == 1f);
-        if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) {
-            mAnimationType = ANIMATION_NONE;
-        }
-    }
-
-    void requestUdfps(boolean request, int color) {
-        mUdfpsRequested = request;
-    }
-
-    void updateColor() {
-        if (!mFullyInflated) {
-            return;
-        }
-
-        mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
-                com.android.internal.R.attr.materialColorOnSurface);
-        final int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
-                com.android.internal.R.attr.materialColorSurfaceContainerHigh);
-        mBgProtection.setImageTintList(ColorStateList.valueOf(backgroundColor));
-        mLockScreenFp.invalidate(); // updated with a valueCallback
-    }
-
-    void setScaleFactor(float scale) {
-        mScaleFactor = scale;
-    }
-
-    void updatePadding() {
-        if (mLockScreenFp == null || mAodFp == null) {
-            return;
-        }
-
-        final int defaultPaddingPx =
-                getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
-        final int padding = (int) (defaultPaddingPx * mScaleFactor);
-        mLockScreenFp.setPadding(padding, padding, padding, padding);
-        mAodFp.setPadding(padding, padding, padding, padding);
-    }
-
-    /**
-     * @param alpha between 0 and 255
-     */
-    void setUnpausedAlpha(int alpha) {
-        mAlpha = alpha;
-        updateAlpha();
-    }
-
-    /**
-     * @return alpha between 0 and 255
-     */
-    int getUnpausedAlpha() {
-        return mAlpha;
-    }
-
-    @Override
-    protected int updateAlpha() {
-        int alpha = super.updateAlpha();
-        updateBurnInOffsets();
-        return alpha;
-    }
-
-    @Override
-    int calculateAlpha() {
-        if (mPauseAuth) {
-            return 0;
-        }
-        return mAlpha;
-    }
-
-    static final int ANIMATION_NONE = 0;
-    static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1;
-    static final int ANIMATE_APPEAR_ON_SCREEN_OFF = 2;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATE_APPEAR_ON_SCREEN_OFF})
-    private @interface AnimationType {}
-
-    void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) {
-        mAnimationType = animationType;
-        mInterpolatedDarkAmount = eased;
-        updateAlpha();
-    }
-
-    void updateSensorLocation(@NonNull Rect sensorBounds) {
-        mSensorBounds.set(sensorBounds);
-    }
-
-    /**
-     * Animates in the bg protection circle behind the fp icon to highlight the icon.
-     */
-    void animateInUdfpsBouncer(Runnable onEndAnimation) {
-        if (mBackgroundInAnimator.isRunning() || !mFullyInflated) {
-            // already animating in or not yet inflated
-            return;
-        }
-
-        // fade in and scale up
-        mBackgroundInAnimator = new AnimatorSet();
-        mBackgroundInAnimator.playTogether(
-                ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f));
-        mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mBackgroundInAnimator.setDuration(500);
-        mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (onEndAnimation != null) {
-                    onEndAnimation.run();
-                }
-            }
-        });
-        mBackgroundInAnimator.start();
-    }
-
-    /**
-     * Print debugging information for this class.
-     */
-    public void dump(PrintWriter pw) {
-        pw.println("UdfpsKeyguardView (" + this + ")");
-        pw.println("    mPauseAuth=" + mPauseAuth);
-        pw.println("    mUnpausedAlpha=" + getUnpausedAlpha());
-        pw.println("    mUdfpsRequested=" + mUdfpsRequested);
-        pw.println("    mInterpolatedDarkAmount=" + mInterpolatedDarkAmount);
-        pw.println("    mAnimationType=" + mAnimationType);
-    }
-
-    private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener =
-            new AsyncLayoutInflater.OnInflateFinishedListener() {
-                @Override
-                public void onInflateFinished(View view, int resid, ViewGroup parent) {
-                    mFullyInflated = true;
-                    mAodFp = view.findViewById(R.id.udfps_aod_fp);
-                    mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp);
-                    mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg);
-
-                    updatePadding();
-                    updateColor();
-                    updateAlpha();
-
-                    final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                    lp.width = mSensorBounds.width();
-                    lp.height = mSensorBounds.height();
-                    RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds));
-                    lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top,
-                            (int) relativeToView.right, (int) relativeToView.bottom);
-                    parent.addView(view, lp);
-
-                    // requires call to invalidate to update the color
-                    mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER,
-                            frameInfo -> new PorterDuffColorFilter(mTextColorPrimary,
-                                    PorterDuff.Mode.SRC_ATOP));
-                    mOnFinishInflateRunnable.run();
-                }
-            };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
deleted file mode 100644
index 76bcd6e..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.Rect
-import android.graphics.RectF
-import android.util.AttributeSet
-import android.util.Log
-import android.view.MotionEvent
-import android.widget.FrameLayout
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.doze.DozeReceiver
-
-private const val TAG = "UdfpsView"
-
-/**
- * The main view group containing all UDFPS animations.
- */
-class UdfpsView(
-    context: Context,
-    attrs: AttributeSet?
-) : FrameLayout(context, attrs), DozeReceiver {
-    // sensorRect may be bigger than the sensor. True sensor dimensions are defined in
-    // overlayParams.sensorBounds
-    var sensorRect = Rect()
-    private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
-    private val debugTextPaint = Paint().apply {
-        isAntiAlias = true
-        color = Color.BLUE
-        textSize = 32f
-    }
-
-    /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
-    var animationViewController: UdfpsAnimationViewController<*>? = null
-
-    /** Parameters that affect the position and size of the overlay. */
-    var overlayParams = UdfpsOverlayParams()
-
-    /** Debug message. */
-    var debugMessage: String? = null
-        set(value) {
-            field = value
-            postInvalidate()
-        }
-
-    /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
-    var isDisplayConfigured: Boolean = false
-        private set
-
-    fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
-        mUdfpsDisplayMode = udfpsDisplayModeProvider
-    }
-
-    // Don't propagate any touch events to the child views.
-    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
-        return (animationViewController == null || !animationViewController!!.shouldPauseAuth())
-    }
-
-    override fun dozeTimeTick() {
-        animationViewController?.dozeTimeTick()
-    }
-
-    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        super.onLayout(changed, left, top, right, bottom)
-
-        // Updates sensor rect in relation to the overlay view
-        animationViewController?.onSensorRectUpdated(RectF(sensorRect))
-    }
-
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-        Log.v(TAG, "onAttachedToWindow")
-    }
-
-    override fun onDetachedFromWindow() {
-        super.onDetachedFromWindow()
-        Log.v(TAG, "onDetachedFromWindow")
-    }
-
-    override fun onDraw(canvas: Canvas) {
-        super.onDraw(canvas)
-        if (!isDisplayConfigured) {
-            if (!debugMessage.isNullOrEmpty()) {
-                canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
-            }
-        }
-    }
-
-    fun configureDisplay(onDisplayConfigured: Runnable) {
-        isDisplayConfigured = true
-        animationViewController?.onDisplayConfiguring()
-        mUdfpsDisplayMode?.enable(onDisplayConfigured)
-    }
-
-    fun unconfigureDisplay() {
-        isDisplayConfigured = false
-        animationViewController?.onDisplayUnconfigured()
-        mUdfpsDisplayMode?.disable(null /* onDisabled */)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
index 7503a8b..a105d66 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
 import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.lifecycle.repeatWhenAttached
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.launch
@@ -42,14 +41,13 @@
         viewModel: UdfpsTouchOverlayViewModel,
         udfpsOverlayInteractor: UdfpsOverlayInteractor,
     ) {
-        if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) return
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
                         viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
                             Log.d(
                                 "UdfpsTouchOverlayBinder",
-                                "[$view]: update shouldHandleTouches=$shouldHandleTouches"
+                                "[$view]: update shouldHandleTouches=$shouldHandleTouches",
                             )
                             view.isInvisible = !shouldHandleTouches
                             udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
@@ -58,7 +56,7 @@
                     .invokeOnCompletion {
                         Log.d(
                             "UdfpsTouchOverlayBinder",
-                            "[$view-detached]: update shouldHandleTouches=false"
+                            "[$view-detached]: update shouldHandleTouches=false",
                         )
                         udfpsOverlayInteractor.setHandleTouches(false)
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModel.kt
new file mode 100644
index 0000000..a6fb150
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModel.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import androidx.annotation.StringRes
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.res.R
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+
+sealed class AudioSharingButtonState {
+    object Gone : AudioSharingButtonState()
+
+    data class Visible(@StringRes val resId: Int, val isActive: Boolean) :
+        AudioSharingButtonState()
+}
+
+class AudioSharingButtonViewModel
+@AssistedInject
+constructor(
+    private val localBluetoothManager: LocalBluetoothManager?,
+    private val audioSharingInteractor: AudioSharingInteractor,
+    private val bluetoothStateInteractor: BluetoothStateInteractor,
+    private val deviceItemInteractor: DeviceItemInteractor,
+) : ExclusiveActivatable() {
+
+    private val mutableButtonState =
+        MutableStateFlow<AudioSharingButtonState>(AudioSharingButtonState.Gone)
+    /** Flow representing the update of AudioSharingButtonState. */
+    val audioSharingButtonStateUpdate: StateFlow<AudioSharingButtonState> =
+        mutableButtonState.asStateFlow()
+
+    override suspend fun onActivated(): Nothing {
+        combine(
+                bluetoothStateInteractor.bluetoothStateUpdate,
+                deviceItemInteractor.deviceItemUpdate,
+                audioSharingInteractor.isAudioSharingOn
+            ) { bluetoothState, deviceItem, audioSharingOn ->
+                getButtonState(bluetoothState, deviceItem, audioSharingOn)
+            }
+            .collect { mutableButtonState.value = it }
+        awaitCancellation()
+    }
+
+    private fun getButtonState(
+        bluetoothState: Boolean,
+        deviceItem: List<DeviceItem>,
+        audioSharingOn: Boolean
+    ): AudioSharingButtonState {
+        return when {
+            // Don't show button when bluetooth is off
+            !bluetoothState -> AudioSharingButtonState.Gone
+            // Show sharing audio when broadcasting
+            audioSharingOn ->
+                AudioSharingButtonState.Visible(
+                    R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
+                    isActive = true
+                )
+            // When not broadcasting, don't show button if there's connected source in any device
+            deviceItem.any {
+                BluetoothUtils.hasConnectedBroadcastSource(
+                    it.cachedBluetoothDevice,
+                    localBluetoothManager
+                )
+            } -> AudioSharingButtonState.Gone
+            // Show audio sharing when there's a connected LE audio device
+            deviceItem.any { BluetoothUtils.isActiveLeAudioDevice(it.cachedBluetoothDevice) } ->
+                AudioSharingButtonState.Visible(
+                    R.string.quick_settings_bluetooth_audio_sharing_button,
+                    isActive = false
+                )
+            else -> AudioSharingButtonState.Gone
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): AudioSharingButtonViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt
new file mode 100644
index 0000000..692a78b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bluetooth.qsdialog
+
+import android.bluetooth.BluetoothDevice
+import android.content.Intent
+import android.os.Bundle
+import android.provider.Settings
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.A2dpProfile
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.HeadsetProfile
+import com.android.settingslib.bluetooth.HearingAidProfile
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class AudioSharingDeviceItemActionInteractorImpl
+@Inject
+constructor(
+    private val activityStarter: ActivityStarter,
+    private val audioSharingInteractor: AudioSharingInteractor,
+    private val dialogTransitionAnimator: DialogTransitionAnimator,
+    private val localBluetoothManager: LocalBluetoothManager?,
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val logger: BluetoothTileDialogLogger,
+    private val uiEventLogger: UiEventLogger,
+    private val delegateFactory: AudioSharingDialogDelegate.Factory,
+    private val deviceItemActionInteractorImpl: DeviceItemActionInteractorImpl,
+) : DeviceItemActionInteractor {
+
+    override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
+        withContext(backgroundDispatcher) {
+            if (!audioSharingInteractor.audioSharingAvailable()) {
+                return@withContext deviceItemActionInteractorImpl.onClick(deviceItem, dialog)
+            }
+            val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager)
+            logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing)
+
+            when {
+                deviceItem.type ==
+                    DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> {
+                    if (audioSharingQsDialogImprovement()) {
+                        withContext(mainDispatcher) {
+                            delegateFactory
+                                .create(deviceItem.cachedBluetoothDevice)
+                                .createDialog()
+                                .let { dialogTransitionAnimator.showFromDialog(it, dialog) }
+                        }
+                    } else {
+                        launchSettings(deviceItem.cachedBluetoothDevice.device, dialog)
+                        logger.logLaunchSettingsCriteriaMatched(
+                            "AvailableAudioSharingDeviceClicked",
+                            deviceItem,
+                        )
+                    }
+                    uiEventLogger.log(
+                        BluetoothTileDialogUiEvent.AVAILABLE_AUDIO_SHARING_DEVICE_CLICKED
+                    )
+                }
+                inSharingAndDeviceNoSource(inAudioSharing, deviceItem) -> {
+                    launchSettings(deviceItem.cachedBluetoothDevice.device, dialog)
+                    logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem)
+                    uiEventLogger.log(
+                        if (deviceItem.isLeAudioSupported)
+                            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED
+                        else
+                            BluetoothTileDialogUiEvent
+                                .LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED
+                    )
+                }
+                else -> {
+                    deviceItemActionInteractorImpl.onClick(deviceItem, dialog)
+                }
+            }
+        }
+    }
+
+    private fun inSharingAndDeviceNoSource(
+        inAudioSharing: Boolean,
+        deviceItem: DeviceItem,
+    ): Boolean {
+        return inAudioSharing &&
+            deviceItem.isMediaDevice &&
+            !BluetoothUtils.hasConnectedBroadcastSource(
+                deviceItem.cachedBluetoothDevice,
+                localBluetoothManager,
+            )
+    }
+
+    private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) {
+        val intent =
+            Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply {
+                putExtra(
+                    EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+                    Bundle().apply {
+                        putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device)
+                    },
+                )
+            }
+        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
+        activityStarter.postStartActivityDismissingKeyguard(
+            intent,
+            0,
+            dialogTransitionAnimator.createActivityTransitionController(dialog),
+        )
+    }
+
+    private companion object {
+        const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
+
+        val DeviceItem.isLeAudioSupported: Boolean
+            get() =
+                cachedBluetoothDevice.profiles.any { profile ->
+                    profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device)
+                }
+
+        val DeviceItem.isMediaDevice: Boolean
+            get() =
+                cachedBluetoothDevice.uiAccessibleProfiles.any {
+                    it is A2dpProfile ||
+                        it is HearingAidProfile ||
+                        it is LeAudioProfile ||
+                        it is HeadsetProfile
+                }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegate.kt
new file mode 100644
index 0000000..3ac942b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegate.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import android.os.Bundle
+import android.widget.Button
+import android.widget.TextView
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+class AudioSharingDialogDelegate
+@AssistedInject
+constructor(
+    @Assisted private val cachedBluetoothDevice: CachedBluetoothDevice,
+    @Application private val coroutineScope: CoroutineScope,
+    private val viewModelFactory: AudioSharingDialogViewModel.Factory,
+    private val sysuiDialogFactory: SystemUIDialog.Factory,
+    private val uiEventLogger: UiEventLogger,
+) : SystemUIDialog.Delegate {
+
+    override fun createDialog(): SystemUIDialog = sysuiDialogFactory.create(this)
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        with(dialog.layoutInflater.inflate(R.layout.audio_sharing_dialog, null)) {
+            dialog.setView(this)
+            val subtitleTextView = requireViewById<TextView>(R.id.subtitle)
+            val shareAudioButton = requireViewById<TextView>(R.id.share_audio_button)
+            val switchActiveButton = requireViewById<Button>(R.id.switch_active_button)
+            val job =
+                coroutineScope.launch {
+                    val viewModel = viewModelFactory.create(cachedBluetoothDevice, this)
+                    viewModel.dialogState.collect {
+                        when (it) {
+                            is AudioSharingDialogState.Hide -> dialog.dismiss()
+                            is AudioSharingDialogState.Show -> {
+                                subtitleTextView.text = it.subtitle
+                                switchActiveButton.text = it.switchButtonText
+                                switchActiveButton.setOnClickListener {
+                                    viewModel.switchActiveClicked()
+                                    uiEventLogger.log(
+                                        BluetoothTileDialogUiEvent
+                                            .AUDIO_SHARING_DIALOG_SWITCH_ACTIVE_CLICKED
+                                    )
+                                    dialog.dismiss()
+                                }
+                                shareAudioButton.setOnClickListener {
+                                    viewModel.shareAudioClicked()
+                                    uiEventLogger.log(
+                                        BluetoothTileDialogUiEvent
+                                            .AUDIO_SHARING_DIALOG_SHARE_AUDIO_CLICKED
+                                    )
+                                    dialog.dismiss()
+                                }
+                            }
+                        }
+                    }
+                }
+            SystemUIDialog.registerDismissListener(dialog) { job.cancel() }
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(cachedBluetoothDevice: CachedBluetoothDevice): AudioSharingDialogDelegate
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModel.kt
new file mode 100644
index 0000000..dc970aea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModel.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import android.content.Context
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+
+sealed class AudioSharingDialogState {
+    data object Hide : AudioSharingDialogState()
+
+    data class Show(val subtitle: String, val switchButtonText: String) : AudioSharingDialogState()
+}
+
+class AudioSharingDialogViewModel
+@AssistedInject
+constructor(
+    deviceItemInteractor: DeviceItemInteractor,
+    private val audioSharingInteractor: AudioSharingInteractor,
+    private val context: Context,
+    private val localBluetoothManager: LocalBluetoothManager?,
+    @Assisted private val cachedBluetoothDevice: CachedBluetoothDevice,
+    @Assisted private val coroutineScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+    val dialogState: Flow<AudioSharingDialogState> =
+        deviceItemInteractor.deviceItemUpdateRequest
+            .map {
+                if (
+                    audioSharingInteractor.isAvailableAudioSharingMediaBluetoothDevice(
+                        cachedBluetoothDevice
+                    )
+                ) {
+                    createShowState(cachedBluetoothDevice)
+                } else {
+                    AudioSharingDialogState.Hide
+                }
+            }
+            .onStart { emit(createShowState(cachedBluetoothDevice)) }
+            .flowOn(backgroundDispatcher)
+            .distinctUntilChanged()
+
+    fun switchActiveClicked() {
+        coroutineScope.launch { audioSharingInteractor.switchActive(cachedBluetoothDevice) }
+    }
+
+    fun shareAudioClicked() {
+        coroutineScope.launch { audioSharingInteractor.startAudioSharing() }
+    }
+
+    private fun createShowState(
+        cachedBluetoothDevice: CachedBluetoothDevice
+    ): AudioSharingDialogState {
+        val activeDeviceName =
+            localBluetoothManager
+                ?.profileManager
+                ?.leAudioProfile
+                ?.activeDevices
+                ?.firstOrNull()
+                ?.let { localBluetoothManager.cachedDeviceManager?.findDevice(it)?.name } ?: ""
+        val availableDeviceName = cachedBluetoothDevice.name
+        return AudioSharingDialogState.Show(
+            context.getString(
+                R.string.quick_settings_bluetooth_audio_sharing_dialog_subtitle,
+                availableDeviceName,
+                activeDeviceName
+            ),
+            context.getString(
+                R.string.quick_settings_bluetooth_audio_sharing_dialog_switch_to_button,
+                availableDeviceName
+            )
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            cachedBluetoothDevice: CachedBluetoothDevice,
+            coroutineScope: CoroutineScope
+        ): AudioSharingDialogViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
index 817f2d7..65f1105 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
@@ -16,82 +16,148 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
-import androidx.annotation.StringRes
 import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.onPlaybackStarted
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.stateIn
-
-internal sealed class AudioSharingButtonState {
-    object Gone : AudioSharingButtonState()
-
-    data class Visible(@StringRes val resId: Int, val isActive: Boolean) :
-        AudioSharingButtonState()
-}
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.withContext
 
 /** Holds business logic for the audio sharing state. */
+interface AudioSharingInteractor {
+    val isAudioSharingOn: Flow<Boolean>
+
+    val audioSourceStateUpdate: Flow<Unit>
+
+    suspend fun handleAudioSourceWhenReady()
+
+    suspend fun isAvailableAudioSharingMediaBluetoothDevice(
+        cachedBluetoothDevice: CachedBluetoothDevice
+    ): Boolean
+
+    suspend fun switchActive(cachedBluetoothDevice: CachedBluetoothDevice)
+
+    suspend fun startAudioSharing()
+
+    suspend fun audioSharingAvailable(): Boolean
+}
+
 @SysUISingleton
-internal class AudioSharingInteractor
+@OptIn(ExperimentalCoroutinesApi::class)
+class AudioSharingInteractorImpl
 @Inject
 constructor(
     private val localBluetoothManager: LocalBluetoothManager?,
-    bluetoothStateInteractor: BluetoothStateInteractor,
-    deviceItemInteractor: DeviceItemInteractor,
-    @Application private val coroutineScope: CoroutineScope,
+    private val audioSharingRepository: AudioSharingRepository,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
-) {
-    /** Flow representing the update of AudioSharingButtonState. */
-    internal val audioSharingButtonStateUpdate: Flow<AudioSharingButtonState> =
-        combine(
-                bluetoothStateInteractor.bluetoothStateUpdate,
-                deviceItemInteractor.deviceItemUpdate
-            ) { bluetoothState, deviceItem ->
-                getButtonState(bluetoothState, deviceItem)
+) : AudioSharingInteractor {
+
+    override val isAudioSharingOn: Flow<Boolean> =
+        flow { emit(audioSharingAvailable()) }
+            .flatMapLatest { isEnabled ->
+                if (isEnabled) {
+                    audioSharingRepository.inAudioSharing
+                } else {
+                    flowOf(false)
+                }
             }
             .flowOn(backgroundDispatcher)
-            .stateIn(
-                coroutineScope,
-                SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
-                initialValue = AudioSharingButtonState.Gone
-            )
 
-    private fun getButtonState(
-        bluetoothState: Boolean,
-        deviceItem: List<DeviceItem>
-    ): AudioSharingButtonState {
-        return when {
-            // Don't show button when bluetooth is off
-            !bluetoothState -> AudioSharingButtonState.Gone
-            // Show sharing audio when broadcasting
-            BluetoothUtils.isBroadcasting(localBluetoothManager) ->
-                AudioSharingButtonState.Visible(
-                    R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
-                    isActive = true
-                )
-            // When not broadcasting, don't show button if there's connected source in any device
-            deviceItem.any {
-                BluetoothUtils.hasConnectedBroadcastSource(
-                    it.cachedBluetoothDevice,
-                    localBluetoothManager
-                )
-            } -> AudioSharingButtonState.Gone
-            // Show audio sharing when there's a connected LE audio device
-            deviceItem.any { BluetoothUtils.isActiveLeAudioDevice(it.cachedBluetoothDevice) } ->
-                AudioSharingButtonState.Visible(
-                    R.string.quick_settings_bluetooth_audio_sharing_button,
-                    isActive = false
-                )
-            else -> AudioSharingButtonState.Gone
+    override val audioSourceStateUpdate =
+        isAudioSharingOn
+            .flatMapLatest {
+                if (it) {
+                    audioSharingRepository.audioSourceStateUpdate
+                } else {
+                    emptyFlow()
+                }
+            }
+            .flowOn(backgroundDispatcher)
+
+    override suspend fun handleAudioSourceWhenReady() {
+        withContext(backgroundDispatcher) {
+            if (audioSharingAvailable()) {
+                audioSharingRepository.leAudioBroadcastProfile?.let { profile ->
+                    isAudioSharingOn
+                        .mapNotNull { audioSharingOn ->
+                            if (audioSharingOn) {
+                                // onPlaybackStarted could emit multiple times during one
+                                // audio sharing session, we only perform add source on the
+                                // first time
+                                profile.onPlaybackStarted.firstOrNull()
+                            } else {
+                                null
+                            }
+                        }
+                        .flowOn(backgroundDispatcher)
+                        .collect { audioSharingRepository.addSource() }
+                }
+            }
         }
     }
+
+    override suspend fun isAvailableAudioSharingMediaBluetoothDevice(
+        cachedBluetoothDevice: CachedBluetoothDevice
+    ): Boolean {
+        return withContext(backgroundDispatcher) {
+            if (audioSharingAvailable()) {
+                BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(
+                    cachedBluetoothDevice,
+                    localBluetoothManager,
+                )
+            } else {
+                false
+            }
+        }
+    }
+
+    override suspend fun switchActive(cachedBluetoothDevice: CachedBluetoothDevice) {
+        if (!audioSharingAvailable()) {
+            return
+        }
+        audioSharingRepository.setActive(cachedBluetoothDevice)
+    }
+
+    override suspend fun startAudioSharing() {
+        if (!audioSharingAvailable()) {
+            return
+        }
+        audioSharingRepository.startAudioSharing()
+    }
+
+    // TODO(b/367965193): Move this after flags rollout
+    override suspend fun audioSharingAvailable(): Boolean {
+        return audioSharingRepository.audioSharingAvailable()
+    }
+}
+
+@SysUISingleton
+class AudioSharingInteractorEmptyImpl @Inject constructor() : AudioSharingInteractor {
+    override val isAudioSharingOn: Flow<Boolean> = flowOf(false)
+
+    override val audioSourceStateUpdate: Flow<Unit> = emptyFlow()
+
+    override suspend fun handleAudioSourceWhenReady() {}
+
+    override suspend fun isAvailableAudioSharingMediaBluetoothDevice(
+        cachedBluetoothDevice: CachedBluetoothDevice
+    ) = false
+
+    override suspend fun switchActive(cachedBluetoothDevice: CachedBluetoothDevice) {}
+
+    override suspend fun startAudioSharing() {}
+
+    override suspend fun audioSharingAvailable(): Boolean = false
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepository.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepository.kt
new file mode 100644
index 0000000..b9b8d36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepository.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved
+import com.android.settingslib.volume.data.repository.AudioSharingRepository as SettingsLibAudioSharingRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.withContext
+
+interface AudioSharingRepository {
+    val leAudioBroadcastProfile: LocalBluetoothLeBroadcast?
+
+    val audioSourceStateUpdate: Flow<Unit>
+
+    val inAudioSharing: StateFlow<Boolean>
+
+    suspend fun audioSharingAvailable(): Boolean
+
+    suspend fun addSource()
+
+    suspend fun setActive(cachedBluetoothDevice: CachedBluetoothDevice)
+
+    suspend fun startAudioSharing()
+}
+
+@SysUISingleton
+class AudioSharingRepositoryImpl(
+    private val localBluetoothManager: LocalBluetoothManager,
+    private val settingsLibAudioSharingRepository: SettingsLibAudioSharingRepository,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : AudioSharingRepository {
+
+    override val leAudioBroadcastProfile: LocalBluetoothLeBroadcast?
+        get() = localBluetoothManager.profileManager?.leAudioBroadcastProfile
+
+    private val leAudioBroadcastAssistantProfile: LocalBluetoothLeBroadcastAssistant?
+        get() = localBluetoothManager.profileManager?.leAudioBroadcastAssistantProfile
+
+    override val audioSourceStateUpdate: Flow<Unit> =
+        leAudioBroadcastAssistantProfile?.onSourceConnectedOrRemoved ?: emptyFlow()
+
+    override val inAudioSharing: StateFlow<Boolean> =
+        settingsLibAudioSharingRepository.inAudioSharing
+
+    override suspend fun audioSharingAvailable(): Boolean {
+        return settingsLibAudioSharingRepository.audioSharingAvailable()
+    }
+
+    override suspend fun addSource() {
+        withContext(backgroundDispatcher) {
+            if (!settingsLibAudioSharingRepository.audioSharingAvailable()) {
+                return@withContext
+            }
+            leAudioBroadcastProfile?.latestBluetoothLeBroadcastMetadata?.let { metadata ->
+                leAudioBroadcastAssistantProfile?.let {
+                    it.allConnectedDevices.forEach { sink -> it.addSource(sink, metadata, false) }
+                }
+            }
+        }
+    }
+
+    override suspend fun setActive(cachedBluetoothDevice: CachedBluetoothDevice) {
+        withContext(backgroundDispatcher) {
+            if (!settingsLibAudioSharingRepository.audioSharingAvailable()) {
+                return@withContext
+            }
+            cachedBluetoothDevice.setActive()
+        }
+    }
+
+    override suspend fun startAudioSharing() {
+        withContext(backgroundDispatcher) {
+            if (!settingsLibAudioSharingRepository.audioSharingAvailable()) {
+                return@withContext
+            }
+            leAudioBroadcastProfile?.startPrivateBroadcast()
+        }
+    }
+}
+
+@SysUISingleton
+class AudioSharingRepositoryEmptyImpl : AudioSharingRepository {
+    override val leAudioBroadcastProfile: LocalBluetoothLeBroadcast? = null
+
+    override val audioSourceStateUpdate: Flow<Unit> = emptyFlow()
+
+    override val inAudioSharing: StateFlow<Boolean> = MutableStateFlow(false)
+
+    override suspend fun audioSharingAvailable(): Boolean = false
+
+    override suspend fun addSource() {}
+
+    override suspend fun setActive(cachedBluetoothDevice: CachedBluetoothDevice) {}
+
+    override suspend fun startAudioSharing() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
index 17f9e63..55d4d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
@@ -39,7 +39,7 @@
 
 /** Holds business logic for the Bluetooth Dialog's bluetooth and device connection state */
 @SysUISingleton
-internal class BluetoothStateInteractor
+class BluetoothStateInteractor
 @Inject
 constructor(
     private val localBluetoothManager: LocalBluetoothManager?,
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index 7deea73..a9c5c69 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -300,7 +300,7 @@
     }
 
     private fun getProgressBarBackground(dialog: SystemUIDialog): View {
-        return dialog.requireViewById(R.id.bluetooth_tile_dialog_progress_animation)
+        return dialog.requireViewById(R.id.bluetooth_tile_dialog_progress_background)
     }
 
     private fun getScrollViewContent(dialog: SystemUIDialog): View {
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
index bdd4c16..aad233f 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
@@ -42,6 +42,7 @@
     LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED(1717),
     @UiEvent(doc = "Currently broadcasting and a non-LE audio supported device is clicked")
     LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED(1718),
+    @Deprecated("Use case no longer needed")
     @UiEvent(
         doc = "Not broadcasting, having one connected, another saved LE audio device is clicked"
     )
@@ -52,8 +53,13 @@
     )
     @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked")
     LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720),
+    @Deprecated("Use case no longer needed")
     @UiEvent(doc = "Not broadcasting, having two connected, the active LE audio devices is clicked")
-    LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED(1881);
+    LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED(1881),
+    @UiEvent(doc = "Clicked on switch active button on audio sharing dialog")
+    AUDIO_SHARING_DIALOG_SWITCH_ACTIVE_CLICKED(1890),
+    @UiEvent(doc = "Clicked on share audio button on audio sharing dialog")
+    AUDIO_SHARING_DIALOG_SHARE_AUDIO_CLICKED(1891);
 
     override fun getId() = metricId
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
index a8f7fc3..5c35c52 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
@@ -28,8 +28,8 @@
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.bluetooth.BluetoothUtils
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement
 import com.android.systemui.Prefs
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -51,6 +51,7 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.channels.produce
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.merge
@@ -68,10 +69,12 @@
     private val bluetoothStateInteractor: BluetoothStateInteractor,
     private val bluetoothAutoOnInteractor: BluetoothAutoOnInteractor,
     private val audioSharingInteractor: AudioSharingInteractor,
+    private val audioSharingButtonViewModelFactory: AudioSharingButtonViewModel.Factory,
     private val bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val activityStarter: ActivityStarter,
     private val uiEventLogger: UiEventLogger,
+    private val logger: BluetoothTileDialogLogger,
     @Application private val coroutineScope: CoroutineScope,
     @Main private val mainDispatcher: CoroutineDispatcher,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
@@ -102,7 +105,7 @@
                     expandable?.dialogTransitionController(
                         DialogCuj(
                             InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                            INTERACTION_JANK_TAG
+                            INTERACTION_JANK_TAG,
                         )
                     )
                 controller?.let {
@@ -117,7 +120,7 @@
                 // stop the progress bar.
                 combine(
                         deviceItemInteractor.deviceItemUpdate,
-                        deviceItemInteractor.showSeeAllUpdate
+                        deviceItemInteractor.showSeeAllUpdate,
                     ) { deviceItem, showSeeAll ->
                         updateDialogUiJob?.cancel()
                         updateDialogUiJob = launch {
@@ -127,7 +130,7 @@
                                     deviceItem,
                                     showSeeAll,
                                     showPairNewDevice =
-                                        bluetoothStateInteractor.isBluetoothEnabled()
+                                        bluetoothStateInteractor.isBluetoothEnabled(),
                                 )
                                 animateProgressBar(dialog, false)
                             }
@@ -139,7 +142,15 @@
                 // the device item list and animate the progress bar.
                 merge(
                         deviceItemInteractor.deviceItemUpdateRequest,
-                        bluetoothDeviceMetadataInteractor.metadataUpdate
+                        bluetoothDeviceMetadataInteractor.metadataUpdate,
+                        if (
+                            audioSharingInteractor.audioSharingAvailable() &&
+                                audioSharingQsDialogImprovement()
+                        ) {
+                            audioSharingInteractor.audioSourceStateUpdate
+                        } else {
+                            emptyFlow()
+                        },
                     )
                     .onEach {
                         dialogDelegate.animateProgressBar(dialog, true)
@@ -147,35 +158,42 @@
                         updateDeviceItemJob = launch {
                             deviceItemInteractor.updateDeviceItems(
                                 context,
-                                DeviceFetchTrigger.BLUETOOTH_CALLBACK_RECEIVED
+                                DeviceFetchTrigger.BLUETOOTH_CALLBACK_RECEIVED,
                             )
                         }
                     }
                     .launchIn(this)
 
-                if (BluetoothUtils.isAudioSharingEnabled()) {
-                    audioSharingInteractor.audioSharingButtonStateUpdate
-                        .onEach {
-                            when (it) {
-                                is AudioSharingButtonState.Visible -> {
-                                    dialogDelegate.onAudioSharingButtonUpdated(
-                                        dialog,
-                                        VISIBLE,
-                                        context.getString(it.resId),
-                                        it.isActive
-                                    )
-                                }
-                                is AudioSharingButtonState.Gone -> {
-                                    dialogDelegate.onAudioSharingButtonUpdated(
-                                        dialog,
-                                        GONE,
-                                        label = null,
-                                        isActive = false
-                                    )
+                if (audioSharingInteractor.audioSharingAvailable()) {
+                    if (audioSharingQsDialogImprovement()) {
+                        launch { audioSharingInteractor.handleAudioSourceWhenReady() }
+                    }
+
+                    audioSharingButtonViewModelFactory.create().run {
+                        audioSharingButtonStateUpdate
+                            .onEach {
+                                when (it) {
+                                    is AudioSharingButtonState.Visible -> {
+                                        dialogDelegate.onAudioSharingButtonUpdated(
+                                            dialog,
+                                            VISIBLE,
+                                            context.getString(it.resId),
+                                            it.isActive,
+                                        )
+                                    }
+                                    is AudioSharingButtonState.Gone -> {
+                                        dialogDelegate.onAudioSharingButtonUpdated(
+                                            dialog,
+                                            GONE,
+                                            label = null,
+                                            isActive = false,
+                                        )
+                                    }
                                 }
                             }
-                        }
-                        .launchIn(this)
+                            .launchIn(this@launch)
+                        launch { activate() }
+                    }
                 }
 
                 // bluetoothStateUpdate is emitted when bluetooth on/off state is changed, re-fetch
@@ -185,13 +203,13 @@
                         dialogDelegate.onBluetoothStateUpdated(
                             dialog,
                             it,
-                            UiProperties.build(it, isAutoOnToggleFeatureAvailable())
+                            UiProperties.build(it, isAutoOnToggleFeatureAvailable()),
                         )
                         updateDeviceItemJob?.cancel()
                         updateDeviceItemJob = launch {
                             deviceItemInteractor.updateDeviceItems(
                                 context,
-                                DeviceFetchTrigger.BLUETOOTH_STATE_CHANGE_RECEIVED
+                                DeviceFetchTrigger.BLUETOOTH_STATE_CHANGE_RECEIVED,
                             )
                         }
                     }
@@ -209,7 +227,10 @@
 
                 // deviceItemClick is emitted when user clicked on a device item.
                 dialogDelegate.deviceItemClick
-                    .onEach { deviceItemActionInteractor.onClick(it, dialog) }
+                    .onEach {
+                        deviceItemActionInteractor.onClick(it, dialog)
+                        logger.logDeviceClick(it.cachedBluetoothDevice.address, it.type)
+                    }
                     .launchIn(this)
 
                 // contentHeight is emitted when the dialog is dismissed.
@@ -230,7 +251,7 @@
                                 dialog,
                                 it,
                                 if (it) R.string.turn_on_bluetooth_auto_info_enabled
-                                else R.string.turn_on_bluetooth_auto_info_disabled
+                                else R.string.turn_on_bluetooth_auto_info_disabled,
                             )
                         }
                         .launchIn(this)
@@ -252,18 +273,18 @@
             withContext(backgroundDispatcher) {
                 sharedPreferences.getInt(
                     CONTENT_HEIGHT_PREF_KEY,
-                    ViewGroup.LayoutParams.WRAP_CONTENT
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
                 )
             }
 
         return bluetoothDialogDelegateFactory.create(
             UiProperties.build(
                 bluetoothStateInteractor.isBluetoothEnabled(),
-                isAutoOnToggleFeatureAvailable()
+                isAutoOnToggleFeatureAvailable(),
             ),
             cachedContentHeight,
             this@BluetoothTileDialogViewModel,
-            { cancelJob() }
+            { cancelJob() },
         )
     }
 
@@ -275,7 +296,7 @@
                     EXTRA_SHOW_FRAGMENT_ARGUMENTS,
                     Bundle().apply {
                         putString("device_address", deviceItem.cachedBluetoothDevice.address)
-                    }
+                    },
                 )
             }
         startSettingsActivity(intent, view)
@@ -299,7 +320,7 @@
                     EXTRA_SHOW_FRAGMENT_ARGUMENTS,
                     Bundle().apply {
                         putBoolean(LocalBluetoothLeBroadcast.EXTRA_START_LE_AUDIO_SHARING, true)
-                    }
+                    },
                 )
             }
         startSettingsActivity(intent, view)
@@ -345,7 +366,7 @@
         companion object {
             internal fun build(
                 isBluetoothEnabled: Boolean,
-                isAutoOnToggleFeatureAvailable: Boolean
+                isAutoOnToggleFeatureAvailable: Boolean,
             ) =
                 UiProperties(
                     subTitleResId = getSubtitleResId(isBluetoothEnabled),
@@ -355,7 +376,7 @@
                     scrollViewMinHeightResId =
                         if (isAutoOnToggleFeatureAvailable)
                             R.dimen.bluetooth_dialog_scroll_view_min_height_with_auto_on
-                        else R.dimen.bluetooth_dialog_scroll_view_min_height
+                        else R.dimen.bluetooth_dialog_scroll_view_min_height,
                 )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
index f1894d3..cf0f19f 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
@@ -16,87 +16,28 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
-import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothProfile
-import android.content.Intent
-import android.os.Bundle
-import android.provider.Settings
 import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.bluetooth.A2dpProfile
-import com.android.settingslib.bluetooth.BluetoothUtils
-import com.android.settingslib.bluetooth.HeadsetProfile
-import com.android.settingslib.bluetooth.HearingAidProfile
-import com.android.settingslib.bluetooth.LeAudioProfile
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
-import com.android.settingslib.bluetooth.LocalBluetoothManager
-import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor.LaunchSettingsCriteria.Companion.getCurrentConnectedLeByGroupId
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.withContext
 
+interface DeviceItemActionInteractor {
+    suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {}
+}
+
 @SysUISingleton
-class DeviceItemActionInteractor
+class DeviceItemActionInteractorImpl
 @Inject
 constructor(
-    private val activityStarter: ActivityStarter,
-    private val dialogTransitionAnimator: DialogTransitionAnimator,
-    private val localBluetoothManager: LocalBluetoothManager?,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
-    private val logger: BluetoothTileDialogLogger,
     private val uiEventLogger: UiEventLogger,
-) {
-    private val leAudioProfile: LeAudioProfile?
-        get() = localBluetoothManager?.profileManager?.leAudioProfile
+) : DeviceItemActionInteractor {
 
-    private val assistantProfile: LocalBluetoothLeBroadcastAssistant?
-        get() = localBluetoothManager?.profileManager?.leAudioBroadcastAssistantProfile
-
-    private val launchSettingsCriteriaList: List<LaunchSettingsCriteria>
-        get() =
-            listOf(
-                InSharingClickedNoSource(localBluetoothManager, backgroundDispatcher, logger),
-                NotSharingClickedNonConnect(
-                    leAudioProfile,
-                    assistantProfile,
-                    backgroundDispatcher,
-                    logger
-                ),
-                NotSharingClickedActive(
-                    leAudioProfile,
-                    assistantProfile,
-                    backgroundDispatcher,
-                    logger
-                )
-            )
-
-    suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
+    override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
         withContext(backgroundDispatcher) {
-            logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
-            if (
-                BluetoothUtils.isAudioSharingEnabled() &&
-                    localBluetoothManager != null &&
-                    leAudioProfile != null &&
-                    assistantProfile != null
-            ) {
-                val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager)
-                logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing)
-
-                val criteriaMatched =
-                    launchSettingsCriteriaList.firstOrNull {
-                        it.matched(inAudioSharing, deviceItem)
-                    }
-                if (criteriaMatched != null) {
-                    uiEventLogger.log(criteriaMatched.getClickUiEvent(deviceItem))
-                    launchSettings(deviceItem.cachedBluetoothDevice.device, dialog)
-                    return@withContext
-                }
-            }
             deviceItem.cachedBluetoothDevice.apply {
                 when (deviceItem.type) {
                     DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
@@ -106,12 +47,6 @@
                     DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> {
                         uiEventLogger.log(BluetoothTileDialogUiEvent.AUDIO_SHARING_DEVICE_CLICKED)
                     }
-                    DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> {
-                        // TODO(b/360759048): pop up dialog
-                        uiEventLogger.log(
-                            BluetoothTileDialogUiEvent.AVAILABLE_AUDIO_SHARING_DEVICE_CLICKED
-                        )
-                    }
                     DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
                         setActive()
                         uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
@@ -126,186 +61,12 @@
                         connect()
                         uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
                     }
-                }
-            }
-        }
-    }
-
-    private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) {
-        val intent =
-            Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply {
-                putExtra(
-                    EXTRA_SHOW_FRAGMENT_ARGUMENTS,
-                    Bundle().apply {
-                        putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device)
+                    DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> {
+                        // Do nothing. Should already be handled in
+                        // AudioSharingDeviceItemActionInteractor.
                     }
-                )
-            }
-        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
-        activityStarter.postStartActivityDismissingKeyguard(
-            intent,
-            0,
-            dialogTransitionAnimator.createActivityTransitionController(dialog)
-        )
-    }
-
-    private interface LaunchSettingsCriteria {
-        suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean
-
-        suspend fun getClickUiEvent(deviceItem: DeviceItem): BluetoothTileDialogUiEvent
-
-        companion object {
-            suspend fun getCurrentConnectedLeByGroupId(
-                leAudioProfile: LeAudioProfile,
-                assistantProfile: LocalBluetoothLeBroadcastAssistant,
-                @Background backgroundDispatcher: CoroutineDispatcher,
-                logger: BluetoothTileDialogLogger,
-            ): Map<Int, List<BluetoothDevice>> {
-                return withContext(backgroundDispatcher) {
-                    assistantProfile
-                        .getDevicesMatchingConnectionStates(
-                            intArrayOf(BluetoothProfile.STATE_CONNECTED)
-                        )
-                        ?.filterNotNull()
-                        ?.groupBy { leAudioProfile.getGroupId(it) }
-                        ?.also { logger.logConnectedLeByGroupId(it) } ?: emptyMap()
                 }
             }
         }
     }
-
-    private class InSharingClickedNoSource(
-        private val localBluetoothManager: LocalBluetoothManager?,
-        @Background private val backgroundDispatcher: CoroutineDispatcher,
-        private val logger: BluetoothTileDialogLogger,
-    ) : LaunchSettingsCriteria {
-        // If currently broadcasting and the clicked device is not connected to the source
-        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
-            return withContext(backgroundDispatcher) {
-                val matched =
-                    inAudioSharing &&
-                        deviceItem.isMediaDevice &&
-                        !BluetoothUtils.hasConnectedBroadcastSource(
-                            deviceItem.cachedBluetoothDevice,
-                            localBluetoothManager
-                        )
-
-                if (matched) {
-                    logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem)
-                }
-
-                matched
-            }
-        }
-
-        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
-            if (deviceItem.isLeAudioSupported)
-                BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED
-            else BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED
-    }
-
-    private class NotSharingClickedNonConnect(
-        private val leAudioProfile: LeAudioProfile?,
-        private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
-        @Background private val backgroundDispatcher: CoroutineDispatcher,
-        private val logger: BluetoothTileDialogLogger,
-    ) : LaunchSettingsCriteria {
-        // If not broadcasting, having one device connected, and clicked on a not yet connected LE
-        // audio device
-        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
-            return withContext(backgroundDispatcher) {
-                val matched =
-                    leAudioProfile?.let { leAudio ->
-                        assistantProfile?.let { assistant ->
-                            !inAudioSharing &&
-                                getCurrentConnectedLeByGroupId(
-                                        leAudio,
-                                        assistant,
-                                        backgroundDispatcher,
-                                        logger
-                                    )
-                                    .size == 1 &&
-                                deviceItem.isNotConnectedLeAudioSupported
-                        }
-                    } ?: false
-
-                if (matched) {
-                    logger.logLaunchSettingsCriteriaMatched(
-                        "NotSharingClickedNonConnect",
-                        deviceItem
-                    )
-                }
-
-                matched
-            }
-        }
-
-        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
-            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED
-    }
-
-    private class NotSharingClickedActive(
-        private val leAudioProfile: LeAudioProfile?,
-        private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
-        @Background private val backgroundDispatcher: CoroutineDispatcher,
-        private val logger: BluetoothTileDialogLogger,
-    ) : LaunchSettingsCriteria {
-        // If not broadcasting, having two device connected, clicked on the active LE audio
-        // device
-        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
-            return withContext(backgroundDispatcher) {
-                val matched =
-                    leAudioProfile?.let { leAudio ->
-                        assistantProfile?.let { assistant ->
-                            !inAudioSharing &&
-                                getCurrentConnectedLeByGroupId(
-                                        leAudio,
-                                        assistant,
-                                        backgroundDispatcher,
-                                        logger
-                                    )
-                                    .size == 2 &&
-                                deviceItem.isActiveLeAudioSupported
-                        }
-                    } ?: false
-
-                if (matched) {
-                    logger.logLaunchSettingsCriteriaMatched(
-                        "NotSharingClickedConnected",
-                        deviceItem
-                    )
-                }
-
-                matched
-            }
-        }
-
-        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
-            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED
-    }
-
-    private companion object {
-        const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
-
-        val DeviceItem.isLeAudioSupported: Boolean
-            get() =
-                cachedBluetoothDevice.profiles.any { profile ->
-                    profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device)
-                }
-
-        val DeviceItem.isNotConnectedLeAudioSupported: Boolean
-            get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported
-
-        val DeviceItem.isActiveLeAudioSupported: Boolean
-            get() = type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE && isLeAudioSupported
-
-        val DeviceItem.isMediaDevice: Boolean
-            get() =
-                cachedBluetoothDevice.uiAccessibleProfiles.any {
-                    it is A2dpProfile ||
-                        it is HearingAidProfile ||
-                        it is LeAudioProfile ||
-                        it is HeadsetProfile
-                }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
index 7280489..7ed5629 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
@@ -23,7 +23,6 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.settingslib.flags.Flags
-import com.android.settingslib.flags.Flags.enableLeAudioSharing
 import com.android.systemui.res.R
 
 private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on
@@ -56,7 +55,7 @@
             connectionSummary: String,
             background: Int,
             actionAccessibilityLabel: String,
-            isActive: Boolean
+            isActive: Boolean,
         ): DeviceItem {
             return DeviceItem(
                 type = type,
@@ -70,7 +69,7 @@
                 background = background,
                 isEnabled = !cachedDevice.isBusy,
                 actionAccessibilityLabel = actionAccessibilityLabel,
-                isActive = isActive
+                isActive = isActive,
             )
         }
     }
@@ -80,7 +79,7 @@
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
             BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
@@ -94,20 +93,20 @@
             cachedDevice.connectionSummary ?: "",
             backgroundOn,
             context.getString(actionAccessibilityLabelDisconnect),
-            isActive = true
+            isActive = true,
         )
     }
 }
 
 internal class AudioSharingMediaDeviceItemFactory(
-    private val localBluetoothManager: LocalBluetoothManager?
+    private val localBluetoothManager: LocalBluetoothManager
 ) : DeviceItemFactory() {
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
-        return enableLeAudioSharing() &&
+        return BluetoothUtils.isAudioSharingEnabled() &&
             BluetoothUtils.hasConnectedBroadcastSource(cachedDevice, localBluetoothManager)
     }
 
@@ -120,24 +119,24 @@
                 ?: context.getString(audioSharing),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOn,
             "",
-            isActive = !cachedDevice.isBusy
+            isActive = !cachedDevice.isBusy,
         )
     }
 }
 
 internal class AvailableAudioSharingMediaDeviceItemFactory(
-    private val localBluetoothManager: LocalBluetoothManager?
+    private val localBluetoothManager: LocalBluetoothManager
 ) : AvailableMediaDeviceItemFactory() {
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return BluetoothUtils.isAudioSharingEnabled() &&
             super.isFilterMatched(context, cachedDevice, audioManager) &&
             BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(
                 cachedDevice,
-                localBluetoothManager
+                localBluetoothManager,
             )
     }
 
@@ -151,7 +150,7 @@
             ),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
             "",
-            isActive = false
+            isActive = false,
         )
     }
 }
@@ -160,7 +159,7 @@
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
             BluetoothUtils.isAvailableHearingDevice(cachedDevice)
@@ -171,7 +170,7 @@
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return !BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
             BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager)
@@ -186,7 +185,7 @@
                 ?: context.getString(connected),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
             context.getString(actionAccessibilityLabelActivate),
-            isActive = false
+            isActive = false,
         )
     }
 }
@@ -195,7 +194,7 @@
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return !BluetoothUtils.isActiveMediaDevice(cachedDevice) &&
             BluetoothUtils.isAvailableHearingDevice(cachedDevice)
@@ -206,7 +205,7 @@
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
             !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
@@ -225,7 +224,7 @@
                 ?: context.getString(connected),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
             context.getString(actionAccessibilityLabelDisconnect),
-            isActive = false
+            isActive = false,
         )
     }
 }
@@ -234,7 +233,7 @@
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
             !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
@@ -254,7 +253,7 @@
                 ?: context.getString(saved),
             if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
             context.getString(actionAccessibilityLabelActivate),
-            isActive = false
+            isActive = false,
         )
     }
 }
@@ -263,12 +262,12 @@
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
-        audioManager: AudioManager
+        audioManager: AudioManager,
     ): Boolean {
         return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
             !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
                 context,
-                cachedDevice.getDevice()
+                cachedDevice.getDevice(),
             ) &&
                 cachedDevice.isHearingAidDevice &&
                 cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
index 9114eca..01b84da 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
@@ -39,6 +39,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.withContext
@@ -54,6 +55,8 @@
     private val localBluetoothManager: LocalBluetoothManager?,
     private val systemClock: SystemClock,
     private val logger: BluetoothTileDialogLogger,
+    private val deviceItemFactoryList: List<@JvmSuppressWildcards DeviceItemFactory>,
+    private val deviceItemDisplayPriority: List<@JvmSuppressWildcards DeviceItemType>,
     @Application private val coroutineScope: CoroutineScope,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
@@ -67,7 +70,7 @@
     internal val showSeeAllUpdate
         get() = mutableShowSeeAllUpdate.asStateFlow()
 
-    internal val deviceItemUpdateRequest: SharedFlow<Unit> =
+    val deviceItemUpdateRequest: SharedFlow<Unit> =
         conflatedCallbackFlow {
                 val listener =
                     object : BluetoothCallback {
@@ -112,28 +115,9 @@
                 localBluetoothManager?.eventManager?.registerCallback(listener)
                 awaitClose { localBluetoothManager?.eventManager?.unregisterCallback(listener) }
             }
+            .flowOn(backgroundDispatcher)
             .shareIn(coroutineScope, SharingStarted.WhileSubscribed(replayExpirationMillis = 0))
 
-    private var deviceItemFactoryList: List<DeviceItemFactory> =
-        listOf(
-            ActiveMediaDeviceItemFactory(),
-            AudioSharingMediaDeviceItemFactory(localBluetoothManager),
-            AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager),
-            AvailableMediaDeviceItemFactory(),
-            ConnectedDeviceItemFactory(),
-            SavedDeviceItemFactory()
-        )
-
-    private var displayPriority: List<DeviceItemType> =
-        listOf(
-            DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
-            DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
-            DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
-            DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
-            DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
-            DeviceItemType.SAVED_BLUETOOTH_DEVICE,
-        )
-
     internal suspend fun updateDeviceItems(context: Context, trigger: DeviceFetchTrigger) {
         withContext(backgroundDispatcher) {
             val start = systemClock.elapsedRealtime()
@@ -144,7 +128,7 @@
                             .firstOrNull { it.isFilterMatched(context, cachedDevice, audioManager) }
                             ?.create(context, cachedDevice)
                     }
-                    .sort(displayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices)
+                    .sort(deviceItemDisplayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices)
             // Only emit when the job is not cancelled
             if (isActive) {
                 mutableDeviceItemUpdate.tryEmit(deviceItems.take(MAX_DEVICE_ITEM_ENTRY))
@@ -176,14 +160,6 @@
         )
     }
 
-    internal fun setDeviceItemFactoryListForTesting(list: List<DeviceItemFactory>) {
-        deviceItemFactoryList = list
-    }
-
-    internal fun setDisplayPriorityForTesting(list: List<DeviceItemType>) {
-        displayPriority = list
-    }
-
     companion object {
         private const val TAG = "DeviceItemInteractor"
         private const val MAX_DEVICE_ITEM_ENTRY = 3
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt
new file mode 100644
index 0000000..50970a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog.dagger
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.flags.Flags
+import com.android.settingslib.volume.data.repository.AudioSharingRepository as SettingsLibAudioSharingRepository
+import com.android.systemui.bluetooth.qsdialog.ActiveMediaDeviceItemFactory
+import com.android.systemui.bluetooth.qsdialog.AudioSharingDeviceItemActionInteractorImpl
+import com.android.systemui.bluetooth.qsdialog.AudioSharingInteractor
+import com.android.systemui.bluetooth.qsdialog.AudioSharingInteractorEmptyImpl
+import com.android.systemui.bluetooth.qsdialog.AudioSharingInteractorImpl
+import com.android.systemui.bluetooth.qsdialog.AudioSharingMediaDeviceItemFactory
+import com.android.systemui.bluetooth.qsdialog.AudioSharingRepository
+import com.android.systemui.bluetooth.qsdialog.AudioSharingRepositoryEmptyImpl
+import com.android.systemui.bluetooth.qsdialog.AudioSharingRepositoryImpl
+import com.android.systemui.bluetooth.qsdialog.AvailableAudioSharingMediaDeviceItemFactory
+import com.android.systemui.bluetooth.qsdialog.AvailableMediaDeviceItemFactory
+import com.android.systemui.bluetooth.qsdialog.ConnectedDeviceItemFactory
+import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor
+import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractorImpl
+import com.android.systemui.bluetooth.qsdialog.DeviceItemFactory
+import com.android.systemui.bluetooth.qsdialog.DeviceItemType
+import com.android.systemui.bluetooth.qsdialog.SavedDeviceItemFactory
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+
+/** Dagger module for audio sharing code for BT QS dialog */
+@Module
+interface AudioSharingModule {
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        fun provideAudioSharingRepository(
+            localBluetoothManager: LocalBluetoothManager?,
+            settingsLibAudioSharingRepository: SettingsLibAudioSharingRepository,
+            @Background backgroundDispatcher: CoroutineDispatcher,
+        ): AudioSharingRepository =
+            if (
+                Flags.enableLeAudioSharing() &&
+                    Flags.audioSharingQsDialogImprovement() &&
+                    localBluetoothManager != null
+            ) {
+                AudioSharingRepositoryImpl(
+                    localBluetoothManager,
+                    settingsLibAudioSharingRepository,
+                    backgroundDispatcher,
+                )
+            } else {
+                AudioSharingRepositoryEmptyImpl()
+            }
+
+        @Provides
+        @SysUISingleton
+        fun provideAudioSharingInteractor(
+            localBluetoothManager: LocalBluetoothManager?,
+            impl: Lazy<AudioSharingInteractorImpl>,
+            emptyImpl: Lazy<AudioSharingInteractorEmptyImpl>,
+        ): AudioSharingInteractor =
+            if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
+                impl.get()
+            } else {
+                emptyImpl.get()
+            }
+
+        @Provides
+        @SysUISingleton
+        fun provideDeviceItemActionInteractor(
+            localBluetoothManager: LocalBluetoothManager?,
+            audioSharingImpl: Lazy<AudioSharingDeviceItemActionInteractorImpl>,
+            impl: Lazy<DeviceItemActionInteractorImpl>,
+        ): DeviceItemActionInteractor =
+            if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
+                audioSharingImpl.get()
+            } else {
+                impl.get()
+            }
+
+        @Provides
+        @SysUISingleton
+        fun provideDeviceItemFactoryList(
+            localBluetoothManager: LocalBluetoothManager?
+        ): List<DeviceItemFactory> = buildList {
+            add(ActiveMediaDeviceItemFactory())
+            if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
+                add(AudioSharingMediaDeviceItemFactory(localBluetoothManager))
+                add(AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager))
+            }
+            add(AvailableMediaDeviceItemFactory())
+            add(ConnectedDeviceItemFactory())
+            add(SavedDeviceItemFactory())
+        }
+
+        @Provides
+        @SysUISingleton
+        fun provideDeviceItemDisplayPriority(
+            localBluetoothManager: LocalBluetoothManager?
+        ): List<DeviceItemType> = buildList {
+            add(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+            if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
+                add(DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE)
+                add(DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE)
+            }
+            add(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+            add(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+            add(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index f1c3f94..22b2888 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.log.dagger.BouncerTableLog
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
@@ -88,7 +87,6 @@
     val showMessage: StateFlow<BouncerShowMessageModel?>
     val resourceUpdateRequests: StateFlow<Boolean>
     val alternateBouncerVisible: StateFlow<Boolean>
-    val alternateBouncerUIAvailable: StateFlow<Boolean>
 
     /** Last shown security mode of the primary bouncer (ie: pin/pattern/password/SIM) */
     val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode>
@@ -126,8 +124,6 @@
 
     fun setAlternateVisible(isVisible: Boolean)
 
-    fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
-
     fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode)
 }
 
@@ -199,9 +195,6 @@
     private val _alternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
-    private val _alternateBouncerUIAvailable = MutableStateFlow(false)
-    override val alternateBouncerUIAvailable: StateFlow<Boolean> =
-        _alternateBouncerUIAvailable.asStateFlow()
 
     init {
         setUpLogging()
@@ -220,11 +213,6 @@
         _alternateBouncerVisible.value = isVisible
     }
 
-    override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        _alternateBouncerUIAvailable.value = isAvailable
-    }
-
     override fun setPrimaryShow(isShowing: Boolean) {
         _primaryBouncerShow.value = isShowing
     }
@@ -320,9 +308,6 @@
         resourceUpdateRequests
             .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false)
             .launchIn(applicationScope)
-        alternateBouncerUIAvailable
-            .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false)
-            .launchIn(applicationScope)
         alternateBouncerVisible
             .logDiffsForTable(buffer, "", "AlternateBouncerVisible", false)
             .launchIn(applicationScope)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 373671d0..9c2a10a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -16,22 +16,18 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
-import com.android.keyguard.KeyguardUpdateMonitor
+import android.util.Log
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.time.SystemClock
 import dagger.Lazy
@@ -46,6 +42,7 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates business logic for interacting with the lock-screen alternate bouncer. */
@@ -53,13 +50,9 @@
 class AlternateBouncerInteractor
 @Inject
 constructor(
-    private val statusBarStateController: StatusBarStateController,
-    private val keyguardStateController: KeyguardStateController,
     private val bouncerRepository: KeyguardBouncerRepository,
     fingerprintPropertyRepository: FingerprintPropertyRepository,
-    private val biometricSettingsRepository: BiometricSettingsRepository,
     private val systemClock: SystemClock,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val deviceEntryBiometricsAllowedInteractor:
         Lazy<DeviceEntryBiometricsAllowedInteractor>,
     private val keyguardInteractor: Lazy<KeyguardInteractor>,
@@ -71,17 +64,10 @@
     val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
     private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet()
     val alternateBouncerSupported: StateFlow<Boolean> =
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            fingerprintPropertyRepository.sensorType
-                .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
-                .stateIn(
-                    scope = scope,
-                    started = SharingStarted.Eagerly,
-                    initialValue = false,
-                )
-        } else {
-            bouncerRepository.alternateBouncerUIAvailable
-        }
+        fingerprintPropertyRepository.sensorType
+            .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
+            .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false)
+
     private val isDozingOrAod: Flow<Boolean> =
         anyOf(
                 keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map {
@@ -104,7 +90,7 @@
                     combine(
                             keyguardTransitionInteractor.get().currentKeyguardState,
                             sceneInteractor.get().currentScene,
-                            ::Pair
+                            ::Pair,
                         )
                         .flatMapLatest { (currentKeyguardState, transitionState) ->
                             if (currentKeyguardState == KeyguardState.GONE) {
@@ -120,7 +106,7 @@
                                         .isFingerprintAuthCurrentlyAllowed,
                                     keyguardInteractor.get().isKeyguardDismissible,
                                     bouncerRepository.primaryBouncerShow,
-                                    isDozingOrAod
+                                    isDozingOrAod,
                                 ) {
                                     fingerprintAllowed,
                                     keyguardDismissible,
@@ -137,37 +123,19 @@
                     flowOf(false)
                 }
             }
-            .stateIn(
-                scope = scope,
-                started = WhileSubscribed(),
-                initialValue = false,
-            )
+            .distinctUntilChanged()
+            .onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") }
+            .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false)
 
     /**
      * Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before
      * calling this.
      */
     fun forceShow() {
-        if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
-            show()
-            return
-        }
         bouncerRepository.setAlternateVisible(true)
     }
 
     /**
-     * Sets the correct bouncer states to show the alternate bouncer if it can show.
-     *
-     * @return whether alternateBouncer is visible
-     * @deprecated use [forceShow] and manually check [canShowAlternateBouncer] beforehand
-     */
-    fun show(): Boolean {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint())
-        return isVisibleState()
-    }
-
-    /**
      * Sets the correct bouncer states to hide the bouncer. Should only be called through
      * StatusBarKeyguardViewManager until ScrimController is refactored to use
      * alternateBouncerInteractor.
@@ -185,28 +153,8 @@
         return bouncerRepository.alternateBouncerVisible.value
     }
 
-    fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) {
-        DeviceEntryUdfpsRefactor.assertInLegacyMode()
-        if (isAvailable) {
-            alternateBouncerUiAvailableFromSource.add(token)
-        } else {
-            alternateBouncerUiAvailableFromSource.remove(token)
-        }
-        bouncerRepository.setAlternateBouncerUIAvailable(
-            alternateBouncerUiAvailableFromSource.isNotEmpty()
-        )
-    }
-
     fun canShowAlternateBouncerForFingerprint(): Boolean {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            return canShowAlternateBouncer.value
-        }
-        return alternateBouncerSupported.value &&
-            biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value &&
-            !keyguardUpdateMonitor.isFingerprintLockedOut &&
-            !keyguardStateController.isUnlocked &&
-            !statusBarStateController.isDozing &&
-            !bouncerRepository.primaryBouncerShow.value
+        return canShowAlternateBouncer.value
     }
 
     /**
@@ -234,5 +182,7 @@
 
     companion object {
         private const val MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS = 200L
+
+        private const val TAG = "AlternateBouncerInteractor"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
index 2c026c0..da01c58 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
 import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -74,6 +75,7 @@
     private val metricsLogger: MetricsLogger,
     private val dozeLogger: DozeLogger,
     private val sceneInteractor: Lazy<SceneInteractor>,
+    private val bouncerHapticPlayer: BouncerHapticPlayer,
 ) {
     /** The bouncer action button. If `null`, the button should not be shown. */
     val actionButton: Flow<BouncerActionButtonModel?> =
@@ -111,6 +113,8 @@
         BouncerActionButtonModel(
             label = applicationContext.getString(R.string.lockscreen_emergency_call),
             onClick = {
+                // TODO(b/373930432): haptics should be played at the UI layer -> refactor
+                bouncerHapticPlayer.playEmergencyButtonClickFeedback()
                 prepareToPerformAction()
                 dozeLogger.logEmergencyCall()
                 startEmergencyDialerActivity()
@@ -119,8 +123,6 @@
             onLongClick = {
                 if (emergencyAffordanceManager.needsEmergencyAffordance()) {
                     prepareToPerformAction()
-
-                    // TODO(b/369767936): Check that !longPressWasDragged before invoking.
                     emergencyAffordanceManager.performEmergencyCall()
                 }
             },
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index 49dadce..12f06bb 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -4,17 +4,21 @@
 import com.android.keyguard.KeyguardMessageAreaController
 import com.android.keyguard.dagger.KeyguardBouncerComponent
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
 import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
 import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.log.BouncerLogger
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Helper data class that allows to lazy load all the dependencies of the legacy bouncer. */
@@ -37,6 +41,10 @@
 data class ComposeBouncerDependencies
 @Inject
 constructor(
+    @Application val applicationScope: CoroutineScope,
+    val keyguardInteractor: KeyguardInteractor,
+    val selectedUserInteractor: SelectedUserInteractor,
+    val legacyInteractor: PrimaryBouncerInteractor,
     val viewModelFactory: BouncerSceneContentViewModel.Factory,
     val dialogFactory: BouncerDialogFactory,
     val bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
@@ -58,6 +66,10 @@
             val deps = composeBouncerDependencies.get()
             ComposeBouncerViewBinder.bind(
                 view,
+                deps.applicationScope,
+                deps.legacyInteractor,
+                deps.keyguardInteractor,
+                deps.selectedUserInteractor,
                 deps.viewModelFactory,
                 deps.dialogFactory,
                 deps.bouncerContainerViewModelFactory,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index fdbc18d..80c4291 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -6,23 +6,59 @@
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.compose.ui.platform.ComposeView
 import androidx.lifecycle.Lifecycle
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
 import com.android.systemui.bouncer.ui.composable.BouncerContainer
 import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.kotlin.sample
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
 
 /** View binder responsible for binding the compose version of the bouncer. */
 object ComposeBouncerViewBinder {
+    private var persistentBouncerJob: Job? = null
+
     fun bind(
         view: ViewGroup,
+        scope: CoroutineScope,
+        legacyInteractor: PrimaryBouncerInteractor,
+        keyguardInteractor: KeyguardInteractor,
+        selectedUserInteractor: SelectedUserInteractor,
         viewModelFactory: BouncerSceneContentViewModel.Factory,
         dialogFactory: BouncerDialogFactory,
         bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
     ) {
+        persistentBouncerJob?.cancel()
+        persistentBouncerJob =
+            scope.launch {
+                launch {
+                    legacyInteractor.isShowing
+                        .sample(keyguardInteractor.isKeyguardDismissible, ::Pair)
+                        .collect { (isShowing, dismissible) ->
+                            if (isShowing && dismissible) {
+                                legacyInteractor.notifyUserRequestedBouncerWhenAlreadyAuthenticated(
+                                    selectedUserInteractor.getSelectedUserId()
+                                )
+                            }
+                        }
+                }
+
+                launch {
+                    legacyInteractor.startingDisappearAnimation.collect {
+                        it.run()
+                        legacyInteractor.hide()
+                    }
+                }
+            }
+
         view.repeatWhenAttached {
             view.viewModel(
                 minWindowLifecycleState = WindowLifecycleState.ATTACHED,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
index d6b9211..8373907 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
@@ -81,4 +81,11 @@
 
     /** Deliver MSDL feedback when a numpad key is pressed on the pin bouncer */
     fun playNumpadKeyFeedback() = msdlPlayer.get().playToken(MSDLToken.KEYPRESS_STANDARD)
+
+    /** Deliver MSDL feedback when clicking on the emergency button */
+    fun playEmergencyButtonClickFeedback() {
+        if (isEnabled) {
+            msdlPlayer.get().playToken(MSDLToken.KEYPRESS_RETURN)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 4185aed..148b9ea 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.bouncer.ui.viewmodel
 
 import android.annotation.StringRes
-import com.android.app.tracing.coroutines.flow.collectLatest
 import com.android.systemui.authentication.domain.interactor.AuthenticationResult
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -28,6 +27,7 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.receiveAsFlow
 
 sealed class AuthMethodBouncerViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
index 5a4f8eb..615cb5b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
@@ -18,10 +18,8 @@
 
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.kotlin.sample
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.awaitCancellation
@@ -34,24 +32,11 @@
     private val legacyInteractor: PrimaryBouncerInteractor,
     private val authenticationInteractor: AuthenticationInteractor,
     private val selectedUserInteractor: SelectedUserInteractor,
-    private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
 ) : ExclusiveActivatable() {
 
     override suspend fun onActivated(): Nothing {
         coroutineScope {
             launch {
-                legacyInteractor.isShowing
-                    .sample(deviceUnlockedInteractor.deviceUnlockStatus, ::Pair)
-                    .collect { (isShowing, unlockStatus) ->
-                        if (isShowing && unlockStatus.isUnlocked) {
-                            legacyInteractor.notifyUserRequestedBouncerWhenAlreadyAuthenticated(
-                                selectedUserInteractor.getSelectedUserId()
-                            )
-                        }
-                    }
-            }
-
-            launch {
                 authenticationInteractor.onAuthenticationResult.collect { authenticationSucceeded ->
                     if (authenticationSucceeded) {
                         legacyInteractor.notifyKeyguardAuthenticatedPrimaryAuth(
@@ -60,13 +45,6 @@
                     }
                 }
             }
-
-            launch {
-                legacyInteractor.startingDisappearAnimation.collect {
-                    it.run()
-                    legacyInteractor.hide()
-                }
-            }
             awaitCancellation()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
index 0bcb58d..5f97391 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -175,8 +175,10 @@
                             actions.map { action ->
                                 UserSwitcherDropdownItemViewModel(
                                     icon =
-                                        Icon.Resource(
-                                            action.iconResourceId,
+                                        Icon.Loaded(
+                                            applicationContext.resources.getDrawable(
+                                                action.iconResourceId
+                                            ),
                                             contentDescription = null,
                                         ),
                                     text = Text.Resource(action.textResourceId),
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index 8270db1..4708079 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -18,6 +18,8 @@
 
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.size
 import androidx.compose.material3.MaterialTheme
@@ -33,12 +35,17 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSlider
+import com.android.systemui.Flags
 import com.android.systemui.brightness.shared.model.GammaBrightness
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
 import com.android.systemui.brightness.ui.viewmodel.Drag
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
+import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.utils.PolicyRestriction
 import kotlinx.coroutines.launch
 
@@ -54,12 +61,30 @@
     onStop: (Int) -> Unit,
     modifier: Modifier = Modifier,
     formatter: (Int) -> String = { "$it" },
+    hapticsViewModelFactory: SliderHapticsViewModel.Factory,
 ) {
     var value by remember(gammaValue) { mutableIntStateOf(gammaValue) }
     val animatedValue by
         animateFloatAsState(targetValue = value.toFloat(), label = "BrightnessSliderAnimatedValue")
     val floatValueRange = valueRange.first.toFloat()..valueRange.last.toFloat()
     val isRestricted = remember(restriction) { restriction is PolicyRestriction.Restricted }
+    val interactionSource = remember { MutableInteractionSource() }
+    val hapticsViewModel: SliderHapticsViewModel? =
+        if (Flags.hapticsForComposeSliders()) {
+            rememberViewModel(traceName = "SliderHapticsViewModel") {
+                hapticsViewModelFactory.create(
+                    interactionSource,
+                    floatValueRange,
+                    Orientation.Horizontal,
+                    SliderHapticFeedbackConfig(
+                        maxVelocityToScale = 1f /* slider progress(from 0 to 1) per sec */
+                    ),
+                    SeekableSliderTrackerConfig(),
+                )
+            }
+        } else {
+            null
+        }
 
     PlatformSlider(
         value = animatedValue,
@@ -67,19 +92,19 @@
         enabled = !isRestricted,
         onValueChange = {
             if (!isRestricted) {
+                hapticsViewModel?.onValueChange(it)
                 value = it.toInt()
                 onDrag(value)
             }
         },
         onValueChangeFinished = {
             if (!isRestricted) {
+                hapticsViewModel?.onValueChangeEnded()
                 onStop(value)
             }
         },
         modifier =
-            modifier.clickable(
-                enabled = isRestricted,
-            ) {
+            modifier.clickable(enabled = isRestricted) {
                 if (restriction is PolicyRestriction.Restricted) {
                     onRestrictedClick(restriction)
                 }
@@ -98,14 +123,12 @@
                 maxLines = 1,
             )
         },
+        interactionSource = interactionSource,
     )
 }
 
 @Composable
-fun BrightnessSliderContainer(
-    viewModel: BrightnessSliderViewModel,
-    modifier: Modifier = Modifier,
-) {
+fun BrightnessSliderContainer(viewModel: BrightnessSliderViewModel, modifier: Modifier = Modifier) {
     val state by viewModel.currentBrightness.collectAsStateWithLifecycle()
     val gamma = state.value
     val coroutineScope = rememberCoroutineScope()
@@ -125,5 +148,6 @@
         onStop = { coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) } },
         modifier = modifier.fillMaxWidth(),
         formatter = viewModel::formatValue,
+        hapticsViewModelFactory = viewModel.hapticsViewModelFactory,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
index 16a1dcc..074ac50 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
 import com.android.systemui.res.R
 import com.android.systemui.utils.PolicyRestriction
 import javax.inject.Inject
@@ -38,12 +39,13 @@
     private val screenBrightnessInteractor: ScreenBrightnessInteractor,
     private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor,
     @Application private val applicationScope: CoroutineScope,
+    val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
 ) {
     val currentBrightness =
         screenBrightnessInteractor.gammaBrightness.stateIn(
             applicationScope,
             SharingStarted.WhileSubscribed(),
-            GammaBrightness(0)
+            GammaBrightness(0),
         )
 
     val maxBrightness = screenBrightnessInteractor.maxGammaBrightness
@@ -85,6 +87,8 @@
 /** Represents a drag event in a brightness slider. */
 sealed interface Drag {
     val brightness: GammaBrightness
+
     @JvmInline value class Dragging(override val brightness: GammaBrightness) : Drag
+
     @JvmInline value class Stopped(override val brightness: GammaBrightness) : Drag
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index 613280c..7716ece 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -17,6 +17,7 @@
 package com.android.systemui.classifier;
 
 import android.content.res.Resources;
+import android.hardware.devicestate.DeviceStateManager;
 import android.view.ViewConfiguration;
 
 import com.android.systemui.dagger.SysUISingleton;
@@ -24,6 +25,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.statusbar.phone.NotificationTapHelper;
+import com.android.systemui.util.Utils;
 
 import dagger.Binds;
 import dagger.Module;
@@ -104,12 +106,8 @@
     /** */
     @Provides
     @Named(IS_FOLDABLE_DEVICE)
-    static boolean providesIsFoldableDevice(@Main Resources resources) {
-        try {
-            return resources.getIntArray(
-                    com.android.internal.R.array.config_foldedDeviceStates).length != 0;
-        } catch (Resources.NotFoundException e) {
-            return false;
-        }
+    static boolean providesIsFoldableDevice(@Main Resources resources,
+            DeviceStateManager deviceStateManager) {
+        return Utils.isDeviceFoldable(resources, deviceStateManager);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt
index e3f1174..a695163 100644
--- a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt
@@ -19,7 +19,7 @@
 import android.app.usage.UsageEvents
 import android.app.usage.UsageEventsQuery
 import android.app.usage.UsageStatsManager
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
 import com.android.systemui.common.usagestats.data.model.UsageStatsQuery
 import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
 import com.android.systemui.common.usagestats.shared.model.ActivityEventModel.Lifecycle
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 08a7c39..8510915 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal
 
+import android.os.UserHandle
 import android.provider.Settings
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
@@ -147,9 +148,10 @@
             .emitOnStart()
             .onEach {
                 screenTimeout =
-                    systemSettings.getInt(
+                    systemSettings.getIntForUser(
                         Settings.System.SCREEN_OFF_TIMEOUT,
                         DEFAULT_SCREEN_TIMEOUT,
+                        UserHandle.USER_CURRENT,
                     )
             }
             .launchIn(bgScope)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 3a04d02..dc24805 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -23,7 +23,7 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.provider.Settings
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 3826fb4..428b83d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.communal.domain.interactor
 
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
index ba96f4e..71bfe0c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
@@ -24,8 +24,7 @@
 import android.widget.FrameLayout
 import androidx.compose.ui.unit.IntSize
 import androidx.core.view.doOnLayout
-import com.android.app.tracing.coroutines.flow.flowOn
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags.communalWidgetResizing
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -41,6 +40,7 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 
 object CommunalAppWidgetHostViewBinder {
     private const val TAG = "CommunalAppWidgetHostViewBinder"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 3ae9250..6508e4b5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -27,7 +27,6 @@
 import android.util.Log
 import android.view.accessibility.AccessibilityEvent
 import android.view.accessibility.AccessibilityManager
-import androidx.activity.result.ActivityResultLauncher
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.communal.dagger.CommunalModule.Companion.LAUNCHER_PACKAGE
 import com.android.systemui.communal.data.model.CommunalWidgetCategories
@@ -184,10 +183,10 @@
 
     val isIdleOnCommunal: StateFlow<Boolean> = communalInteractor.isIdleOnCommunal
 
-    /** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */
+    /** Launch the widget picker activity using the given startActivity method. */
     suspend fun onOpenWidgetPicker(
         resources: Resources,
-        activityLauncher: ActivityResultLauncher<Intent>,
+        startActivity: (intent: Intent) -> Unit,
     ): Boolean =
         withContext(backgroundDispatcher) {
             val widgets = communalInteractor.widgetContent.first()
@@ -199,7 +198,7 @@
                 }
             getWidgetPickerActivityIntent(resources, excludeList)?.let {
                 try {
-                    activityLauncher.launch(it)
+                    startActivity(it)
                     return@withContext true
                 } catch (e: Exception) {
                     Log.e(TAG, "Failed to launch widget picker activity", e)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
index 87fcdd7..db4bee7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
@@ -13,14 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.systemui.communal.ui.viewmodel
 
 import androidx.compose.foundation.gestures.AnchoredDraggableState
 import androidx.compose.foundation.gestures.DraggableAnchors
 import androidx.compose.runtime.snapshotFlow
-import com.android.app.tracing.coroutines.coroutineScope
+import com.android.app.tracing.coroutines.coroutineScopeTraced as coroutineScope
 import com.android.systemui.lifecycle.ExclusiveActivatable
+import kotlin.math.abs
+import kotlin.math.ceil
+import kotlin.math.floor
+import kotlin.math.sign
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -50,14 +53,33 @@
 }
 
 class ResizeableItemFrameViewModel : ExclusiveActivatable() {
-    private data class GridLayoutInfo(
-        val minSpan: Int,
-        val maxSpan: Int,
-        val heightPerSpanPx: Float,
-        val verticalItemSpacingPx: Float,
+    data class GridLayoutInfo(
         val currentRow: Int,
         val currentSpan: Int,
-    )
+        val maxHeightPx: Int,
+        val minHeightPx: Int,
+        val resizeMultiple: Int,
+        val totalSpans: Int,
+        private val heightPerSpanPx: Float,
+        private val verticalItemSpacingPx: Float,
+    ) {
+        fun getPxOffsetForResize(spans: Int): Int =
+            (spans * (heightPerSpanPx + verticalItemSpacingPx)).toInt()
+
+        private fun getSpansForPx(height: Int): Int =
+            ceil((height + verticalItemSpacingPx) / (heightPerSpanPx + verticalItemSpacingPx))
+                .toInt()
+                .coerceIn(resizeMultiple, totalSpans)
+
+        private fun roundDownToMultiple(spans: Int): Int =
+            floor(spans.toDouble() / resizeMultiple).toInt() * resizeMultiple
+
+        val maxSpans: Int
+            get() = roundDownToMultiple(getSpansForPx(maxHeightPx))
+
+        val minSpans: Int
+            get() = roundDownToMultiple(getSpansForPx(minHeightPx))
+    }
 
     /**
      * The layout information necessary in order to calculate the pixel offsets of the drag anchor
@@ -84,37 +106,44 @@
      */
     fun setGridLayoutInfo(
         verticalItemSpacingPx: Float,
-        verticalContentPaddingPx: Float,
-        viewportHeightPx: Int,
-        maxItemSpan: Int,
-        minItemSpan: Int,
         currentRow: Int?,
-        currentSpan: Int?,
+        maxHeightPx: Int,
+        minHeightPx: Int,
+        currentSpan: Int,
+        resizeMultiple: Int,
+        totalSpans: Int,
+        viewportHeightPx: Int,
+        verticalContentPaddingPx: Float,
     ) {
-        if (currentSpan == null || currentRow == null) {
+        if (currentRow == null) {
             gridLayoutInfo.value = null
             return
         }
-        require(maxItemSpan >= minItemSpan) {
-            "Maximum item span of $maxItemSpan cannot be less than the minimum span of $minItemSpan"
+        require(maxHeightPx >= minHeightPx) {
+            "Maximum item span of $maxHeightPx cannot be less than the minimum span of $minHeightPx"
         }
-        require(minItemSpan in 1..maxItemSpan) {
-            "Minimum span must be between 1 and $maxItemSpan, but was $minItemSpan"
+
+        require(currentSpan <= totalSpans) {
+            "Current span ($currentSpan) cannot exceed the total number of spans ($totalSpans)"
         }
-        require(currentSpan % minItemSpan == 0) {
-            "Current span of $currentSpan is not a multiple of the minimum span of $minItemSpan"
+
+        require(resizeMultiple > 0) {
+            "Resize multiple ($resizeMultiple) must be a positive integer"
         }
         val availableHeight = viewportHeightPx - verticalContentPaddingPx
-        val totalSpacing = verticalItemSpacingPx * ((maxItemSpan / minItemSpan) - 1)
-        val heightPerSpanPx = (availableHeight - totalSpacing) / maxItemSpan
+        val heightPerSpanPx =
+            (availableHeight - (totalSpans - 1) * verticalItemSpacingPx) / totalSpans
+
         gridLayoutInfo.value =
             GridLayoutInfo(
-                minSpan = minItemSpan,
-                maxSpan = maxItemSpan,
                 heightPerSpanPx = heightPerSpanPx,
                 verticalItemSpacingPx = verticalItemSpacingPx,
                 currentRow = currentRow,
                 currentSpan = currentSpan,
+                maxHeightPx = maxHeightPx.coerceAtMost(availableHeight.toInt()),
+                minHeightPx = minHeightPx,
+                resizeMultiple = resizeMultiple,
+                totalSpans = totalSpans,
             )
     }
 
@@ -123,50 +152,46 @@
         layoutInfo: GridLayoutInfo?,
     ): DraggableAnchors<Int> {
 
-        if (layoutInfo == null || !isDragAllowed(handle, layoutInfo)) {
+        if (layoutInfo == null || (!isDragAllowed(handle, layoutInfo))) {
             return DraggableAnchors { 0 at 0f }
         }
-
-        val (
-            minItemSpan,
-            maxItemSpan,
-            heightPerSpanPx,
-            verticalSpacingPx,
-            currentRow,
-            currentSpan,
-        ) = layoutInfo
+        val currentRow = layoutInfo.currentRow
+        val currentSpan = layoutInfo.currentSpan
+        val minItemSpan = layoutInfo.minSpans
+        val maxItemSpan = layoutInfo.maxSpans
+        val totalSpans = layoutInfo.totalSpans
 
         // The maximum row this handle can be dragged to.
         val maxRow =
             if (handle == DragHandle.TOP) {
                 (currentRow + currentSpan - minItemSpan).coerceAtLeast(0)
             } else {
-                maxItemSpan
+                (currentRow + maxItemSpan).coerceAtMost(totalSpans)
             }
 
         // The minimum row this handle can be dragged to.
         val minRow =
             if (handle == DragHandle.TOP) {
-                0
+                (currentRow + currentSpan - maxItemSpan).coerceAtLeast(0)
             } else {
-                (currentRow + minItemSpan).coerceAtMost(maxItemSpan)
+                (currentRow + minItemSpan).coerceAtMost(totalSpans)
             }
 
         // The current row position of this handle
         val currentPosition = if (handle == DragHandle.TOP) currentRow else currentRow + currentSpan
 
         return DraggableAnchors {
-            for (targetRow in minRow..maxRow step minItemSpan) {
+            for (targetRow in minRow..maxRow step layoutInfo.resizeMultiple) {
                 val diff = targetRow - currentPosition
-                val spacing = diff / minItemSpan * verticalSpacingPx
-                diff at diff * heightPerSpanPx + spacing
+                val pixelOffset = (layoutInfo.getPxOffsetForResize(abs(diff)) * diff.sign).toFloat()
+                diff at pixelOffset
             }
         }
     }
 
     private fun isDragAllowed(handle: DragHandle, layoutInfo: GridLayoutInfo): Boolean {
-        val minItemSpan = layoutInfo.minSpan
-        val maxItemSpan = layoutInfo.maxSpan
+        val minItemSpan = layoutInfo.minSpans
+        val maxItemSpan = layoutInfo.maxSpans
         val currentRow = layoutInfo.currentRow
         val currentSpan = layoutInfo.currentSpan
         val atMinSize = currentSpan == minItemSpan
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
index 07a7c7cb..d5d3a92 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
@@ -19,7 +19,7 @@
 import android.content.Context
 import android.os.Bundle
 import android.util.SizeF
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
 import com.android.systemui.communal.widgets.CommunalAppWidgetHost
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 13b4aa9..8c14d63 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -27,14 +27,12 @@
 import android.view.WindowInsets
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
-import androidx.activity.result.ActivityResultLauncher
-import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.ui.Modifier
 import androidx.lifecycle.lifecycleScope
-import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.theme.PlatformTheme
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.Flags.communalEditWidgetsActivityFinishFix
@@ -51,6 +49,7 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
@@ -64,12 +63,15 @@
     private val uiEventLogger: UiEventLogger,
     private val widgetConfiguratorFactory: WidgetConfigurationController.Factory,
     private val widgetSection: CommunalAppWidgetSection,
+    private val userTracker: UserTracker,
     @CommunalLog logBuffer: LogBuffer,
 ) : ComponentActivity() {
     companion object {
         private const val TAG = "EditWidgetsActivity"
         private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag"
         const val EXTRA_OPEN_WIDGET_PICKER_ON_START = "open_widget_picker_on_start"
+
+        private const val REQUEST_CODE_WIDGET_PICKER = 200
     }
 
     /**
@@ -110,7 +112,7 @@
                 object : ActivityLifecycleCallbacks {
                     override fun onActivityCreated(
                         activity: Activity,
-                        savedInstanceState: Bundle?
+                        savedInstanceState: Bundle?,
                     ) {
                         waitingForResult =
                             savedInstanceState?.getBoolean(STATE_EXTRA_IS_WAITING_FOR_RESULT)
@@ -172,41 +174,6 @@
         if (communalEditWidgetsActivityFinishFix()) ActivityControllerImpl(this)
         else NopActivityController()
 
-    private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
-        registerForActivityResult(StartActivityForResult()) { result ->
-            when (result.resultCode) {
-                RESULT_OK -> {
-                    uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_WIDGET_PICKER_SHOWN)
-
-                    result.data?.let { intent ->
-                        val isPendingWidgetDrag =
-                            intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false)
-                        // Nothing to do when a widget is being dragged & dropped. The drop
-                        // target in the communal grid will receive the widget to be added (if
-                        // the user drops it over).
-                        if (!isPendingWidgetDrag) {
-                            val (componentName, user) = getWidgetExtraFromIntent(intent)
-                            if (componentName != null && user != null) {
-                                // Add widget at the end.
-                                communalViewModel.onAddWidget(
-                                    componentName,
-                                    user,
-                                    configurator = widgetConfigurator,
-                                )
-                            } else {
-                                run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
-                            }
-                        }
-                    } ?: run { Log.w(TAG, "No data in result.") }
-                }
-                else ->
-                    Log.w(
-                        TAG,
-                        "Failed to receive result from widget picker, code=${result.resultCode}"
-                    )
-            }
-        }
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
@@ -226,8 +193,7 @@
             PlatformTheme {
                 Box(
                     modifier =
-                        Modifier.fillMaxSize()
-                            .background(LocalAndroidColorScheme.current.surfaceDim),
+                        Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surfaceDim)
                 ) {
                     CommunalHub(
                         viewModel = communalViewModel,
@@ -274,7 +240,13 @@
 
     private fun onOpenWidgetPicker() {
         lifecycleScope.launch {
-            communalViewModel.onOpenWidgetPicker(resources, addWidgetActivityLauncher)
+            communalViewModel.onOpenWidgetPicker(resources) { intent: Intent ->
+                startActivityForResultAsUser(
+                    intent,
+                    REQUEST_CODE_WIDGET_PICKER,
+                    userTracker.userHandle,
+                )
+            }
         }
     }
 
@@ -285,7 +257,7 @@
             communalViewModel.changeScene(
                 scene = CommunalScenes.Communal,
                 loggingReason = "edit mode closing",
-                transitionKey = CommunalTransitionKeys.FromEditMode
+                transitionKey = CommunalTransitionKeys.FromEditMode,
             )
 
             // Wait for the current scene to be idle on communal.
@@ -309,7 +281,7 @@
         flagsMask: Int,
         flagsValues: Int,
         extraFlags: Int,
-        options: Bundle?
+        options: Bundle?,
     ) {
         activityController.onWaitingForResult(true)
         super.startIntentSenderForResult(
@@ -319,15 +291,46 @@
             flagsMask,
             flagsValues,
             extraFlags,
-            options
+            options,
         )
     }
 
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         activityController.onWaitingForResult(false)
         super.onActivityResult(requestCode, resultCode, data)
-        if (requestCode == WidgetConfigurationController.REQUEST_CODE) {
-            widgetConfigurator.setConfigurationResult(resultCode)
+
+        when (requestCode) {
+            WidgetConfigurationController.REQUEST_CODE ->
+                widgetConfigurator.setConfigurationResult(resultCode)
+            REQUEST_CODE_WIDGET_PICKER -> {
+                if (resultCode != RESULT_OK) {
+                    Log.w(TAG, "Failed to receive result from widget picker, code=$resultCode")
+                    return
+                }
+
+                uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_WIDGET_PICKER_SHOWN)
+
+                data?.let { intent ->
+                    val isPendingWidgetDrag =
+                        intent.getBooleanExtra(EXTRA_IS_PENDING_WIDGET_DRAG, false)
+                    // Nothing to do when a widget is being dragged & dropped. The drop
+                    // target in the communal grid will receive the widget to be added (if
+                    // the user drops it over).
+                    if (!isPendingWidgetDrag) {
+                        val (componentName, user) = getWidgetExtraFromIntent(intent)
+                        if (componentName != null && user != null) {
+                            // Add widget at the end.
+                            communalViewModel.onAddWidget(
+                                componentName,
+                                user,
+                                configurator = widgetConfigurator,
+                            )
+                        } else {
+                            run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+                        }
+                    }
+                } ?: run { Log.w(TAG, "No data in result.") }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 542b988..c894267 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -21,7 +21,7 @@
 import android.content.Intent
 import android.view.View
 import android.widget.RemoteViews
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Flags.communalWidgetTrampolineFix
 import com.android.systemui.animation.ActivityTransitionAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt
new file mode 100644
index 0000000..5b1c9c8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.coroutines
+
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
+import kotlin.coroutines.CoroutineContext
+
+fun newTracingContext(name: String): CoroutineContext {
+    return createCoroutineTracingContext(name) { className ->
+        className.startsWith("com.android.systemui.util.kotlin.JavaAdapter") ||
+            className.startsWith("com.android.systemui.lifecycle.RepeatWhenAttached")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index a5b2277..c6be0dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -34,6 +34,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
+import com.android.systemui.education.dagger.ContextualEducationModule;
 import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialModule;
 import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
@@ -153,6 +154,7 @@
         VolumeModule.class,
         WallpaperModule.class,
         ShortcutHelperModule.class,
+        ContextualEducationModule.class,
 })
 public abstract class ReferenceSystemUIModule {
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 8da4d46..0de919d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -50,7 +50,6 @@
 import com.android.systemui.statusbar.ImmersiveModeConfirmation
 import com.android.systemui.statusbar.gesture.GesturePointerEventListener
 import com.android.systemui.statusbar.notification.InstantAppNotifier
-import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener
 import com.android.systemui.statusbar.policy.BatteryControllerStartable
 import com.android.systemui.stylus.StylusUsiPowerStartable
@@ -288,11 +287,6 @@
 
     @Binds
     @IntoMap
-    @ClassKey(ScrimController::class)
-    abstract fun bindScrimController(impl: ScrimController): CoreStartable
-
-    @Binds
-    @IntoMap
     @ClassKey(StatusBarHeadsUpChangeListener::class)
     abstract fun bindStatusBarHeadsUpChangeListener(
         impl: StatusBarHeadsUpChangeListener
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index b55108d..450863f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -63,7 +63,6 @@
 import com.android.systemui.doze.dagger.DozeComponent;
 import com.android.systemui.dreams.dagger.DreamModule;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.education.dagger.ContextualEducationModule;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.FlagDependenciesModule;
 import com.android.systemui.flags.FlagsModule;
@@ -272,8 +271,7 @@
         UserModule.class,
         UtilModule.class,
         NoteTaskModule.class,
-        WalletModule.class,
-        ContextualEducationModule.class
+        WalletModule.class
 },
         subcomponents = {
                 ComplicationComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java
copy to packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java
index 96cff59..1950d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone.fragment.dagger;
+package com.android.systemui.dagger.qualifiers;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
-import javax.inject.Scope;
+import javax.inject.Qualifier;
 
-/**
- * Scope annotation for singleton items within the {@link StatusBarFragmentComponent}.
- */
+@Qualifier
 @Documented
 @Retention(RUNTIME)
-@Scope
-public @interface StatusBarFragmentScope {}
+public @interface Default {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index b8c03c0..c464a66 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -17,25 +17,17 @@
 package com.android.systemui.deviceentry
 
 import com.android.keyguard.EmptyLockIconViewController
-import com.android.keyguard.LegacyLockIconViewController
 import com.android.keyguard.LockIconViewController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
 import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import dagger.Lazy
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.Multibinds
 
-@Module(
-    includes =
-        [
-            DeviceEntryRepositoryModule::class,
-            FaceWakeUpTriggersConfigModule::class,
-        ],
-)
+@Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class])
 abstract class DeviceEntryModule {
     /**
      * A set of DeviceEntryIconTransitions. Ensures that this can be injected even if it's empty.
@@ -46,14 +38,9 @@
         @Provides
         @SysUISingleton
         fun provideLockIconViewController(
-            legacyLockIconViewController: Lazy<LegacyLockIconViewController>,
-            emptyLockIconViewController: Lazy<EmptyLockIconViewController>,
+            emptyLockIconViewController: Lazy<EmptyLockIconViewController>
         ): LockIconViewController {
-            return if (DeviceEntryUdfpsRefactor.isEnabled) {
-                emptyLockIconViewController.get()
-            } else {
-                legacyLockIconViewController.get()
-            }
+            return emptyLockIconViewController.get()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
index ffe392a..88daa5d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
 import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
 import javax.inject.Inject
@@ -55,9 +54,7 @@
         faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
 
     init {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            startUpdatingFaceHelpMessageDeferral()
-        }
+        startUpdatingFaceHelpMessageDeferral()
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
deleted file mode 100644
index b5d5803..0000000
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.deviceentry.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the device entry udfps refactor flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object DeviceEntryUdfpsRefactor {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
-
-    /** A token used for dependency declaration */
-    val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
-
-    /** Is the refactor enabled */
-    @JvmStatic
-    inline val isEnabled
-        get() = Flags.deviceEntryUdfpsRefactor()
-
-    /**
-     * Called to ensure code is only run when the flag is enabled. This protects users from the
-     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
-     * build to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun isUnexpectedlyInLegacyMode() =
-        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
-    /**
-     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
-     * the flag is enabled to ensure that the refactor author catches issues in testing.
-     */
-    @JvmStatic
-    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 462e820..589dbf9 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -16,16 +16,27 @@
 
 package com.android.systemui.display
 
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.display.data.repository.DeviceStateRepository
 import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
 import com.android.systemui.display.data.repository.DisplayRepository
 import com.android.systemui.display.data.repository.DisplayRepositoryImpl
+import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.display.data.repository.DisplayScopeRepositoryImpl
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
 import com.android.systemui.display.data.repository.FocusedDisplayRepository
 import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import dagger.Binds
+import dagger.Lazy
 import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
 
 /** Module binding display related classes. */
 @Module
@@ -46,4 +57,41 @@
     fun bindsFocusedDisplayRepository(
         focusedDisplayRepository: FocusedDisplayRepositoryImpl
     ): FocusedDisplayRepository
+
+    @Binds fun displayScopeRepository(impl: DisplayScopeRepositoryImpl): DisplayScopeRepository
+
+    @Binds
+    fun displayWindowPropertiesRepository(
+        impl: DisplayWindowPropertiesRepositoryImpl
+    ): DisplayWindowPropertiesRepository
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(DisplayScopeRepositoryImpl::class)
+        fun displayScopeRepoCoreStartable(
+            repoImplLazy: Lazy<DisplayScopeRepositoryImpl>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                repoImplLazy.get()
+            } else {
+                CoreStartable.NOP
+            }
+        }
+
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(DisplayWindowPropertiesRepository::class)
+        fun displayWindowPropertiesRepoAsCoreStartable(
+            repoLazy: Lazy<DisplayWindowPropertiesRepositoryImpl>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                return repoLazy.get()
+            } else {
+                CoreStartable.NOP
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
index fa7603f..1da5351 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
@@ -17,7 +17,14 @@
 package com.android.systemui.display.data.repository
 
 import android.content.Context
+import android.hardware.devicestate.DeviceState as PlatformDeviceState
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
 import android.hardware.devicestate.DeviceStateManager
+import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags
 import com.android.internal.R
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Background
@@ -34,15 +41,15 @@
     val state: StateFlow<DeviceState>
 
     enum class DeviceState {
-        /** Device state in [R.array.config_foldedDeviceStates] */
+        /** Device state that corresponds to the device being folded */
         FOLDED,
-        /** Device state in [R.array.config_halfFoldedDeviceStates] */
+        /** Device state that corresponds to the device being half-folded */
         HALF_FOLDED,
-        /** Device state in [R.array.config_openDeviceStates] */
+        /** Device state in that corresponds to the device being unfolded */
         UNFOLDED,
-        /** Device state in [R.array.config_rearDisplayDeviceStates] */
+        /** Device state that corresponds to the device being in rear display mode */
         REAR_DISPLAY,
-        /** Device state in [R.array.config_concurrentDisplayDeviceStates] */
+        /** Device state in that corresponds to the device being in concurrent display mode */
         CONCURRENT_DISPLAY,
         /** Device state in none of the other arrays. */
         UNKNOWN,
@@ -52,8 +59,8 @@
 class DeviceStateRepositoryImpl
 @Inject
 constructor(
-    context: Context,
-    deviceStateManager: DeviceStateManager,
+    val context: Context,
+    val deviceStateManager: DeviceStateManager,
     @Background bgScope: CoroutineScope,
     @Background executor: Executor
 ) : DeviceStateRepository {
@@ -70,11 +77,17 @@
             .stateIn(bgScope, started = SharingStarted.WhileSubscribed(), DeviceState.UNKNOWN)
 
     private fun deviceStateToPosture(deviceStateId: Int): DeviceState {
-        return deviceStateMap.firstOrNull { (ids, _) -> deviceStateId in ids }?.deviceState
-            ?: DeviceState.UNKNOWN
+        return if (DeviceStateManagerFlags.deviceStatePropertyMigration()) {
+            deviceStateManager.supportedDeviceStates
+                .firstOrNull { it.identifier == deviceStateId }
+                ?.toDeviceStateEnum() ?: DeviceState.UNKNOWN
+        } else {
+            deviceStateMap.firstOrNull { (ids, _) -> deviceStateId in ids }?.deviceState
+                ?: DeviceState.UNKNOWN
+        }
     }
 
-    private val deviceStateMap =
+    private val deviceStateMap: List<IdsPerDeviceState> =
         listOf(
                 R.array.config_foldedDeviceStates to DeviceState.FOLDED,
                 R.array.config_halfFoldedDeviceStates to DeviceState.HALF_FOLDED,
@@ -85,4 +98,26 @@
             .map { IdsPerDeviceState(context.resources.getIntArray(it.first).toSet(), it.second) }
 
     private data class IdsPerDeviceState(val ids: Set<Int>, val deviceState: DeviceState)
+
+    /**
+     * Maps a [PlatformDeviceState] to the corresponding [DeviceState] value based on the properties
+     * of the state.
+     */
+    private fun PlatformDeviceState.toDeviceStateEnum(): DeviceState {
+        return when {
+            hasProperty(PROPERTY_FEATURE_REAR_DISPLAY) -> DeviceState.REAR_DISPLAY
+            hasProperty(PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT) -> {
+                DeviceState.CONCURRENT_DISPLAY
+            }
+            hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY) -> DeviceState.FOLDED
+            hasProperties(
+                PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+                PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+            ) -> DeviceState.HALF_FOLDED
+            hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) -> {
+                DeviceState.UNFOLDED
+            }
+            else -> DeviceState.UNKNOWN
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index e68aba5..034cb31 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.DisplayEvent
 import com.android.systemui.util.Compile
+import com.android.systemui.util.kotlin.pairwiseBy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -41,11 +42,12 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -61,8 +63,11 @@
     /** Display addition event indicating a new display has been added. */
     val displayAdditionEvent: Flow<Display?>
 
+    /** Display removal event indicating a display has been removed. */
+    val displayRemovalEvent: Flow<Int>
+
     /** Provides the current set of displays. */
-    val displays: Flow<Set<Display>>
+    val displays: StateFlow<Set<Display>>
 
     /**
      * Pending display id that can be enabled/disabled.
@@ -79,8 +84,8 @@
      *
      * This method is guaranteed to not result in any binder call.
      */
-    suspend fun getDisplay(displayId: Int): Display? =
-        displays.first().firstOrNull { it.displayId == displayId }
+    fun getDisplay(displayId: Int): Display? =
+        displays.value.firstOrNull { it.displayId == displayId }
 
     /** Represents a connected display that has not been enabled yet. */
     interface PendingDisplay {
@@ -143,10 +148,8 @@
     override val displayChangeEvent: Flow<Int> =
         allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
 
-    override val displayAdditionEvent: Flow<Display?> =
-        allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map {
-            getDisplayFromDisplayManager(it.displayId)
-        }
+    override val displayRemovalEvent: Flow<Int> =
+        allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId }
 
     // This is necessary because there might be multiple displays, and we could
     // have missed events for those added before this process or flow started.
@@ -180,7 +183,7 @@
      *
      * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
      */
-    private val enabledDisplays: Flow<Set<Display>> =
+    private val enabledDisplays: StateFlow<Set<Display>> =
         enabledDisplayIds
             .mapElementsLazily { displayId -> getDisplayFromDisplayManager(displayId) }
             .onEach {
@@ -204,7 +207,18 @@
      *
      * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
      */
-    override val displays: Flow<Set<Display>> = enabledDisplays
+    override val displays: StateFlow<Set<Display>> = enabledDisplays
+
+    /**
+     * Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
+     * 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
+     *    calling [getDisplay] for the newly added display will be non-null.
+     * 2. Reuse the existing instance of [Display] without a new call to [DisplayManager].
+     */
+    override val displayAdditionEvent: Flow<Display?> =
+        displays
+            .pairwiseBy { previousDisplays, currentDisplays -> currentDisplays - previousDisplays }
+            .flatMapLatest { it.asFlow() }
 
     val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
     private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
new file mode 100644
index 0000000..e3fce00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.coroutines.newTracingContext
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import java.util.concurrent.ConcurrentHashMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+
+/**
+ * Provides per display instances of [CoroutineScope]. These will remain active as long as the
+ * display is connected, and automatically cancelled when the display is removed.
+ */
+interface DisplayScopeRepository {
+    fun scopeForDisplay(displayId: Int): CoroutineScope
+}
+
+@SysUISingleton
+class DisplayScopeRepositoryImpl
+@Inject
+constructor(
+    @Background private val backgroundApplicationScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val displayRepository: DisplayRepository,
+) : DisplayScopeRepository, CoreStartable {
+
+    private val perDisplayScopes = ConcurrentHashMap<Int, CoroutineScope>()
+
+    override fun scopeForDisplay(displayId: Int): CoroutineScope {
+        return perDisplayScopes.computeIfAbsent(displayId) { createScopeForDisplay(displayId) }
+    }
+
+    override fun start() {
+        StatusBarConnectedDisplays.assertInNewMode()
+        backgroundApplicationScope.launch {
+            displayRepository.displayRemovalEvent.collect { displayId ->
+                val scope = perDisplayScopes.remove(displayId)
+                scope?.cancel("Display $displayId has been removed.")
+            }
+        }
+    }
+
+    private fun createScopeForDisplay(displayId: Int): CoroutineScope {
+        return if (displayId == Display.DEFAULT_DISPLAY) {
+            // The default display is connected all the time, therefore we can optimise by reusing
+            // the application scope, and don't need to create a new scope.
+            backgroundApplicationScope
+        } else {
+            CoroutineScope(
+                backgroundDispatcher + newTracingContext("DisplayScope$displayId")
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
new file mode 100644
index 0000000..88d3a28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.Display
+import android.view.WindowManager
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.shared.model.DisplayWindowProperties
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.google.common.collect.HashBasedTable
+import com.google.common.collect.Table
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** Provides per display instances of [DisplayWindowProperties]. */
+interface DisplayWindowPropertiesRepository {
+
+    /**
+     * Returns a [DisplayWindowProperties] instance for a given display id and window type.
+     *
+     * @throws IllegalArgumentException if no display with the given display id exists.
+     */
+    fun get(
+        displayId: Int,
+        @WindowManager.LayoutParams.WindowType windowType: Int,
+    ): DisplayWindowProperties
+}
+
+@SysUISingleton
+class DisplayWindowPropertiesRepositoryImpl
+@Inject
+constructor(
+    @Background private val backgroundApplicationScope: CoroutineScope,
+    private val globalContext: Context,
+    private val globalWindowManager: WindowManager,
+    private val displayRepository: DisplayRepository,
+) : DisplayWindowPropertiesRepository, CoreStartable {
+
+    init {
+        StatusBarConnectedDisplays.assertInNewMode()
+    }
+
+    private val properties: Table<Int, Int, DisplayWindowProperties> = HashBasedTable.create()
+
+    override fun get(
+        displayId: Int,
+        @WindowManager.LayoutParams.WindowType windowType: Int,
+    ): DisplayWindowProperties {
+        val display =
+            displayRepository.getDisplay(displayId)
+                ?: throw IllegalArgumentException("Display with id $displayId doesn't exist")
+        return properties.get(displayId, windowType)
+            ?: create(display, windowType).also { properties.put(displayId, windowType, it) }
+    }
+
+    override fun start() {
+        backgroundApplicationScope.launch(
+            CoroutineName("DisplayWindowPropertiesRepositoryImpl#start")
+        ) {
+            displayRepository.displayRemovalEvent.collect { removedDisplayId ->
+                properties.row(removedDisplayId).clear()
+            }
+        }
+    }
+
+    private fun create(display: Display, windowType: Int): DisplayWindowProperties {
+        val displayId = display.displayId
+        return if (displayId == Display.DEFAULT_DISPLAY) {
+            // For the default display, we can just reuse the global/application properties.
+            // Creating a window context is expensive, therefore we avoid it.
+            DisplayWindowProperties(
+                displayId = displayId,
+                windowType = windowType,
+                context = globalContext,
+                windowManager = globalWindowManager,
+            )
+        } else {
+            val context = createWindowContext(display, windowType)
+            @SuppressLint("NonInjectedService") // Need to manually get the service
+            val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager
+            DisplayWindowProperties(displayId, windowType, context, windowManager)
+        }
+    }
+
+    private fun createWindowContext(display: Display, windowType: Int): Context =
+        globalContext.createWindowContext(display, windowType, /* options= */ null).also {
+            it.setTheme(R.style.Theme_SystemUI)
+        }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.write("perDisplayContexts: $properties")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
new file mode 100644
index 0000000..2ce3e43
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.qualifiers.Background
+import java.io.PrintWriter
+import java.util.concurrent.ConcurrentHashMap
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** Provides per display instances of [T]. */
+interface PerDisplayStore<T> {
+
+    /**
+     * The instance for the default/main display of the device. For example, on a phone or a tablet,
+     * the default display is the internal/built-in display of the device.
+     *
+     * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
+     */
+    val defaultDisplay: T
+
+    /**
+     * Returns an instance for a specific display id.
+     *
+     * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
+     *   displays.
+     */
+    fun forDisplay(displayId: Int): T
+}
+
+abstract class PerDisplayStoreImpl<T>(
+    @Background private val backgroundApplicationScope: CoroutineScope,
+    private val displayRepository: DisplayRepository,
+) : PerDisplayStore<T>, CoreStartable {
+
+    private val perDisplayInstances = ConcurrentHashMap<Int, T>()
+
+    /**
+     * The instance for the default/main display of the device. For example, on a phone or a tablet,
+     * the default display is the internal/built-in display of the device.
+     *
+     * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
+     */
+    override val defaultDisplay: T
+        get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+    /**
+     * Returns an instance for a specific display id.
+     *
+     * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
+     *   displays.
+     */
+    override fun forDisplay(displayId: Int): T {
+        if (displayRepository.getDisplay(displayId) == null) {
+            throw IllegalArgumentException("Display with id $displayId doesn't exist.")
+        }
+        return perDisplayInstances.computeIfAbsent(displayId) {
+            createInstanceForDisplay(displayId)
+        }
+    }
+
+    abstract fun createInstanceForDisplay(displayId: Int): T
+
+    override fun start() {
+        val instanceType = instanceClass.simpleName
+        backgroundApplicationScope.launch(CoroutineName("PerDisplayStore#<$instanceType>start")) {
+            displayRepository.displayRemovalEvent.collect { removedDisplayId ->
+                val removedInstance = perDisplayInstances.remove(removedDisplayId)
+                removedInstance?.let { onDisplayRemovalAction(it) }
+            }
+        }
+    }
+
+    abstract val instanceClass: Class<T>
+
+    /**
+     * Will be called when the display associated with [instance] was removed. It allows to perform
+     * any clean up if needed.
+     */
+    open suspend fun onDisplayRemovalAction(instance: T) {}
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println(perDisplayInstances)
+    }
+}
+
+class SingleDisplayStore<T>(defaultInstance: T) : PerDisplayStore<T> {
+    override val defaultDisplay: T = defaultInstance
+
+    override fun forDisplay(displayId: Int): T = defaultDisplay
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt b/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
new file mode 100644
index 0000000..6acc296
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.shared.model
+
+import android.content.Context
+import android.view.WindowManager
+
+/** Represents a display specific group of window related properties. */
+data class DisplayWindowProperties(
+    /** The id of the display associated with this instance. */
+    val displayId: Int,
+    /**
+     * The window type that was used to create the [Context] in this instance, using
+     * [Context.createWindowContext]. This is the window type that can be used when adding views to
+     * the [WindowManager] associated with this instance.
+     */
+    @WindowManager.LayoutParams.WindowType val windowType: Int,
+    /**
+     * The display specific [Context] created using [Context.createWindowContext] with window type
+     * associated with this instance.
+     */
+    val context: Context,
+
+    /**
+     * The display specific [WindowManager] instance to be used when adding windows of the type
+     * associated with this instance.
+     */
+    val windowManager: WindowManager,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt
index d5ff8f2..2b61752 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayCallbackController.kt
@@ -39,8 +39,10 @@
     }
 
     fun onWakeUp() {
-        isDreaming = false
-        callbacks.forEach { it.onWakeUp() }
+        if (isDreaming) {
+            isDreaming = false
+            callbacks.forEach { it.onWakeUp() }
+        }
     }
 
     fun onStartDream() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 83f86a7..7a6ca08 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -297,6 +297,8 @@
             mStateController.setLowLightActive(false);
             mStateController.setEntryAnimationsFinished(false);
 
+            mDreamOverlayCallbackController.onWakeUp();
+
             if (mDreamOverlayContainerViewController != null) {
                 mDreamOverlayContainerViewController.destroy();
                 mDreamOverlayContainerViewController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
index 3992c3f..724f1c5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
@@ -22,6 +22,7 @@
 import android.service.dreams.DreamService
 import android.window.TaskFragmentInfo
 import com.android.systemui.controls.settings.ControlsSettingsRepository
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dreams.DreamLogger
 import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
@@ -39,7 +40,6 @@
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 
 class HomeControlsDreamService
 @Inject
@@ -54,7 +54,8 @@
 ) : DreamService() {
 
     private val serviceJob = SupervisorJob()
-    private val serviceScope = CoroutineScope(bgDispatcher + serviceJob + createCoroutineTracingContext("HomeControlsDreamService"))
+    private val serviceScope =
+        CoroutineScope(bgDispatcher + serviceJob + newTracingContext("HomeControlsDreamService"))
     private val logger = DreamLogger(logBuffer, TAG)
     private lateinit var taskFragmentComponent: TaskFragmentComponent
     private val wakeLock: WakeLock by lazy {
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 4caf95b..abe0289 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -16,17 +16,14 @@
 
 package com.android.systemui.education.dagger
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags
-import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.education.data.repository.ContextualEducationRepository
 import com.android.systemui.education.data.repository.UserContextualEducationRepository
 import com.android.systemui.education.domain.interactor.ContextualEducationInteractor
 import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
-import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
-import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractorImpl
 import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
 import dagger.Binds
 import dagger.Lazy
@@ -57,7 +54,9 @@
         fun provideEduDataStoreScope(
             @Background bgDispatcher: CoroutineDispatcher
         ): CoroutineScope {
-            return CoroutineScope(bgDispatcher + SupervisorJob() + createCoroutineTracingContext("EduDataStoreScope"))
+            return CoroutineScope(
+                bgDispatcher + SupervisorJob() + newTracingContext("EduDataStoreScope")
+            )
         }
 
         @EduClock
@@ -81,18 +80,6 @@
         }
 
         @Provides
-        fun provideKeyboardTouchpadEduStatsInteractor(
-            implLazy: Lazy<KeyboardTouchpadEduStatsInteractorImpl>
-        ): KeyboardTouchpadEduStatsInteractor {
-            return if (Flags.keyboardTouchpadContextualEducation()) {
-                implLazy.get()
-            } else {
-                // No-op implementation when the flag is disabled.
-                return NoOpKeyboardTouchpadEduStatsInteractor
-            }
-        }
-
-        @Provides
         @IntoMap
         @ClassKey(KeyboardTouchpadEduInteractor::class)
         fun provideKeyboardTouchpadEduInteractor(
@@ -122,12 +109,6 @@
     }
 }
 
-private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
-    override fun incrementSignalCount(gestureType: GestureType) {}
-
-    override fun updateShortcutTriggerTime(gestureType: GestureType) {}
-}
-
 private object NoOpCoreStartable : CoreStartable {
     override fun start() {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
index faee326..c17f3fb 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt
@@ -18,6 +18,9 @@
 
 import android.os.SystemProperties
 import com.android.systemui.CoreStartable
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
@@ -25,6 +28,13 @@
 import com.android.systemui.education.shared.model.EducationInfo
 import com.android.systemui.education.shared.model.EducationUiType
 import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import java.time.Clock
 import javax.inject.Inject
 import kotlin.time.Duration
@@ -33,9 +43,11 @@
 import kotlin.time.toDuration
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.launch
@@ -48,6 +60,8 @@
     @Background private val backgroundScope: CoroutineScope,
     private val contextualEducationInteractor: ContextualEducationInteractor,
     private val userInputDeviceRepository: UserInputDeviceRepository,
+    private val tutorialRepository: TutorialSchedulerRepository,
+    private val overviewProxyService: OverviewProxyService,
     @EduClock private val clock: Clock,
 ) : CoreStartable {
 
@@ -59,14 +73,16 @@
             getDurationForConfig("persist.contextual_edu.usage_session_sec", 3.days)
         val minIntervalBetweenEdu =
             getDurationForConfig("persist.contextual_edu.edu_interval_sec", 7.days)
+        val initialDelayDuration =
+            getDurationForConfig("persist.contextual_edu.initial_delay_sec", 7.days)
 
         private fun getDurationForConfig(
             systemPropertyKey: String,
-            defaultDuration: Duration
+            defaultDuration: Duration,
         ): Duration =
             SystemProperties.getLong(
                     systemPropertyKey,
-                    /* defaultValue= */ defaultDuration.inWholeSeconds
+                    /* defaultValue= */ defaultDuration.inWholeSeconds,
                 )
                 .toDuration(DurationUnit.SECONDS)
     }
@@ -74,6 +90,24 @@
     private val _educationTriggered = MutableStateFlow<EducationInfo?>(null)
     val educationTriggered = _educationTriggered.asStateFlow()
 
+    private val statsUpdateRequests: Flow<StatsUpdateRequest> = conflatedCallbackFlow {
+        val listener: OverviewProxyListener =
+            object : OverviewProxyListener {
+                override fun updateContextualEduStats(
+                    isTrackpadGesture: Boolean,
+                    gestureType: GestureType,
+                ) {
+                    trySendWithFailureLogging(
+                        StatsUpdateRequest(isTrackpadGesture, gestureType),
+                        TAG,
+                    )
+                }
+            }
+
+        overviewProxyService.addCallback(listener)
+        awaitClose { overviewProxyService.removeCallback(listener) }
+    }
+
     @OptIn(ExperimentalCoroutinesApi::class)
     override fun start() {
         backgroundScope.launch {
@@ -133,6 +167,16 @@
                 contextualEducationInteractor.updateShortcutTriggerTime(it)
             }
         }
+
+        backgroundScope.launch {
+            statsUpdateRequests.collect {
+                if (it.isTrackpadGesture) {
+                    contextualEducationInteractor.updateShortcutTriggerTime(it.gestureType)
+                } else {
+                    incrementSignalCount(it.gestureType)
+                }
+            }
+        }
     }
 
     private fun isEducationNeeded(model: GestureEduModel): Boolean {
@@ -160,4 +204,41 @@
 
     private fun getEduType(model: GestureEduModel) =
         if (model.educationShownCount > 0) EducationUiType.Notification else EducationUiType.Toast
+
+    private suspend fun incrementSignalCount(gestureType: GestureType) {
+        val targetDevice = getTargetDevice(gestureType)
+        if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) {
+            contextualEducationInteractor.incrementSignalCount(gestureType)
+        }
+    }
+
+    private suspend fun isTargetDeviceConnected(deviceType: DeviceType): Boolean {
+        return when (deviceType) {
+            KEYBOARD -> userInputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected
+            TOUCHPAD -> userInputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected
+        }
+    }
+
+    /**
+     * Keyboard shortcut education would be provided for All Apps. Touchpad gesture education would
+     * be provided for the rest of the gesture types (i.e. Home, Overview, Back). This method maps
+     * gesture to its target education device.
+     */
+    private fun getTargetDevice(gestureType: GestureType) =
+        when (gestureType) {
+            ALL_APPS -> KEYBOARD
+            else -> TOUCHPAD
+        }
+
+    private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean {
+        val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false
+        return clock
+            .instant()
+            .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds))
+    }
+
+    private data class StatsUpdateRequest(
+        val isTrackpadGesture: Boolean,
+        val gestureType: GestureType,
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
deleted file mode 100644
index 43e39cf..0000000
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.education.domain.interactor
-
-import android.os.SystemProperties
-import com.android.systemui.contextualeducation.GestureType
-import com.android.systemui.contextualeducation.GestureType.ALL_APPS
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.education.dagger.ContextualEducationModule.EduClock
-import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository
-import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
-import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
-import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
-import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
-import java.time.Clock
-import javax.inject.Inject
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.days
-import kotlin.time.DurationUnit
-import kotlin.time.toDuration
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
-
-/**
- * Encapsulates the update functions of KeyboardTouchpadEduStatsInteractor. This encapsulation is
- * for having a different implementation of interactor when the feature flag is off.
- */
-interface KeyboardTouchpadEduStatsInteractor {
-    fun incrementSignalCount(gestureType: GestureType)
-
-    fun updateShortcutTriggerTime(gestureType: GestureType)
-}
-
-/** Allow update to education data related to keyboard/touchpad. */
-@SysUISingleton
-class KeyboardTouchpadEduStatsInteractorImpl
-@Inject
-constructor(
-    @Background private val backgroundScope: CoroutineScope,
-    private val contextualEducationInteractor: ContextualEducationInteractor,
-    private val inputDeviceRepository: UserInputDeviceRepository,
-    private val tutorialRepository: TutorialSchedulerRepository,
-    @EduClock private val clock: Clock,
-) : KeyboardTouchpadEduStatsInteractor {
-
-    companion object {
-        val initialDelayDuration: Duration
-            get() =
-                SystemProperties.getLong(
-                        "persist.contextual_edu.initial_delay_sec",
-                        /* defaultValue= */ 7.days.inWholeSeconds,
-                    )
-                    .toDuration(DurationUnit.SECONDS)
-    }
-
-    override fun incrementSignalCount(gestureType: GestureType) {
-        backgroundScope.launch {
-            val targetDevice = getTargetDevice(gestureType)
-            if (isTargetDeviceConnected(targetDevice) && hasInitialDelayElapsed(targetDevice)) {
-                contextualEducationInteractor.incrementSignalCount(gestureType)
-            }
-        }
-    }
-
-    override fun updateShortcutTriggerTime(gestureType: GestureType) {
-        backgroundScope.launch {
-            contextualEducationInteractor.updateShortcutTriggerTime(gestureType)
-        }
-    }
-
-    private suspend fun isTargetDeviceConnected(deviceType: DeviceType): Boolean {
-        return when (deviceType) {
-            KEYBOARD -> inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected
-            TOUCHPAD -> inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected
-        }
-    }
-
-    /**
-     * Keyboard shortcut education would be provided for All Apps. Touchpad gesture education would
-     * be provided for the rest of the gesture types (i.e. Home, Overview, Back). This method maps
-     * gesture to its target education device.
-     */
-    private fun getTargetDevice(gestureType: GestureType) =
-        when (gestureType) {
-            ALL_APPS -> KEYBOARD
-            else -> TOUCHPAD
-        }
-
-    private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean {
-        val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false
-        return clock
-            .instant()
-            .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds))
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt
index ca92953..1439ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt
@@ -22,13 +22,18 @@
 import android.view.Gravity
 import android.view.Window
 import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
 import android.widget.ImageView
 import android.widget.TextView
 import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel
 import com.android.systemui.res.R
 
-class ContextualEduDialog(context: Context, private val model: ContextualEduToastViewModel) :
-    Dialog(context) {
+class ContextualEduDialog(
+    context: Context,
+    private val model: ContextualEduToastViewModel,
+    private val accessibilityManager: AccessibilityManager,
+) : Dialog(context) {
     override fun onCreate(savedInstanceState: Bundle?) {
         setUpWindowProperties()
         setWindowPosition()
@@ -36,6 +41,7 @@
         window?.setTitle(context.getString(R.string.contextual_education_dialog_title))
         setContentView(R.layout.contextual_edu_dialog)
         setContent()
+        sendAccessibilityEvent()
         super.onCreate(savedInstanceState)
     }
 
@@ -44,10 +50,30 @@
         findViewById<ImageView>(R.id.edu_icon)?.let { it.setImageResource(model.icon) }
     }
 
+    private fun sendAccessibilityEvent() {
+        if (!accessibilityManager.isEnabled) {
+            return
+        }
+
+        // It is a toast-like dialog which is unobtrusive and not focusable. So it needs to call
+        // accessibilityManager.sendAccessibilityEvent explicitly to announce the message.
+        accessibilityManager.sendAccessibilityEvent(
+            AccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT).apply {
+                text.add(model.message)
+            }
+        )
+    }
+
     private fun setUpWindowProperties() {
         window?.apply {
             requestFeature(Window.FEATURE_NO_TITLE)
             setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG)
+            // NOT_TOUCH_MODAL allows users to interact with background elements and NOT_FOCUSABLE
+            // avoids changing the existing focus when dialog is shown.
+            addFlags(
+                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+            )
             clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
             setBackgroundDrawableResource(android.R.color.transparent)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
index 913ecdd..1996efa 100644
--- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
@@ -25,6 +25,7 @@
 import android.content.Intent
 import android.os.Bundle
 import android.os.UserHandle
+import android.view.accessibility.AccessibilityManager
 import androidx.core.app.NotificationCompat
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
@@ -64,12 +65,13 @@
         context: Context,
         viewModel: ContextualEduViewModel,
         notificationManager: NotificationManager,
+        accessibilityManager: AccessibilityManager,
     ) : this(
         applicationScope,
         viewModel,
         context,
         notificationManager,
-        createDialog = { model -> ContextualEduDialog(context, model) },
+        createDialog = { model -> ContextualEduDialog(context, model, accessibilityManager) },
     )
 
     var dialog: Dialog? = null
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index aa1873c..162047b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -135,6 +135,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.EmergencyDialerConstants;
@@ -248,7 +249,7 @@
     private final IStatusBarService mStatusBarService;
     protected final LightBarController mLightBarController;
     protected final NotificationShadeWindowController mNotificationShadeWindowController;
-    private final StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
     private final IWindowManager mIWindowManager;
     private final Executor mBackgroundExecutor;
     private final RingerModeTracker mRingerModeTracker;
@@ -364,7 +365,7 @@
             IStatusBarService statusBarService,
             LightBarController lightBarController,
             NotificationShadeWindowController notificationShadeWindowController,
-            StatusBarWindowController statusBarWindowController,
+            StatusBarWindowControllerStore statusBarWindowControllerStore,
             IWindowManager iWindowManager,
             @Background Executor backgroundExecutor,
             UiEventLogger uiEventLogger,
@@ -400,7 +401,7 @@
         mStatusBarService = statusBarService;
         mLightBarController = lightBarController;
         mNotificationShadeWindowController = notificationShadeWindowController;
-        mStatusBarWindowController = statusBarWindowController;
+        mStatusBarWindowControllerStore = statusBarWindowControllerStore;
         mIWindowManager = iWindowManager;
         mBackgroundExecutor = backgroundExecutor;
         mRingerModeTracker = ringerModeTracker;
@@ -708,7 +709,7 @@
                 mLightBarController,
                 mKeyguardStateController,
                 mNotificationShadeWindowController,
-                mStatusBarWindowController,
+                mStatusBarWindowControllerStore.getDefaultDisplay(),
                 this::onRefresh,
                 mKeyguardShowing,
                 mPowerAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt
index 58736c60..0c9fadd 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt
@@ -28,7 +28,8 @@
     override fun start() {}
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println("MSDLPlayer history of the last ${MSDLHistoryLogger.HISTORY_SIZE} events:")
+        pw.println(msdlPlayer)
+        pw.println("MSDL player history of the last ${MSDLHistoryLogger.HISTORY_SIZE} events:")
         msdlPlayer.getHistory().forEach { event -> pw.println("$event") }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt
index d2dc8c1..108d5b1 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt
@@ -17,10 +17,8 @@
 package com.android.systemui.haptics.msdl.dagger
 
 import android.annotation.SuppressLint
-import android.content.Context
-import android.os.VibratorManager
+import android.os.Vibrator
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.google.android.msdl.domain.MSDLPlayer
 import dagger.Module
 import dagger.Provides
@@ -30,9 +28,5 @@
     @SuppressLint("NonInjectedService")
     @Provides
     @SysUISingleton
-    fun provideMSDLPlayer(@Application context: Context): MSDLPlayer {
-        val vibratorManager =
-            context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
-        return MSDLPlayer.createPlayer(vibratorManager.defaultVibrator)
-    }
+    fun provideMSDLPlayer(vibrator: Vibrator?): MSDLPlayer = MSDLPlayer.createPlayer(vibrator)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt
index 2007db34..932e5af 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt
@@ -46,12 +46,24 @@
 
     private val velocityTracker = VelocityTracker.obtain()
 
+    private val dragVelocityProvider = SliderDragVelocityProvider {
+        velocityTracker.computeCurrentVelocity(
+            UNITS_SECOND,
+            sliderHapticFeedbackConfig.maxVelocityToScale,
+        )
+        if (velocityTracker.isAxisSupported(sliderHapticFeedbackConfig.velocityAxis)) {
+            velocityTracker.getAxisVelocity(sliderHapticFeedbackConfig.velocityAxis)
+        } else {
+            0f
+        }
+    }
+
     private val sliderEventProducer = SliderStateProducer()
 
     private val sliderHapticFeedbackProvider =
         SliderHapticFeedbackProvider(
             vibratorHelper,
-            velocityTracker,
+            dragVelocityProvider,
             sliderHapticFeedbackConfig,
             systemClock,
         )
@@ -188,5 +200,6 @@
 
     companion object {
         const val KEY_UP_TIMEOUT = 60L
+        private const val UNITS_SECOND = 1000
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderDragVelocityProvider.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/haptics/slider/SliderDragVelocityProvider.kt
index f4d281d..6829326 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderDragVelocityProvider.kt
@@ -14,10 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.haptics.slider
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+/** A provider of the velocity at which a slider is being dragged */
+fun interface SliderDragVelocityProvider {
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+    /**
+     * Get the velocity of the slider at the time this function is called.
+     *
+     * @return the velocity of the drag in pixels/sec
+     */
+    fun getTrackedVelocity(): Float
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
index 89bfd96..24dd04d 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
@@ -38,7 +38,7 @@
     /** Number of low ticks in a drag texture composition. This is not expected to change */
     val numberOfLowTicks: Int = 5,
     /** Maximum velocity allowed for vibration scaling. This is not expected to change. */
-    val maxVelocityToScale: Float = 2000f, /* In pixels/sec */
+    val maxVelocityToScale: Float = 2000f, /* In units/sec. The default units are pixels */
     /** Axis to use when computing velocity. Must be the same as the slider's axis of movement */
     val velocityAxis: Int = MotionEvent.AXIS_X,
     /** Vibration scale at the upper bookend of the slider */
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
index 6f28ab7..06428b7 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
@@ -38,7 +38,7 @@
  */
 class SliderHapticFeedbackProvider(
     private val vibratorHelper: VibratorHelper,
-    private val velocityTracker: VelocityTracker,
+    private val velocityProvider: SliderDragVelocityProvider,
     private val config: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(),
     private val clock: com.android.systemui.util.time.SystemClock,
 ) : SliderStateListener {
@@ -50,6 +50,7 @@
     private var dragTextureLastTime = clock.elapsedRealtime()
     var dragTextureLastProgress = -1f
         private set
+
     private val lowTickDurationMs =
         vibratorHelper.getPrimitiveDurations(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)[0]
     private var hasVibratedAtLowerBookend = false
@@ -99,7 +100,7 @@
      */
     private fun vibrateDragTexture(
         absoluteVelocity: Float,
-        @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float
+        @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float,
     ) {
         // Check if its time to vibrate
         val currentTime = clock.elapsedRealtime()
@@ -132,7 +133,7 @@
     @VisibleForTesting
     fun scaleOnDragTexture(
         absoluteVelocity: Float,
-        @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float
+        @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float,
     ): Float {
         val velocityInterpolated =
             velocityAccelerateInterpolator.getInterpolation(
@@ -162,33 +163,24 @@
 
     override fun onLowerBookend() {
         if (!hasVibratedAtLowerBookend) {
-            vibrateOnEdgeCollision(abs(getTrackedVelocity()))
+            vibrateOnEdgeCollision(abs(velocityProvider.getTrackedVelocity()))
             hasVibratedAtLowerBookend = true
         }
     }
 
     override fun onUpperBookend() {
         if (!hasVibratedAtUpperBookend) {
-            vibrateOnEdgeCollision(abs(getTrackedVelocity()))
+            vibrateOnEdgeCollision(abs(velocityProvider.getTrackedVelocity()))
             hasVibratedAtUpperBookend = true
         }
     }
 
     override fun onProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
-        vibrateDragTexture(abs(getTrackedVelocity()), progress)
+        vibrateDragTexture(abs(velocityProvider.getTrackedVelocity()), progress)
         hasVibratedAtUpperBookend = false
         hasVibratedAtLowerBookend = false
     }
 
-    private fun getTrackedVelocity(): Float {
-        velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale)
-        return if (velocityTracker.isAxisSupported(config.velocityAxis)) {
-            velocityTracker.getAxisVelocity(config.velocityAxis)
-        } else {
-            0f
-        }
-    }
-
     override fun onProgressJump(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
 
     override fun onSelectAndArrow(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
@@ -199,6 +191,5 @@
                 .setUsage(VibrationAttributes.USAGE_TOUCH)
                 .setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT)
                 .build()
-        private const val UNITS_SECOND = 1000
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
new file mode 100644
index 0000000..1dbcb3df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider.compose.ui
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.DragInteraction
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.util.VelocityTracker
+import androidx.compose.ui.unit.Velocity
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
+import com.android.systemui.haptics.slider.SliderDragVelocityProvider
+import com.android.systemui.haptics.slider.SliderEventType
+import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig
+import com.android.systemui.haptics.slider.SliderHapticFeedbackProvider
+import com.android.systemui.haptics.slider.SliderStateProducer
+import com.android.systemui.haptics.slider.SliderStateTracker
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.time.SystemClock
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.math.abs
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+
+class SliderHapticsViewModel
+@AssistedInject
+constructor(
+    @Assisted private val interactionSource: InteractionSource,
+    @Assisted private val sliderRange: ClosedFloatingPointRange<Float>,
+    @Assisted private val orientation: Orientation,
+    @Assisted private val sliderHapticFeedbackConfig: SliderHapticFeedbackConfig,
+    @Assisted private val sliderTrackerConfig: SeekableSliderTrackerConfig,
+    vibratorHelper: VibratorHelper,
+    systemClock: SystemClock,
+) : ExclusiveActivatable() {
+
+    var currentSliderEventType = SliderEventType.NOTHING
+        private set
+
+    private val velocityTracker = VelocityTracker()
+    private val maxVelocity =
+        Velocity(
+            sliderHapticFeedbackConfig.maxVelocityToScale,
+            sliderHapticFeedbackConfig.maxVelocityToScale,
+        )
+    private val dragVelocityProvider = SliderDragVelocityProvider {
+        val velocity =
+            when (orientation) {
+                Orientation.Horizontal -> velocityTracker.calculateVelocity(maxVelocity).x
+                Orientation.Vertical -> velocityTracker.calculateVelocity(maxVelocity).y
+            }
+        abs(velocity)
+    }
+
+    private var startingProgress = 0f
+
+    // Haptic slider stack of components
+    private val sliderStateProducer = SliderStateProducer()
+    private val sliderHapticFeedbackProvider =
+        SliderHapticFeedbackProvider(
+            vibratorHelper,
+            dragVelocityProvider,
+            sliderHapticFeedbackConfig,
+            systemClock,
+        )
+    private var sliderTracker: SliderStateTracker? = null
+
+    private var trackerJob: Job? = null
+
+    val isRunning: Boolean
+        get() = trackerJob?.isActive == true && sliderTracker?.isTracking == true
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            trackerJob =
+                launch("SliderHapticsViewModel#SliderStateTracker") {
+                    try {
+                        sliderTracker =
+                            SliderStateTracker(
+                                sliderHapticFeedbackProvider,
+                                sliderStateProducer,
+                                this,
+                                sliderTrackerConfig,
+                            )
+                        sliderTracker?.startTracking()
+                        awaitCancellation()
+                    } finally {
+                        sliderTracker?.stopTracking()
+                        sliderTracker = null
+                        velocityTracker.resetTracking()
+                    }
+                }
+
+            launch("SliderHapticsViewModel#InteractionSource") {
+                interactionSource.interactions.collect { interaction ->
+                    if (interaction is DragInteraction.Start) {
+                        currentSliderEventType = SliderEventType.STARTED_TRACKING_TOUCH
+                        sliderStateProducer.onStartTracking(true)
+                    }
+                }
+            }
+            awaitCancellation()
+        }
+    }
+
+    /**
+     * React to a value change in the slider.
+     *
+     * @param[value] latest value of the slider inside the [sliderRange] provided to the class
+     *   constructor.
+     */
+    fun onValueChange(value: Float) {
+        val normalized = value.normalize()
+        when (currentSliderEventType) {
+            SliderEventType.NOTHING -> {
+                currentSliderEventType = SliderEventType.STARTED_TRACKING_PROGRAM
+                startingProgress = normalized
+                sliderStateProducer.resetWithProgress(normalized)
+                sliderStateProducer.onStartTracking(false)
+            }
+            SliderEventType.STARTED_TRACKING_TOUCH -> {
+                startingProgress = normalized
+                currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER
+            }
+            SliderEventType.PROGRESS_CHANGE_BY_USER -> {
+                velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset())
+                currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER
+                sliderStateProducer.onProgressChanged(true, normalized)
+            }
+            SliderEventType.STARTED_TRACKING_PROGRAM -> {
+                startingProgress = normalized
+                currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
+            }
+            SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> {
+                velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset())
+                currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
+                sliderStateProducer.onProgressChanged(false, normalized)
+            }
+            else -> {}
+        }
+    }
+
+    fun onValueChangeEnded() {
+        when (currentSliderEventType) {
+            SliderEventType.STARTED_TRACKING_PROGRAM,
+            SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> sliderStateProducer.onStopTracking(false)
+            SliderEventType.STARTED_TRACKING_TOUCH,
+            SliderEventType.PROGRESS_CHANGE_BY_USER -> sliderStateProducer.onStopTracking(true)
+            else -> {}
+        }
+        currentSliderEventType = SliderEventType.NOTHING
+        velocityTracker.resetTracking()
+    }
+
+    private fun Float.normalize(): Float =
+        (this / (sliderRange.endInclusive - sliderRange.start)).coerceIn(0f, 1f)
+
+    private fun Float.toOffset(): Offset =
+        when (orientation) {
+            Orientation.Horizontal -> Offset(x = this - startingProgress, y = 0f)
+            Orientation.Vertical -> Offset(x = 0f, y = this - startingProgress)
+        }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            interactionSource: InteractionSource,
+            sliderRange: ClosedFloatingPointRange<Float>,
+            orientation: Orientation,
+            sliderHapticFeedbackConfig: SliderHapticFeedbackConfig,
+            sliderTrackerConfig: SeekableSliderTrackerConfig,
+        ): SliderHapticsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index 73975a0..8e01e37 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -16,24 +16,11 @@
 
 package com.android.systemui.inputdevice.tutorial.ui.composable
 
-import android.graphics.ColorFilter
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
-import androidx.annotation.RawRes
 import androidx.annotation.StringRes
-import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.snap
-import androidx.compose.animation.core.tween
 import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.background
 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
@@ -46,29 +33,20 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
-import com.airbnb.lottie.LottieProperty
-import com.airbnb.lottie.compose.LottieAnimation
-import com.airbnb.lottie.compose.LottieCompositionSpec
-import com.airbnb.lottie.compose.LottieConstants
-import com.airbnb.lottie.compose.LottieDynamicProperties
-import com.airbnb.lottie.compose.LottieDynamicProperty
-import com.airbnb.lottie.compose.animateLottieCompositionAsState
-import com.airbnb.lottie.compose.rememberLottieComposition
-import com.airbnb.lottie.compose.rememberLottieDynamicProperty
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
 
 sealed interface TutorialActionState {
     data object NotStarted : TutorialActionState
 
-    data class InProgress(val progress: Float = 0f) : TutorialActionState
+    data class InProgress(
+        val progress: Float = 0f,
+        val startMarker: String? = null,
+        val endMarker: String? = null,
+    ) : TutorialActionState
 
     data object Finished : TutorialActionState
 }
@@ -132,104 +110,3 @@
         )
     }
 }
-
-@Composable
-fun TutorialAnimation(
-    actionState: TutorialActionState,
-    config: TutorialScreenConfig,
-    modifier: Modifier = Modifier,
-) {
-    Box(modifier = modifier.fillMaxWidth()) {
-        AnimatedContent(
-            targetState = actionState,
-            transitionSpec = {
-                if (initialState == NotStarted) {
-                    val transitionDurationMillis = 150
-                    fadeIn(animationSpec = tween(transitionDurationMillis, easing = LinearEasing))
-                        .togetherWith(
-                            fadeOut(animationSpec = snap(delayMillis = transitionDurationMillis))
-                        )
-                        // we explicitly don't want size transform because when targetState
-                        // animation is loaded for the first time, AnimatedContent thinks target
-                        // size is smaller and tries to shrink initial state animation
-                        .using(sizeTransform = null)
-                } else {
-                    // empty transition works because all remaining transitions are from IN_PROGRESS
-                    // state which shares initial animation frame with both FINISHED and NOT_STARTED
-                    EnterTransition.None togetherWith ExitTransition.None
-                }
-            },
-        ) { state ->
-            when (state) {
-                NotStarted ->
-                    EducationAnimation(
-                        config.animations.educationResId,
-                        config.colors.animationColors,
-                    )
-                is InProgress ->
-                    FrozenSuccessAnimation(
-                        config.animations.successResId,
-                        config.colors.animationColors,
-                    )
-                Finished ->
-                    SuccessAnimation(config.animations.successResId, config.colors.animationColors)
-            }
-        }
-    }
-}
-
-@Composable
-private fun FrozenSuccessAnimation(
-    @RawRes successAnimationId: Int,
-    animationProperties: LottieDynamicProperties,
-) {
-    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
-    LottieAnimation(
-        composition = composition,
-        progress = { 0f }, // animation should freeze on 1st frame
-        dynamicProperties = animationProperties,
-    )
-}
-
-@Composable
-private fun EducationAnimation(
-    @RawRes educationAnimationId: Int,
-    animationProperties: LottieDynamicProperties,
-) {
-    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
-    val progress by
-        animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
-    LottieAnimation(
-        composition = composition,
-        progress = { progress },
-        dynamicProperties = animationProperties,
-    )
-}
-
-@Composable
-private fun SuccessAnimation(
-    @RawRes successAnimationId: Int,
-    animationProperties: LottieDynamicProperties,
-) {
-    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
-    val progress by animateLottieCompositionAsState(composition, iterations = 1)
-    LottieAnimation(
-        composition = composition,
-        progress = { progress },
-        dynamicProperties = animationProperties,
-    )
-}
-
-@Composable
-fun rememberColorFilterProperty(
-    layerName: String,
-    color: Color,
-): LottieDynamicProperty<ColorFilter> {
-    return rememberLottieDynamicProperty(
-        LottieProperty.COLOR_FILTER,
-        value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
-        // "**" below means match zero or more layers, so ** layerName ** means find layer with that
-        // name at any depth
-        keyPath = arrayOf("**", layerName, "**"),
-    )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/LottieHelpers.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/LottieHelpers.kt
new file mode 100644
index 0000000..94b3d9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/LottieHelpers.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.ui.composable
+
+import android.graphics.ColorFilter
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.compose.LottieDynamicProperty
+import com.airbnb.lottie.compose.rememberLottieDynamicProperty
+
+@Composable
+fun rememberColorFilterProperty(
+    layerName: String,
+    color: Color,
+): LottieDynamicProperty<ColorFilter> {
+    return rememberLottieDynamicProperty(
+        LottieProperty.COLOR_FILTER,
+        value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
+        // "**" below means match zero or more layers, so ** layerName ** means find layer with that
+        // name at any depth
+        keyPath = arrayOf("**", layerName, "**"),
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
new file mode 100644
index 0000000..ef375a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.ui.composable
+
+import androidx.annotation.RawRes
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.Ref
+import androidx.compose.ui.util.lerp
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.LottieDynamicProperties
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+
+@Composable
+fun TutorialAnimation(
+    actionState: TutorialActionState,
+    config: TutorialScreenConfig,
+    modifier: Modifier = Modifier,
+) {
+    Box(modifier = modifier.fillMaxWidth()) {
+        AnimatedContent(
+            targetState = actionState::class,
+            transitionSpec = {
+                EnterTransition.None.togetherWith(
+                        fadeOut(animationSpec = tween(durationMillis = 10, easing = LinearEasing))
+                    )
+                    // we don't want size transform because when targetState animation is loaded for
+                    // the first time, AnimatedContent thinks target size is smaller and tries to
+                    // shrink initial state
+                    .using(sizeTransform = null)
+            },
+        ) { state ->
+            when (state) {
+                NotStarted::class ->
+                    EducationAnimation(
+                        config.animations.educationResId,
+                        config.colors.animationColors,
+                    )
+                InProgress::class ->
+                    InProgressAnimation(
+                        // actionState can be already of different class while this composable is
+                        // transitioning to another one
+                        actionState as? InProgress,
+                        config.animations.educationResId,
+                        config.colors.animationColors,
+                    )
+                Finished::class ->
+                    SuccessAnimation(config.animations.successResId, config.colors.animationColors)
+            }
+        }
+    }
+}
+
+@Composable
+private fun EducationAnimation(
+    @RawRes educationAnimationId: Int,
+    animationProperties: LottieDynamicProperties,
+) {
+    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
+    val progress by
+        animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
+    LottieAnimation(
+        composition = composition,
+        progress = { progress },
+        dynamicProperties = animationProperties,
+    )
+}
+
+@Composable
+private fun SuccessAnimation(
+    @RawRes successAnimationId: Int,
+    animationProperties: LottieDynamicProperties,
+) {
+    val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
+    val progress by animateLottieCompositionAsState(composition, iterations = 1)
+    LottieAnimation(
+        composition = composition,
+        progress = { progress },
+        dynamicProperties = animationProperties,
+    )
+}
+
+@Composable
+private fun InProgressAnimation(
+    state: InProgress?,
+    @RawRes inProgressAnimationId: Int,
+    animationProperties: LottieDynamicProperties,
+) {
+    // Caching latest progress for when we're animating this view away and state is null.
+    // Without this there's jumpcut in the animation while it's animating away.
+    // state should never be null when composable appears, only when disappearing
+    val cached = remember { Ref<InProgress>() }
+    cached.value = state ?: cached.value
+    val progress = cached.value?.progress ?: 0f
+
+    val composition by
+        rememberLottieComposition(LottieCompositionSpec.RawRes(inProgressAnimationId))
+    val startProgress =
+        rememberSaveable(composition, cached.value?.startMarker) {
+            composition.progressForMarker(cached.value?.startMarker)
+        }
+    val endProgress =
+        rememberSaveable(composition, cached.value?.endMarker) {
+            composition.progressForMarker(cached.value?.endMarker)
+        }
+    LottieAnimation(
+        composition = composition,
+        progress = { lerp(start = startProgress, stop = endProgress, fraction = progress) },
+        dynamicProperties = animationProperties,
+    )
+}
+
+private fun LottieComposition?.progressForMarker(marker: String?): Float {
+    if (marker == null) return 0f
+    val startFrame = this?.getMarker(marker)?.startFrame ?: 0f
+    return this?.getProgressForFrame(startFrame) ?: 0f
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 5e05dab..3c8bb09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -24,6 +24,7 @@
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
+import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.collectIsFocusedAsState
@@ -47,6 +48,7 @@
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
@@ -80,25 +82,18 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.geometry.CornerRadius
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.key
 import androidx.compose.ui.input.key.onKeyEvent
-import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.Role
@@ -113,9 +108,9 @@
 import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachIndexed
-import androidx.compose.ui.zIndex
+import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
@@ -405,7 +400,7 @@
         if (index > 0) {
             HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainerHigh)
         }
-        ShortcutView(Modifier.padding(vertical = 24.dp), searchQuery, shortcut)
+        Shortcut(Modifier.padding(vertical = 24.dp), searchQuery, shortcut)
     }
 }
 
@@ -440,13 +435,15 @@
 
 @Composable
 private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: ShortcutCategory?) {
+    val listState = rememberLazyListState()
+    LaunchedEffect(key1 = category) { if (category != null) listState.animateScrollToItem(0) }
     if (category == null) {
         NoSearchResultsText(horizontalPadding = 24.dp, fillHeight = false)
         return
     }
-    LazyColumn(modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
-        items(items = category.subCategories, key = { item -> item.label }) {
-            SubCategoryContainerDualPane(searchQuery, it)
+    LazyColumn(modifier = modifier, state = listState) {
+        items(category.subCategories) { subcategory ->
+            SubCategoryContainerDualPane(searchQuery = searchQuery, subCategory = subcategory)
             Spacer(modifier = Modifier.height(8.dp))
         }
     }
@@ -477,14 +474,21 @@
         shape = RoundedCornerShape(28.dp),
         color = MaterialTheme.colorScheme.surfaceBright,
     ) {
-        Column(Modifier.padding(24.dp)) {
+        Column(Modifier.padding(16.dp)) {
             SubCategoryTitle(subCategory.label)
             Spacer(Modifier.height(8.dp))
             subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
                 if (index > 0) {
-                    HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainerHigh)
+                    HorizontalDivider(
+                        modifier = Modifier.padding(horizontal = 8.dp),
+                        color = MaterialTheme.colorScheme.surfaceContainerHigh,
+                    )
                 }
-                ShortcutView(Modifier.padding(vertical = 16.dp), searchQuery, shortcut)
+                Shortcut(
+                    modifier = Modifier.padding(vertical = 8.dp),
+                    searchQuery = searchQuery,
+                    shortcut = shortcut,
+                )
             }
         }
     }
@@ -500,18 +504,17 @@
 }
 
 @Composable
-private fun ShortcutView(modifier: Modifier, searchQuery: String, shortcut: Shortcut) {
+private fun Shortcut(modifier: Modifier, searchQuery: String, shortcut: ShortcutModel) {
     val interactionSource = remember { MutableInteractionSource() }
     val isFocused by interactionSource.collectIsFocusedAsState()
+    val focusColor = MaterialTheme.colorScheme.secondary
     Row(
         modifier
+            .thenIf(isFocused) {
+                Modifier.border(width = 3.dp, color = focusColor, shape = RoundedCornerShape(16.dp))
+            }
             .focusable(interactionSource = interactionSource)
-            .outlineFocusModifier(
-                isFocused = isFocused,
-                focusColor = MaterialTheme.colorScheme.secondary,
-                padding = 8.dp,
-                cornerRadius = 16.dp,
-            )
+            .padding(8.dp)
     ) {
         Row(
             modifier = Modifier.width(128.dp).align(Alignment.CenterVertically),
@@ -523,7 +526,7 @@
             }
             ShortcutDescriptionText(searchQuery = searchQuery, shortcut = shortcut)
         }
-        Spacer(modifier = Modifier.width(16.dp))
+        Spacer(modifier = Modifier.width(24.dp))
         ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut)
     }
 }
@@ -548,7 +551,7 @@
 
 @OptIn(ExperimentalLayoutApi::class)
 @Composable
-private fun ShortcutKeyCombinations(modifier: Modifier = Modifier, shortcut: Shortcut) {
+private fun ShortcutKeyCombinations(modifier: Modifier = Modifier, shortcut: ShortcutModel) {
     FlowRow(
         modifier = modifier,
         verticalArrangement = Arrangement.spacedBy(8.dp),
@@ -628,7 +631,7 @@
 @Composable
 private fun ShortcutDescriptionText(
     searchQuery: String,
-    shortcut: Shortcut,
+    shortcut: ShortcutModel,
     modifier: Modifier = Modifier,
 ) {
     Text(
@@ -751,39 +754,6 @@
     }
 }
 
-private fun Modifier.outlineFocusModifier(
-    isFocused: Boolean,
-    focusColor: Color,
-    padding: Dp,
-    cornerRadius: Dp,
-): Modifier {
-    if (isFocused) {
-        return this.drawWithContent {
-                val focusOutline =
-                    Rect(Offset.Zero, size).let {
-                        if (padding > 0.dp) {
-                            it.inflate(padding.toPx())
-                        } else {
-                            it.deflate(padding.unaryMinus().toPx())
-                        }
-                    }
-                drawContent()
-                drawRoundRect(
-                    color = focusColor,
-                    style = Stroke(width = 3.dp.toPx()),
-                    topLeft = focusOutline.topLeft,
-                    size = focusOutline.size,
-                    cornerRadius = CornerRadius(cornerRadius.toPx()),
-                )
-            }
-            // Increasing Z-Index so focus outline is drawn on top of "selected" category
-            // background.
-            .zIndex(1f)
-    } else {
-        return this
-    }
-}
-
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
 private fun TitleBar() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 6df8355..a94df09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -30,7 +30,7 @@
 import android.os.Binder
 import android.os.Bundle
 import android.util.Log
-import com.android.app.tracing.coroutines.runBlocking
+import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking
 import com.android.systemui.SystemUIAppComponentFactoryBase
 import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 2f41c0b..f549e64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -23,6 +23,7 @@
 import android.content.res.Resources
 import android.graphics.Matrix
 import android.graphics.Rect
+import android.hardware.devicestate.DeviceStateManager
 import android.os.DeadObjectException
 import android.os.Handler
 import android.os.PowerManager
@@ -56,6 +57,7 @@
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.Utils.isDeviceFoldable
 import dagger.Lazy
 import javax.inject.Inject
 
@@ -169,6 +171,7 @@
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val powerManager: PowerManager,
     private val wallpaperManager: WallpaperManager,
+    private val deviceStateManager: DeviceStateManager
 ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() {
 
     interface KeyguardUnlockAnimationListener {
@@ -489,7 +492,7 @@
                 "${!notificationShadeWindowController.isLaunchingActivity}"
         )
         Log.wtf(TAG, "  launcherUnlockController != null: ${launcherUnlockController != null}")
-        Log.wtf(TAG, "  !isFoldable(context): ${!isFoldable(resources)}")
+        Log.wtf(TAG, "  !isFoldable(context): ${!isDeviceFoldable(resources, deviceStateManager)}")
     }
 
     /**
@@ -1310,11 +1313,4 @@
         return if (fasterUnlockTransition()) UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
         else LEGACY_UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
     }
-
-    companion object {
-
-        fun isFoldable(resources: Resources): Boolean {
-            return resources.getIntArray(R.array.config_foldedDeviceStates).isNotEmpty()
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 063adc8..e79f590 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -30,21 +30,20 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.keyguard.KeyguardStatusView
 import com.android.keyguard.KeyguardStatusViewController
-import com.android.keyguard.LegacyLockIconViewController
-import com.android.keyguard.LockIconView
 import com.android.keyguard.dagger.KeyguardStatusViewComponent
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.lightRevealMigration
 import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
+import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder
 import com.android.systemui.keyguard.ui.composable.LockscreenContent
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
@@ -54,6 +53,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
 import com.android.systemui.plugins.FalsingManager
@@ -62,11 +62,13 @@
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
+import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel
 import com.google.android.msdl.domain.MSDLPlayer
 import dagger.Lazy
 import java.util.Optional
@@ -94,7 +96,6 @@
     private val configuration: ConfigurationState,
     private val context: Context,
     private val keyguardIndicationController: KeyguardIndicationController,
-    private val lockIconViewController: Lazy<LegacyLockIconViewController>,
     private val shadeInteractor: ShadeInteractor,
     private val interactionJankMonitor: InteractionJankMonitor,
     private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
@@ -109,6 +110,9 @@
     private val keyguardViewMediator: KeyguardViewMediator,
     private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+    private val lightRevealScrimViewModel: LightRevealScrimViewModel,
+    private val lightRevealScrim: LightRevealScrim,
+    private val wallpaperViewModel: WallpaperViewModel,
     @Main private val mainDispatcher: CoroutineDispatcher,
     private val msdlPlayer: MSDLPlayer,
 ) : CoreStartable {
@@ -137,6 +141,14 @@
         bindKeyguardRootView()
         initializeViews()
 
+        if (lightRevealMigration()) {
+            LightRevealScrimViewBinder.bind(
+                lightRevealScrim,
+                lightRevealScrimViewModel,
+                wallpaperViewModel,
+            )
+        }
+
         if (!SceneContainerFlag.isEnabled) {
             KeyguardBlueprintViewBinder.bind(
                 keyguardRootView,
@@ -171,10 +183,6 @@
     private fun initializeViews() {
         val indicationArea = KeyguardIndicationArea(context, null)
         keyguardIndicationController.setIndicationArea(indicationArea)
-
-        if (!DeviceEntryUdfpsRefactor.isEnabled) {
-            lockIconViewController.get().setLockIconView(LockIconView(context, null))
-        }
     }
 
     private fun bindKeyguardRootView() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2052459..fbc76c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -139,7 +139,6 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.ui.viewmodel.DreamViewModel;
 import com.android.systemui.dump.DumpManager;
@@ -2460,6 +2459,12 @@
             android.util.Log.i(TAG, "Ignoring request to dismiss (user switch in progress?)");
             return;
         }
+
+        if (mKeyguardStateController.isKeyguardGoingAway()) {
+            Log.i(TAG, "Ignoring dismiss because we're already going away.");
+            return;
+        }
+
         mHandler.obtainMessage(DISMISS, new DismissMessage(callback, message)).sendToTarget();
     }
 
@@ -3428,6 +3433,12 @@
             return;
         }
 
+        if (mIsKeyguardExitAnimationCanceled) {
+            Log.d(TAG, "Ignoring exitKeyguardAndFinishSurfaceBehindRemoteAnimation. "
+                    + "mIsKeyguardExitAnimationCanceled==true");
+            return;
+        }
+
         // Block the panel from expanding, in case we were doing a swipe to dismiss gesture.
         mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch();
         final boolean wasShowing = mShowing;
@@ -3557,9 +3568,7 @@
         }
 
         // Ensure that keyguard becomes visible if the going away animation is canceled
-        if (showKeyguard && !KeyguardWmStateRefactor.isEnabled()
-                && (MigrateClocksToBlueprint.isEnabled()
-                    || DeviceEntryUdfpsRefactor.isEnabled())) {
+        if (showKeyguard && !KeyguardWmStateRefactor.isEnabled()) {
             mKeyguardInteractor.showKeyguard();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 690ae71..b7d0d45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -24,7 +24,7 @@
 import android.os.Trace
 import android.util.Log
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.model.KeyguardState
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 0a15bbf..4c9c282 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -39,6 +39,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
@@ -65,6 +66,9 @@
 
     val isAnimating: Boolean
 
+    /** Limit the max alpha for the scrim to allow for some transparency */
+    val maxAlpha: MutableStateFlow<Float>
+
     fun startRevealAmountAnimator(reveal: Boolean, duration: Long = DEFAULT_REVEAL_DURATION)
 }
 
@@ -79,6 +83,7 @@
 ) : LightRevealScrimRepository {
     companion object {
         val TAG = LightRevealScrimRepository::class.simpleName!!
+        val DEFAULT_MAX_ALPHA = 1f
     }
 
     /** The reveal effect used if the device was locked/unlocked via the power button. */
@@ -127,6 +132,8 @@
 
     private val revealAmountAnimator = ValueAnimator.ofFloat(0f, 1f)
 
+    override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(DEFAULT_MAX_ALPHA)
+
     override val revealAmount: Flow<Float> = callbackFlow {
         val updateListener =
             Animator.AnimatorUpdateListener {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index 9626077..03cf1a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -23,15 +23,12 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.BiometricType
 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -41,7 +38,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
 
 /**
  * Encapsulates business logic for device entry events that impact the side fingerprint sensor
@@ -51,7 +47,6 @@
 class DeviceEntrySideFpsOverlayInteractor
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     @Application private val context: Context,
     deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
     private val sceneInteractor: SceneInteractor,
@@ -60,18 +55,6 @@
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
 ) {
 
-    init {
-        if (!DeviceEntryUdfpsRefactor.isEnabled) {
-            applicationScope.launch {
-                deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType ->
-                    if (sensorType == BiometricType.SIDE_FINGERPRINT) {
-                        alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
-                    }
-                }
-            }
-        }
-    }
-
     private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean
         get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
 
@@ -90,7 +73,7 @@
                 primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(),
                 // Bouncer scene visibility changes.
                 isBouncerSceneActive,
-                deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }
+                deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it },
             )
             .map {
                 isBouncerActive() &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 4cf9ec8..9896365 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -19,7 +19,7 @@
 import android.animation.ValueAnimator
 import android.util.Log
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
deleted file mode 100644
index f3bd0e9..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import android.animation.ValueAnimator
-import com.android.app.animation.Interpolators
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
-import com.android.systemui.keyguard.shared.model.DozeStateModel
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.util.kotlin.sample
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-
-@SysUISingleton
-class FromDreamingLockscreenHostedTransitionInteractor
-@Inject
-constructor(
-    override val transitionRepository: KeyguardTransitionRepository,
-    override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
-    transitionInteractor: KeyguardTransitionInteractor,
-    @Background private val scope: CoroutineScope,
-    @Background bgDispatcher: CoroutineDispatcher,
-    @Main mainDispatcher: CoroutineDispatcher,
-    keyguardInteractor: KeyguardInteractor,
-    powerInteractor: PowerInteractor,
-    keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
-) :
-    TransitionInteractor(
-        fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-        transitionInteractor = transitionInteractor,
-        mainDispatcher = mainDispatcher,
-        bgDispatcher = bgDispatcher,
-        powerInteractor = powerInteractor,
-        keyguardOcclusionInteractor = keyguardOcclusionInteractor,
-        keyguardInteractor = keyguardInteractor,
-    ) {
-
-    override fun start() {
-        if (SceneContainerFlag.isEnabled) return
-        listenForDreamingLockscreenHostedToLockscreen()
-        listenForDreamingLockscreenHostedToGone()
-        listenForDreamingLockscreenHostedToDozing()
-        listenForDreamingLockscreenHostedToOccluded()
-        listenForDreamingLockscreenHostedToPrimaryBouncer()
-    }
-
-    private fun listenForDreamingLockscreenHostedToLockscreen() {
-        scope.launch {
-            keyguardInteractor.isActiveDreamLockscreenHosted
-                // Add a slight delay to prevent transitioning to lockscreen from happening too soon
-                // as dozing can arrive in a slight gap after the lockscreen hosted dream stops.
-                .onEach { delay(50) }
-                .sample(keyguardInteractor.dozeTransitionModel, ::Pair)
-                .filterRelevantKeyguardStateAnd {
-                    (isActiveDreamLockscreenHosted, dozeTransitionModel) ->
-                    !isActiveDreamLockscreenHosted &&
-                        DozeStateModel.isDozeOff(dozeTransitionModel.to)
-                }
-                .collect { startTransitionTo(KeyguardState.LOCKSCREEN) }
-        }
-    }
-
-    private fun listenForDreamingLockscreenHostedToOccluded() {
-        scope.launch {
-            keyguardInteractor.isActiveDreamLockscreenHosted
-                .sample(keyguardInteractor.isKeyguardOccluded, ::Pair)
-                .filterRelevantKeyguardStateAnd { (isActiveDreamLockscreenHosted, isOccluded) ->
-                    isOccluded && !isActiveDreamLockscreenHosted
-                }
-                .collect { startTransitionTo(KeyguardState.OCCLUDED) }
-        }
-    }
-
-    private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
-        scope.launch {
-            keyguardInteractor.primaryBouncerShowing
-                .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
-                .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) }
-        }
-    }
-
-    private fun listenForDreamingLockscreenHostedToGone() {
-        scope.launch {
-            keyguardInteractor.biometricUnlockState
-                .filterRelevantKeyguardStateAnd { biometricUnlockState ->
-                    biometricUnlockState.mode == BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM
-                }
-                .collect { startTransitionTo(KeyguardState.GONE) }
-        }
-    }
-
-    private fun listenForDreamingLockscreenHostedToDozing() {
-        scope.launch {
-            keyguardInteractor.dozeTransitionModel
-                .filterRelevantKeyguardStateAnd { it.to == DozeStateModel.DOZE }
-                .collect { startTransitionTo(KeyguardState.DOZING) }
-        }
-    }
-
-    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
-        return ValueAnimator().apply {
-            interpolator = Interpolators.LINEAR
-            duration =
-                if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds
-                else DEFAULT_DURATION.inWholeMilliseconds
-        }
-    }
-
-    companion object {
-        private val DEFAULT_DURATION = 500.milliseconds
-        val TO_LOCKSCREEN_DURATION = 1167.milliseconds
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 9a0a858..7b75765 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -20,7 +20,7 @@
 import android.annotation.SuppressLint
 import android.app.DreamManager
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 6b6a3dce..a6f0db5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -18,7 +18,7 @@
 
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 58c8a04..a4a215f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -18,7 +18,7 @@
 
 import android.animation.ValueAnimator
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -75,7 +75,6 @@
         listenForGoneToDreaming()
         listenForGoneToLockscreenOrHubOrOccluded()
         listenForGoneToOccluded()
-        listenForGoneToDreamingLockscreenHosted()
     }
 
     fun showKeyguard() {
@@ -93,7 +92,7 @@
                 if (keyguardInteractor.isKeyguardOccluded.value) {
                     startTransitionTo(
                         KeyguardState.OCCLUDED,
-                        ownerReason = "Dismissible keyguard with occlusion"
+                        ownerReason = "Dismissible keyguard with occlusion",
                     )
                 }
             }
@@ -129,7 +128,7 @@
                             KeyguardState.LOCKSCREEN,
                             ownerReason =
                                 "Keyguard was re-enabled, and we weren't GONE when it " +
-                                    "was originally disabled"
+                                    "was originally disabled",
                         )
                     }
             }
@@ -153,23 +152,10 @@
         }
     }
 
-    private fun listenForGoneToDreamingLockscreenHosted() {
-        scope.launch("$TAG#listenForGoneToDreamingLockscreenHosted") {
-            keyguardInteractor.isActiveDreamLockscreenHosted
-                .filterRelevantKeyguardStateAnd { isActiveDreamLockscreenHosted ->
-                    isActiveDreamLockscreenHosted
-                }
-                .collect { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) }
-        }
-    }
-
     private fun listenForGoneToDreaming() {
         scope.launch("$TAG#listenForGoneToDreaming") {
             keyguardInteractor.isAbleToDream
-                .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair)
-                .filterRelevantKeyguardStateAnd { (isAbleToDream, isActiveDreamLockscreenHosted) ->
-                    isAbleToDream && !isActiveDreamLockscreenHosted
-                }
+                .filterRelevantKeyguardStateAnd { isAbleToDream -> isAbleToDream }
                 .collect { startTransitionTo(KeyguardState.DREAMING) }
         }
     }
@@ -177,7 +163,7 @@
     private fun listenForGoneToAodOrDozing() {
         scope.launch("$TAG#listenForGoneToAodOrDozing") {
             listenForSleepTransition(
-                modeOnCanceledFromStartedStep = { TransitionModeOnCanceled.RESET },
+                modeOnCanceledFromStartedStep = { TransitionModeOnCanceled.RESET }
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 6d1d9cb..e84db06 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -19,7 +19,7 @@
 import android.animation.ValueAnimator
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -135,20 +135,13 @@
                 .sampleCombine(
                     internalTransitionInteractor.currentTransitionInfoInternal,
                     transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
-                    keyguardInteractor.isActiveDreamLockscreenHosted,
                 )
-                .collect {
-                    (isAbleToDream, transitionInfo, isOnLockscreen, isActiveDreamLockscreenHosted)
-                    ->
+                .collect { (isAbleToDream, transitionInfo, isOnLockscreen) ->
                     val isTransitionInterruptible =
                         transitionInfo.to == KeyguardState.LOCKSCREEN &&
                             !invalidFromStates.contains(transitionInfo.from)
                     if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) {
-                        if (isActiveDreamLockscreenHosted) {
-                            startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
-                        } else {
-                            startTransitionTo(KeyguardState.DREAMING)
-                        }
+                        startTransitionTo(KeyguardState.DREAMING)
                     }
                 }
         }
@@ -390,7 +383,6 @@
                             TO_AOD_DURATION
                         }
                     KeyguardState.DOZING -> TO_DOZING_DURATION
-                    KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION
                     KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
@@ -402,7 +394,6 @@
         private val DEFAULT_DURATION = 400.milliseconds
         val TO_DOZING_DURATION = 500.milliseconds
         val TO_DREAMING_DURATION = 933.milliseconds
-        val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
         val TO_OCCLUDED_DURATION = 550.milliseconds
         val TO_AOD_DURATION = 500.milliseconds
         val TO_AOD_FOLD_DURATION = 1100.milliseconds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 30babe6..0ecf781 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -78,7 +78,6 @@
         listenForPrimaryBouncerToGone()
         listenForPrimaryBouncerToAsleep()
         listenForPrimaryBouncerNotShowing()
-        listenForPrimaryBouncerToDreamingLockscreenHosted()
         listenForTransitionToCamera(scope, keyguardInteractor)
     }
 
@@ -108,23 +107,19 @@
         if (KeyguardWmStateRefactor.isEnabled) {
             scope.launch {
                 keyguardInteractor.primaryBouncerShowing
-                    .sample(
-                        powerInteractor.isAwake,
-                        keyguardInteractor.isActiveDreamLockscreenHosted,
-                        communalSceneInteractor.isIdleOnCommunal,
-                    )
-                    .filterRelevantKeyguardStateAnd { (isBouncerShowing, _, _, _) ->
+                    .sample(powerInteractor.isAwake, communalSceneInteractor.isIdleOnCommunal)
+                    .filterRelevantKeyguardStateAnd { (isBouncerShowing, _, _) ->
                         // TODO(b/307976454) - See if we need to listen for SHOW_WHEN_LOCKED
                         // activities showing up over the bouncer. Camera launch can't show up over
                         // bouncer since the first power press hides bouncer. Do occluding
                         // activities auto hide bouncer? Not sure.
                         !isBouncerShowing
                     }
-                    .collect { (_, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal) ->
+                    .collect { (_, isAwake, isIdleOnCommunal) ->
                         if (
                             !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
                                 startTransitionTo(state, ownerReason = reason)
-                            } && isAwake && !isActiveDreamLockscreenHosted
+                            } && isAwake
                         ) {
                             val toState =
                                 if (isIdleOnCommunal) {
@@ -195,19 +190,6 @@
         scope.launch { listenForSleepTransition() }
     }
 
-    private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
-        if (SceneContainerFlag.isEnabled) return
-        scope.launch {
-            keyguardInteractor.primaryBouncerShowing
-                .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair)
-                .filterRelevantKeyguardStateAnd { (isBouncerShowing, isActiveDreamLockscreenHosted)
-                    ->
-                    !isBouncerShowing && isActiveDreamLockscreenHosted
-                }
-                .collect { startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED) }
-        }
-    }
-
     private fun listenForPrimaryBouncerToGone() {
         if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index d7f96b5..6ecbc61 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -297,20 +297,39 @@
     val isKeyguardVisible: Flow<Boolean> =
         combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded }
 
+    /**
+     * Event types that affect whether secure camera is active. Only used by [isSecureCameraActive].
+     */
+    private enum class SecureCameraRelatedEventType {
+        KeyguardBecameVisible,
+        PrimaryBouncerBecameVisible,
+        SecureCameraLaunched,
+    }
+
     /** Whether camera is launched over keyguard. */
-    val isSecureCameraActive: Flow<Boolean> by lazy {
-        combine(isKeyguardVisible, primaryBouncerShowing, onCameraLaunchDetected) {
-                isKeyguardVisible,
-                isPrimaryBouncerShowing,
-                cameraLaunchEvent ->
-                when {
-                    isKeyguardVisible -> false
-                    isPrimaryBouncerShowing -> false
-                    else -> cameraLaunchEvent.type == CameraLaunchType.POWER_DOUBLE_TAP
+    val isSecureCameraActive: Flow<Boolean> =
+        merge(
+                onCameraLaunchDetected
+                    .filter { it.type == CameraLaunchType.POWER_DOUBLE_TAP }
+                    .map { SecureCameraRelatedEventType.SecureCameraLaunched },
+                isKeyguardVisible
+                    .filter { it }
+                    .map { SecureCameraRelatedEventType.KeyguardBecameVisible },
+                primaryBouncerShowing
+                    .filter { it }
+                    .map { SecureCameraRelatedEventType.PrimaryBouncerBecameVisible },
+            )
+            .map {
+                when (it) {
+                    SecureCameraRelatedEventType.SecureCameraLaunched -> true
+                    // When secure camera is closed, either the keyguard or the primary bouncer will
+                    // have to show, so those events tell us that secure camera is no longer active.
+                    SecureCameraRelatedEventType.KeyguardBecameVisible -> false
+                    SecureCameraRelatedEventType.PrimaryBouncerBecameVisible -> false
                 }
             }
             .onStart { emit(false) }
-    }
+            .distinctUntilChanged()
 
     /** The approximate location on the screen of the fingerprint sensor, if one is available. */
     val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index 65b42e6..fcf486b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler.Companion.handleAction
 import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.shade.ShadeController
@@ -105,7 +106,15 @@
                 (statusBarStateController.state != StatusBarState.SHADE) &&
                 statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
         if (shouldUnlockOnMenuPressed) {
-            shadeController.animateCollapseShadeForced()
+            statusBarKeyguardViewManager.dismissWithAction(
+                object : OnDismissAction {
+                    override fun onDismiss(): Boolean {
+                        return false
+                    }
+                },
+                null,
+                false,
+            )
             return true
         }
         return false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 2c3b481..26bf26b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -22,7 +22,7 @@
 import android.content.Context
 import android.content.Intent
 import android.util.Log
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index b715333..2b4582a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -48,7 +48,6 @@
                     is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
-                    is FromDreamingLockscreenHostedTransitionInteractor -> Log.d(TAG, "Started $it")
                 }
             it.start()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index c4f231d..a0000f3 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
@@ -19,6 +19,7 @@
 
 import android.annotation.SuppressLint
 import android.util.Log
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -37,15 +38,22 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.onStart
@@ -206,44 +214,155 @@
                 )
             }
 
-        return if (SceneContainerFlag.isEnabled) {
-            flow.filter { step ->
-                val fromScene =
-                    when (edge) {
-                        is Edge.StateToState -> edge.from?.mapToSceneContainerScene()
-                        is Edge.StateToScene -> edge.from?.mapToSceneContainerScene()
-                        is Edge.SceneToState -> edge.from
+        if (!SceneContainerFlag.isEnabled) {
+            return flow
+        }
+        if (edge.isSceneWildcardEdge()) {
+            return simulateTransitionStepsForSceneTransitions(edge)
+        }
+        return flow.filter { step ->
+            val fromScene =
+                when (edge) {
+                    is Edge.StateToState -> edge.from?.mapToSceneContainerScene()
+                    is Edge.StateToScene -> edge.from?.mapToSceneContainerScene()
+                    is Edge.SceneToState -> edge.from
+                }
+
+            val toScene =
+                when (edge) {
+                    is Edge.StateToState -> edge.to?.mapToSceneContainerScene()
+                    is Edge.StateToScene -> edge.to
+                    is Edge.SceneToState -> edge.to?.mapToSceneContainerScene()
+                }
+
+            val isTransitioningBetweenLockscreenStates =
+                fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()
+            val isTransitioningBetweenDesiredScenes =
+                sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene)
+
+            // We can't compare the terminal step with the current sceneTransition because
+            // a) STL has no guarantee that it will settle in Idle() when finished/canceled
+            // b) Comparing to Idle(toScene) would make any other FINISHED step settling in
+            //    toScene pass as well
+            val terminalStepBelongsToPreviousTransition =
+                (step.transitionState == TransitionState.FINISHED ||
+                    step.transitionState == TransitionState.CANCELED) &&
+                    sceneTransitionPair.value.previousValue.isTransitioning(fromScene, toScene)
+
+            return@filter isTransitioningBetweenLockscreenStates ||
+                isTransitioningBetweenDesiredScenes ||
+                terminalStepBelongsToPreviousTransition
+        }
+    }
+
+    private fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
+
+    /**
+     * This function will return a flow that simulates TransitionSteps based on STL movements
+     * filtered by [edge].
+     *
+     * STL transitions outside of Lockscreen Transitions are not tracked in KTI. This is an issue
+     * for wildcard edges, as this means that Scenes.Bouncer -> Scenes.Gone would not appear while
+     * AOD -> Scenes.Bouncer would appear.
+     *
+     * This function will track STL transitions only when a wildcard edge is provided and emit a
+     * RUNNING step for each update to [Transition.progress]. It will also emit a STARTED and
+     * FINISHED step when the transitions starts and finishes.
+     *
+     * All TransitionSteps will have UNDEFINED as to and from state even when one of them is the
+     * Lockscreen Scene. It indicates that both are scenes but it should not be relevant to
+     * consumers of the [transition] API as usually all viewModels are just interested in the
+     * progress value. The correct filtering based on the provided [edge] is always the
+     * responsibility of KTI and therefore only proper [TransitionStep]s are emitted. The filter is
+     * applied within this function.
+     */
+    private fun simulateTransitionStepsForSceneTransitions(edge: Edge) =
+        sceneInteractor.transitionState.flatMapLatestWithFinished {
+            when (it) {
+                is ObservableTransitionState.Idle -> {
+                    flowOf()
+                }
+                is ObservableTransitionState.Transition -> {
+                    val isMatchingTransition =
+                        when (edge) {
+                            is Edge.StateToState ->
+                                throw IllegalStateException("Should not be reachable.")
+                            is Edge.SceneToState -> it.isTransitioning(from = edge.from)
+                            is Edge.StateToScene -> it.isTransitioning(to = edge.to)
+                        }
+                    if (!isMatchingTransition) {
+                        return@flatMapLatestWithFinished flowOf()
                     }
-
-                val toScene =
-                    when (edge) {
-                        is Edge.StateToState -> edge.to?.mapToSceneContainerScene()
-                        is Edge.StateToScene -> edge.to
-                        is Edge.SceneToState -> edge.to?.mapToSceneContainerScene()
+                    flow {
+                        emit(
+                            TransitionStep(
+                                from = UNDEFINED,
+                                to = UNDEFINED,
+                                value = 0f,
+                                transitionState = TransitionState.STARTED,
+                            )
+                        )
+                        emitAll(
+                            it.progress.map { progress ->
+                                TransitionStep(
+                                    from = UNDEFINED,
+                                    to = UNDEFINED,
+                                    value = progress,
+                                    transitionState = TransitionState.RUNNING,
+                                )
+                            }
+                        )
                     }
-
-                fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
-
-                val isTransitioningBetweenLockscreenStates =
-                    fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()
-                val isTransitioningBetweenDesiredScenes =
-                    sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene)
-
-                // We can't compare the terminal step with the current sceneTransition because
-                // a) STL has no guarantee that it will settle in Idle() when finished/canceled
-                // b) Comparing to Idle(toScene) would make any other FINISHED step settling in
-                //    toScene pass as well
-                val terminalStepBelongsToPreviousTransition =
-                    (step.transitionState == TransitionState.FINISHED ||
-                        step.transitionState == TransitionState.CANCELED) &&
-                        sceneTransitionPair.value.previousValue.isTransitioning(fromScene, toScene)
-
-                return@filter isTransitioningBetweenLockscreenStates ||
-                    isTransitioningBetweenDesiredScenes ||
-                    terminalStepBelongsToPreviousTransition
+                }
             }
-        } else {
-            flow
+        }
+
+    /**
+     * This function is similar to flatMapLatest but it will additionally emit a FINISHED
+     * TransitionStep whenever the flattened innerFlow emitted a STARTED step and is now being
+     * replaced by a new innerFlow.
+     *
+     * This is to make sure that every STARTED step will receive a corresponding FINISHED step.
+     *
+     * We can't simply write this into a flow {} block because Transition.progress doesn't complete.
+     * We also can't emit the FINISHED step simply when an Idle state is reached because a)
+     * Transitions are not guaranteed to finish in Idle and b) There can be multiple Idle
+     * transitions after another
+     */
+    private fun <T> Flow<T>.flatMapLatestWithFinished(
+        transform: suspend (T) -> Flow<TransitionStep>
+    ): Flow<TransitionStep> = channelFlow {
+        var job: Job? = null
+        var startedEmitted = false
+
+        coroutineScope {
+            collect { value ->
+                job?.cancelAndJoin()
+
+                job = launch {
+                    val innerFlow = transform(value)
+                    try {
+                        innerFlow.collect { step ->
+                            if (step.transitionState == TransitionState.STARTED) {
+                                startedEmitted = true
+                            }
+                            send(step)
+                        }
+                    } finally {
+                        if (startedEmitted) {
+                            send(
+                                TransitionStep(
+                                    from = UNDEFINED,
+                                    to = UNDEFINED,
+                                    value = 1f,
+                                    transitionState = TransitionState.FINISHED,
+                                )
+                            )
+                            startedEmitted = false
+                        }
+                    }
+                }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 1497026..cf747c8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:OptIn(ExperimentalCoroutinesApi::class)
 
 package com.android.systemui.keyguard.domain.interactor
 
@@ -21,17 +22,22 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_DURATION
 import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.util.kotlin.sample
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -39,7 +45,7 @@
 @Inject
 constructor(
     private val transitionInteractor: KeyguardTransitionInteractor,
-    private val lightRevealScrimRepository: LightRevealScrimRepository,
+    private val repository: LightRevealScrimRepository,
     @Application private val scope: CoroutineScope,
     private val scrimLogger: ScrimLogger,
     private val powerInteractor: Lazy<PowerInteractor>,
@@ -63,17 +69,17 @@
                         DEFAULT_REVEAL_DURATION
                     }
 
-                lightRevealScrimRepository.startRevealAmountAnimator(
+                repository.startRevealAmountAnimator(
                     willBeRevealedInState(it.to),
-                    duration = animationDuration
+                    duration = animationDuration,
                 )
             }
         }
     }
 
     private val isLastSleepDueToFold: Boolean
-        get() = powerInteractor.get().detailedWakefulness.value
-            .lastSleepReason == WakeSleepReason.FOLD
+        get() =
+            powerInteractor.get().detailedWakefulness.value.lastSleepReason == WakeSleepReason.FOLD
 
     /**
      * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
@@ -86,12 +92,29 @@
      * LiftReveal.
      */
     val lightRevealEffect: Flow<LightRevealEffect> =
-        transitionInteractor.startedKeyguardTransitionStep.sample(
-            lightRevealScrimRepository.revealEffect
-        )
+        transitionInteractor.startedKeyguardTransitionStep.sample(repository.revealEffect)
+
+    /** Limit the max alpha for the scrim to allow for some transparency */
+    val maxAlpha: Flow<Float> =
+        transitionInteractor
+            .isInTransition(
+                edge = Edge.create(Scenes.Gone, KeyguardState.AOD),
+                edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD),
+            )
+            .flatMapLatest { isInTransition ->
+                // During GONE->AOD transitions, the home screen and wallpaper are still visible
+                // until
+                // WM is told to hide them, which occurs at the end of the animation. Use an opaque
+                // scrim until this transition is complete
+                if (isInTransition) {
+                    flowOf(1f)
+                } else {
+                    repository.maxAlpha
+                }
+            }
 
     val revealAmount =
-        lightRevealScrimRepository.revealAmount.filter {
+        repository.revealAmount.filter {
             // When the screen is off we do not want to keep producing frames as this is causing
             // (invisible) jank. However, we need to still pass through 1f and 0f to ensure that the
             // correct end states are respected even if the screen turned off (or was still off)
@@ -104,7 +127,17 @@
             powerInteractor.get().screenPowerState.value != ScreenPowerState.SCREEN_TURNING_ON
 
     val isAnimating: Boolean
-        get() = lightRevealScrimRepository.isAnimating
+        get() = repository.isAnimating
+
+    /** If the wallpaper supports ambient mode, allow partial transparency */
+    fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
+        repository.maxAlpha.value =
+            if (supportsAmbientMode) {
+                0.7f
+            } else {
+                1f
+            }
+    }
 
     /**
      * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 3c66186..f976800 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -58,12 +58,6 @@
 
     @Binds
     @IntoSet
-    abstract fun fromDreamingLockscreenHosted(
-        impl: FromDreamingLockscreenHostedTransitionInteractor
-    ): TransitionInteractor
-
-    @Binds
-    @IntoSet
     abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
index fe5f632..23b7b66 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
@@ -18,7 +18,7 @@
 
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.AuthKeyguardMessageArea
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index fb97191..6ef9863 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -21,8 +21,7 @@
 import android.view.View
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -34,13 +33,7 @@
 
     /** Updates UI for the UDFPS icon on the alternate bouncer. */
     @JvmStatic
-    fun bind(
-        view: DeviceEntryIconView,
-        viewModel: AlternateBouncerUdfpsIconViewModel,
-    ) {
-        if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
-            return
-        }
+    fun bind(view: DeviceEntryIconView, viewModel: AlternateBouncerUdfpsIconViewModel) {
         val fgIconView = view.iconView
         val bgView = view.bgView
 
@@ -66,7 +59,7 @@
                 viewModel.fgViewModel.collect { fgViewModel ->
                     fgIconView.setImageState(
                         view.getIconState(fgViewModel.type, fgViewModel.useAodVariant),
-                        /* merge */ false
+                        /* merge */ false,
                     )
                     fgIconView.imageTintList = ColorStateList.valueOf(fgViewModel.tint)
                     fgIconView.setPadding(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 7696273..7a36899 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -29,11 +29,10 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
 import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
 import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
@@ -95,7 +94,7 @@
     private var alternateBouncerView: ConstraintLayout? = null
 
     override fun start() {
-        if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) {
+        if (SceneContainerFlag.isEnabled) {
             return
         }
 
@@ -182,14 +181,7 @@
     }
 
     /** Binds the view to the view-model, continuing to update the former based on the latter. */
-    fun bind(
-        view: ConstraintLayout,
-        alternateBouncerDependencies: AlternateBouncerDependencies,
-    ) {
-        if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
-            return
-        }
-
+    fun bind(view: ConstraintLayout, alternateBouncerDependencies: AlternateBouncerDependencies) {
         optionallyAddUdfpsViews(
             view = view,
             logger = alternateBouncerDependencies.logger,
@@ -287,10 +279,7 @@
                                         )
                                 }
                             view.addView(udfpsView)
-                            AlternateBouncerUdfpsViewBinder.bind(
-                                udfpsView,
-                                udfpsIconViewModel,
-                            )
+                            AlternateBouncerUdfpsViewBinder.bind(udfpsView, udfpsIconViewModel)
                         }
 
                         val constraintSet = ConstraintSet().apply { clone(view) }
@@ -310,17 +299,17 @@
                                 ConstraintSet.START,
                                 ConstraintSet.PARENT_ID,
                                 ConstraintSet.START,
-                                iconLocation.left
+                                iconLocation.left,
                             )
 
                             // udfpsA11yOverlayView:
                             constrainWidth(
                                 udfpsA11yOverlayViewId,
-                                ViewGroup.LayoutParams.MATCH_PARENT
+                                ViewGroup.LayoutParams.MATCH_PARENT,
                             )
                             constrainHeight(
                                 udfpsA11yOverlayViewId,
-                                ViewGroup.LayoutParams.MATCH_PARENT
+                                ViewGroup.LayoutParams.MATCH_PARENT,
                             )
                         }
                         constraintSet.applyTo(view)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index b951b73..6c104a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -28,9 +28,8 @@
 import androidx.core.view.isInvisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.common.ui.view.LongPressHandlingView
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
@@ -68,7 +67,6 @@
         vibratorHelper: VibratorHelper,
         overrideColor: Color? = null,
     ): DisposableHandle {
-        DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
         val disposables = DisposableHandles()
         val longPressHandlingView = view.longPressHandlingView
         val fgIconView = view.iconView
@@ -79,7 +77,7 @@
                     view: View,
                     x: Int,
                     y: Int,
-                    isA11yAction: Boolean
+                    isA11yAction: Boolean,
                 ) {
                     if (
                         !isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
@@ -87,14 +85,11 @@
                         Log.d(
                             TAG,
                             "Long press rejected because it is not a11yAction " +
-                                "and it is a falseLongTap"
+                                "and it is a falseLongTap",
                         )
                         return
                     }
-                    vibratorHelper.performHapticFeedback(
-                        view,
-                        HapticFeedbackConstants.CONFIRM,
-                    )
+                    vibratorHelper.performHapticFeedback(view, HapticFeedbackConstants.CONFIRM)
                     applicationScope.launch {
                         view.clearFocus()
                         view.clearAccessibilityFocus()
@@ -192,7 +187,7 @@
                         fgViewModel.viewModel.collect { viewModel ->
                             fgIconView.setImageState(
                                 view.getIconState(viewModel.type, viewModel.useAodVariant),
-                                /* merge */ false
+                                /* merge */ false,
                             )
                             if (viewModel.type.contentDescriptionResId != -1) {
                                 fgIconView.contentDescription =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 00aa44f..5bad016 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -22,7 +22,8 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.BaseBlueprintTransition
@@ -32,7 +33,6 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
 import com.android.systemui.util.kotlin.pairwise
 
@@ -128,7 +128,7 @@
     ) {
         val currentClock = viewModel.currentClock.value
         if (!DEBUG || currentClock == null) return
-        val smallClockViewId = R.id.lockscreen_clock_view
+        val smallClockViewId = customR.id.lockscreen_clock_view
         val largeClockViewId = currentClock.largeClock.layout.views[0].id
         val smartspaceDateId = sharedR.id.date_smartspace_view
         Log.i(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 660a650..3bdf7da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -36,7 +36,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.settingslib.Utils
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index ba94f45..8b947a3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -22,7 +22,7 @@
 import android.widget.TextView
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
index 76d3389..2fd9818 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
@@ -22,7 +22,7 @@
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.common.ui.view.LongPressHandlingView
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 57cb10f..21d1db4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -33,9 +33,9 @@
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.policy.SystemBarUtils
-import com.android.systemui.customization.R as customizationR
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection.Companion.getDimen
@@ -50,6 +50,8 @@
 
 /** Binder for the small clock view, large clock view. */
 object KeyguardPreviewClockViewBinder {
+    val lockId = View.generateViewId()
+
     @JvmStatic
     fun bind(
         largeClockHostView: View,
@@ -120,35 +122,38 @@
 
     private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) {
         constraints.apply {
-            constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT)
+            constrainWidth(customR.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT)
             // The following two lines make lockscreen_clock_view_large is constrained to available
             // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
-            constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT)
-            constrainMaxHeight(R.id.lockscreen_clock_view_large, 0)
+            constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
+            constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
             val largeClockTopMargin =
                 SystemBarUtils.getStatusBarHeight(context) +
-                    context.resources.getDimensionPixelSize(
-                        customizationR.dimen.small_clock_padding_top
-                    ) +
+                    context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
                     context.resources.getDimensionPixelSize(
                         R.dimen.keyguard_smartspace_top_offset
                     ) +
                     getDimen(context, DATE_WEATHER_VIEW_HEIGHT) +
                     getDimen(context, ENHANCED_SMARTSPACE_HEIGHT)
-            connect(R.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
-            connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START)
             connect(
-                R.id.lockscreen_clock_view_large,
+                customR.id.lockscreen_clock_view_large,
+                TOP,
+                PARENT_ID,
+                TOP,
+                largeClockTopMargin,
+            )
+            connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
+            connect(
+                customR.id.lockscreen_clock_view_large,
                 ConstraintSet.END,
                 PARENT_ID,
                 ConstraintSet.END,
             )
 
-            // In preview, we'll show UDFPS icon for UDFPS devices
-            // and nothing for non-UDFPS devices,
-            // but we need position of device entry icon to constrain clock
-            if (getConstraint(R.id.lock_icon_view) != null) {
-                connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP)
+            // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS
+            // devices, but we need position of device entry icon to constrain clock
+            if (getConstraint(lockId) != null) {
+                connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP)
             } else {
                 // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
                 val bottomPaddingPx =
@@ -159,7 +164,7 @@
                 val lockIconRadiusPx = (defaultDensity * 36).toInt()
                 val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx
                 connect(
-                    R.id.lockscreen_clock_view_large,
+                    customR.id.lockscreen_clock_view_large,
                     BOTTOM,
                     PARENT_ID,
                     BOTTOM,
@@ -167,23 +172,23 @@
                 )
             }
 
-            constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT)
+            constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT)
             constrainHeight(
-                R.id.lockscreen_clock_view,
-                context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height),
+                customR.id.lockscreen_clock_view,
+                context.resources.getDimensionPixelSize(customR.dimen.small_clock_height),
             )
             connect(
-                R.id.lockscreen_clock_view,
+                customR.id.lockscreen_clock_view,
                 START,
                 PARENT_ID,
                 START,
-                context.resources.getDimensionPixelSize(customizationR.dimen.clock_padding_start) +
+                context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
                     context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
             )
             val smallClockTopMargin =
                 context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
                     Utils.getStatusBarHeaderHeightKeyguard(context)
-            connect(R.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin)
+            connect(customR.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
index 4b75b80..baa6812 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
@@ -22,7 +22,7 @@
 import androidx.core.view.isInvisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 27dd18d..cfd6481 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,7 +30,7 @@
 import androidx.core.view.updateLayoutParams
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
 import com.android.settingslib.Utils
 import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index ee2ee52..89bca0c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -39,7 +39,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
 import com.android.keyguard.AuthInteractionProperties
@@ -52,8 +52,8 @@
 import com.android.systemui.common.ui.view.onApplyWindowInsets
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.common.ui.view.onTouchListener
+import com.android.systemui.customization.R as customR
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
@@ -180,16 +180,12 @@
                         }
                     }
 
-                    if (
-                        KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled
-                    ) {
-                        launch("$TAG#alpha") {
-                            viewModel.alpha(viewState).collect { alpha ->
-                                view.alpha = alpha
-                                if (KeyguardBottomAreaRefactor.isEnabled) {
-                                    childViews[statusViewId]?.alpha = alpha
-                                    childViews[burnInLayerId]?.alpha = alpha
-                                }
+                    launch("$TAG#alpha") {
+                        viewModel.alpha(viewState).collect { alpha ->
+                            view.alpha = alpha
+                            if (KeyguardBottomAreaRefactor.isEnabled) {
+                                childViews[statusViewId]?.alpha = alpha
+                                childViews[burnInLayerId]?.alpha = alpha
                             }
                         }
                     }
@@ -223,7 +219,6 @@
                                                 indicationArea,
                                                 startButton,
                                                 endButton,
-                                                lockIcon,
                                                 deviceEntryIcon -> {
                                                     // Do not move these views
                                                 }
@@ -622,12 +617,11 @@
     private val statusViewId = R.id.keyguard_status_view
     private val burnInLayerId = R.id.burn_in_layer
     private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
-    private val largeClockId = R.id.lockscreen_clock_view_large
-    private val smallClockId = R.id.lockscreen_clock_view
+    private val largeClockId = customR.id.lockscreen_clock_view_large
+    private val smallClockId = customR.id.lockscreen_clock_view
     private val indicationArea = R.id.keyguard_indication_area
     private val startButton = R.id.start_button
     private val endButton = R.id.end_button
-    private val lockIcon = R.id.lock_icon_view
     private val deviceEntryIcon = R.id.device_entry_icon_view
     private val nsslPlaceholderId = R.id.nssl_placeholder
     private val authInteractionProperties = AuthInteractionProperties()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 4150ceb..79360370 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -22,7 +22,7 @@
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.common.ui.binder.TextViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 8b74f5d..de4a1b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -21,7 +21,7 @@
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt
index fd27dc3..3934917 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.keyguard.ui.binder
 
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
index b2ee689..32757ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
@@ -16,21 +16,49 @@
 
 package com.android.systemui.keyguard.ui.binder
 
+import android.animation.ValueAnimator
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shared.Flags.ambientAod
 import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel
 import kotlinx.coroutines.launch
 
 object LightRevealScrimViewBinder {
     @JvmStatic
-    fun bind(revealScrim: LightRevealScrim, viewModel: LightRevealScrimViewModel) {
+    fun bind(
+        revealScrim: LightRevealScrim,
+        viewModel: LightRevealScrimViewModel,
+        wallpaperViewModel: WallpaperViewModel,
+    ) {
         revealScrim.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
+                if (ambientAod()) {
+                    launch("$TAG#wallpaperViewModel.wallpaperSupportsAmbientMode") {
+                        wallpaperViewModel.wallpaperSupportsAmbientMode.collect {
+                            viewModel.setWallpaperSupportsAmbientMode(it)
+                        }
+                    }
+                    launch("$TAG#viewModel.maxAlpha") {
+                        viewModel.maxAlpha.collect { alpha ->
+                            if (alpha != revealScrim.alpha) {
+                                ValueAnimator.ofFloat(revealScrim.alpha, alpha).apply {
+                                    duration = 400
+                                    addUpdateListener { animation ->
+                                        revealScrim.alpha = animation.getAnimatedValue() as Float
+                                    }
+                                    start()
+                                }
+                            }
+                        }
+                    }
+                }
+
                 launch("$TAG#viewModel.revealAmount") {
-                    viewModel.revealAmount.collect { amount -> revealScrim.revealAmount = amount }
+                    viewModel.revealAmount.collect { revealScrim.revealAmount = it }
                 }
 
                 launch("$TAG#viewModel.lightRevealEffect") {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt
index ae46dd3..b1ce47e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.keyguard.ui.binder
 
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
 import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index cef9a4e..08d35a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.keyguard.ui.preview
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.app.WallpaperColors
 import android.content.BroadcastReceiver
 import android.content.Context
@@ -57,6 +56,7 @@
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder
 import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -151,10 +151,7 @@
     private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
     private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT)
     private val shouldHighlightSelectedAffordance: Boolean =
-        bundle.getBoolean(
-            KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
-            false,
-        )
+        bundle.getBoolean(KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES, false)
 
     private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY)
     private val display: Display? = displayManager.getDisplay(displayId)
@@ -188,24 +185,26 @@
     private var themeStyle: Style? = null
 
     init {
-        coroutineScope = CoroutineScope(applicationScope.coroutineContext + Job() + createCoroutineTracingContext("KeyguardPreviewRenderer"))
+        coroutineScope =
+            CoroutineScope(
+                applicationScope.coroutineContext +
+                    Job() +
+                    newTracingContext("KeyguardPreviewRenderer")
+            )
         disposables += DisposableHandle { coroutineScope.cancel() }
         clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
 
         if (KeyguardBottomAreaRefactor.isEnabled) {
             quickAffordancesCombinedViewModel.enablePreviewMode(
                 initiallySelectedSlotId =
-                    bundle.getString(
-                        KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
-                    ) ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                    bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID)
+                        ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
                 shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
             )
         } else {
             bottomAreaViewModel.enablePreviewMode(
                 initiallySelectedSlotId =
-                    bundle.getString(
-                        KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
-                    ),
+                    bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID),
                 shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
             )
         }
@@ -218,7 +217,7 @@
                     context,
                     displayManager.getDisplay(DEFAULT_DISPLAY),
                     if (hostToken == null) null else InputTransferToken(hostToken),
-                    "KeyguardPreviewRenderer"
+                    "KeyguardPreviewRenderer",
                 )
             disposables += DisposableHandle { host.release() }
         }
@@ -247,12 +246,12 @@
             rootView.measure(
                 View.MeasureSpec.makeMeasureSpec(
                     displayInfo?.logicalWidth ?: windowManager.currentWindowMetrics.bounds.width(),
-                    View.MeasureSpec.EXACTLY
+                    View.MeasureSpec.EXACTLY,
                 ),
                 View.MeasureSpec.makeMeasureSpec(
                     displayInfo?.logicalHeight
                         ?: windowManager.currentWindowMetrics.bounds.height(),
-                    View.MeasureSpec.EXACTLY
+                    View.MeasureSpec.EXACTLY,
                 ),
             )
             rootView.layout(0, 0, rootView.measuredWidth, rootView.measuredHeight)
@@ -278,9 +277,7 @@
         }
     }
 
-    fun onStartCustomizingQuickAffordances(
-        initiallySelectedSlotId: String?,
-    ) {
+    fun onStartCustomizingQuickAffordances(initiallySelectedSlotId: String?) {
         quickAffordancesCombinedViewModel.enablePreviewMode(
             initiallySelectedSlotId = initiallySelectedSlotId,
             shouldHighlightSelectedAffordance = true,
@@ -379,15 +376,9 @@
     @Deprecated("Deprecated as part of b/278057014")
     private fun setUpBottomArea(parentView: ViewGroup) {
         val bottomAreaView =
-            LayoutInflater.from(context)
-                .inflate(
-                    R.layout.keyguard_bottom_area,
-                    parentView,
-                    false,
-                ) as KeyguardBottomAreaView
-        bottomAreaView.init(
-            viewModel = bottomAreaViewModel,
-        )
+            LayoutInflater.from(context).inflate(R.layout.keyguard_bottom_area, parentView, false)
+                as KeyguardBottomAreaView
+        bottomAreaView.init(viewModel = bottomAreaViewModel)
         parentView.addView(
             bottomAreaView,
             FrameLayout.LayoutParams(
@@ -433,7 +424,7 @@
 
         setUpUdfps(
             previewContext,
-            if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView
+            if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView,
         )
 
         if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -466,7 +457,7 @@
                 previewContext,
                 it,
                 previewInSplitShade(),
-                smartspaceViewModel
+                smartspaceViewModel,
             )
         }
         setupCommunalTutorialIndicator(keyguardRootView)
@@ -515,23 +506,20 @@
 
         val finger =
             LayoutInflater.from(previewContext)
-                .inflate(
-                    R.layout.udfps_keyguard_preview,
-                    parentView,
-                    false,
-                ) as View
+                .inflate(R.layout.udfps_keyguard_preview, parentView, false) as View
 
         // Place the UDFPS view in the proper sensor location
         if (MigrateClocksToBlueprint.isEnabled) {
-            finger.id = R.id.lock_icon_view
+            val lockId = KeyguardPreviewClockViewBinder.lockId
+            finger.id = lockId
             parentView.addView(finger)
             val cs = ConstraintSet()
             cs.clone(parentView as ConstraintLayout)
             cs.apply {
-                constrainWidth(R.id.lock_icon_view, sensorBounds.width())
-                constrainHeight(R.id.lock_icon_view, sensorBounds.height())
-                connect(R.id.lock_icon_view, TOP, PARENT_ID, TOP, sensorBounds.top)
-                connect(R.id.lock_icon_view, START, PARENT_ID, START, sensorBounds.left)
+                constrainWidth(lockId, sensorBounds.width())
+                constrainHeight(lockId, sensorBounds.height())
+                connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top)
+                connect(lockId, START, PARENT_ID, START, sensorBounds.left)
             }
             cs.applyTo(parentView)
         } else {
@@ -541,7 +529,7 @@
                 sensorBounds.left,
                 sensorBounds.top,
                 sensorBounds.right,
-                sensorBounds.bottom
+                sensorBounds.bottom,
             )
             parentView.addView(finger, fingerprintLayoutParams)
         }
@@ -565,7 +553,7 @@
                     FrameLayout.LayoutParams.WRAP_CONTENT,
                     resources.getDimensionPixelSize(
                         com.android.systemui.customization.R.dimen.small_clock_height
-                    )
+                    ),
                 )
             layoutParams.topMargin =
                 SystemBarUtils.getStatusBarHeight(previewContext) +
@@ -579,7 +567,7 @@
                 ),
                 /* top = */ 0,
                 /* end = */ 0,
-                /* bottom = */ 0
+                /* bottom = */ 0,
             )
             smallClockHostView.clipChildren = false
             parentView.addView(smallClockHostView)
@@ -703,9 +691,7 @@
     private suspend fun fetchThemeStyleFromSetting(): Style {
         val overlayPackageJson =
             withContext(backgroundDispatcher) {
-                secureSettings.getString(
-                    Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
-                )
+                secureSettings.getString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)
             }
         return if (!overlayPackageJson.isNullOrEmpty()) {
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index 075a1d2..9355200 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -25,7 +25,7 @@
 import android.util.ArrayMap
 import android.util.Log
 import androidx.annotation.VisibleForTesting
-import com.android.app.tracing.coroutines.runBlocking
+import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 0b10c8a..ee4f41d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -30,7 +30,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
 import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
-import com.android.systemui.customization.R as custR
+import com.android.systemui.customization.R as customR
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -54,6 +54,9 @@
 internal fun ConstraintSet.setVisibility(views: Iterable<View>, visibility: Int) =
     views.forEach { view -> this.setVisibility(view.id, visibility) }
 
+internal fun ConstraintSet.setAlpha(views: Iterable<View>, alpha: Float) =
+    views.forEach { view -> this.setAlpha(view.id, alpha) }
+
 internal fun ConstraintSet.setScaleX(views: Iterable<View>, scaleX: Float) =
     views.forEach { view -> this.setScaleX(view.id, scaleX) }
 
@@ -125,6 +128,8 @@
         return constraintSet.apply {
             setVisibility(getTargetClockFace(clock).views, VISIBLE)
             setVisibility(getNonTargetClockFace(clock).views, GONE)
+            setAlpha(getTargetClockFace(clock).views, 1F)
+            setAlpha(getNonTargetClockFace(clock).views, 0F)
             if (!keyguardClockViewModel.isLargeClockVisible.value) {
                 connect(sharedR.id.bc_smartspace_view, TOP, sharedR.id.date_smartspace_view, BOTTOM)
             } else {
@@ -148,7 +153,7 @@
                 R.id.weather_clock_bc_smartspace_bottom,
                 Barrier.BOTTOM,
                 getDimen(ENHANCED_SMARTSPACE_HEIGHT),
-                (custR.id.weather_clock_time),
+                (customR.id.weather_clock_time),
             )
             if (
                 rootViewModel.isNotifIconContainerVisible.value.value &&
@@ -179,40 +184,40 @@
             if (keyguardClockViewModel.clockShouldBeCentered.value) PARENT_ID
             else R.id.split_shade_guideline
         constraints.apply {
-            connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START)
-            connect(R.id.lockscreen_clock_view_large, END, guideline, END)
-            connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP)
+            connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
+            connect(customR.id.lockscreen_clock_view_large, END, guideline, END)
+            connect(customR.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP)
             val largeClockTopMargin =
                 keyguardClockViewModel.getLargeClockTopMargin() +
                     getDimen(DATE_WEATHER_VIEW_HEIGHT) +
                     getDimen(ENHANCED_SMARTSPACE_HEIGHT)
-            connect(R.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
-            constrainWidth(R.id.lockscreen_clock_view_large, WRAP_CONTENT)
+            connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
+            constrainWidth(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
 
             // The following two lines make lockscreen_clock_view_large is constrained to available
             // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT
-            constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT)
-            constrainMaxHeight(R.id.lockscreen_clock_view_large, 0)
-            constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT)
+            constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT)
+            constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
+            constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT)
             constrainHeight(
-                R.id.lockscreen_clock_view,
-                context.resources.getDimensionPixelSize(custR.dimen.small_clock_height),
+                customR.id.lockscreen_clock_view,
+                context.resources.getDimensionPixelSize(customR.dimen.small_clock_height),
             )
             connect(
-                R.id.lockscreen_clock_view,
+                customR.id.lockscreen_clock_view,
                 START,
                 PARENT_ID,
                 START,
-                context.resources.getDimensionPixelSize(custR.dimen.clock_padding_start) +
+                context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
                     context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
             )
             val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
             create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
             setGuidelineBegin(R.id.small_clock_guideline_top, smallClockTopMargin)
-            connect(R.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
+            connect(customR.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
 
             // Explicitly clear pivot to force recalculate pivot instead of using legacy value
-            setTransformPivot(R.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
+            setTransformPivot(customR.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
 
             val smallClockBottom =
                 keyguardClockViewModel.getSmallClockTopMargin() +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 782d37b..8d2bfb5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -27,11 +27,8 @@
 import androidx.annotation.VisibleForTesting
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
-import com.android.keyguard.LockIconView
-import com.android.keyguard.LockIconViewController
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -66,7 +63,6 @@
     private val context: Context,
     private val notificationPanelView: NotificationPanelView,
     private val featureFlags: FeatureFlags,
-    private val lockIconViewController: Lazy<LockIconViewController>,
     private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
     private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
     private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
@@ -78,70 +74,44 @@
     private var disposableHandle: DisposableHandle? = null
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (
-            !KeyguardBottomAreaRefactor.isEnabled &&
-                !MigrateClocksToBlueprint.isEnabled &&
-                !DeviceEntryUdfpsRefactor.isEnabled
-        ) {
+        if (!KeyguardBottomAreaRefactor.isEnabled && !MigrateClocksToBlueprint.isEnabled) {
             return
         }
 
-        notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
-            notificationPanelView.removeView(it)
-        }
-
         val view =
-            if (DeviceEntryUdfpsRefactor.isEnabled) {
-                DeviceEntryIconView(
-                        context,
-                        null,
-                        logger =
-                            LongPressHandlingViewLogger(
-                                logBuffer = logBuffer,
-                                TAG
-                            )
-                    )
-                    .apply { id = deviceEntryIconViewId }
-            } else {
-                // KeyguardBottomAreaRefactor.isEnabled or MigrateClocksToBlueprint.isEnabled
-                LockIconView(context, null).apply { id = R.id.lock_icon_view }
-            }
+            DeviceEntryIconView(
+                    context,
+                    null,
+                    logger = LongPressHandlingViewLogger(logBuffer = logBuffer, TAG),
+                )
+                .apply { id = deviceEntryIconViewId }
+
         constraintLayout.addView(view)
     }
 
     override fun bindData(constraintLayout: ConstraintLayout) {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
-                disposableHandle?.dispose()
-                disposableHandle =
-                    DeviceEntryIconViewBinder.bind(
-                        applicationScope,
-                        it,
-                        deviceEntryIconViewModel.get(),
-                        deviceEntryForegroundViewModel.get(),
-                        deviceEntryBackgroundViewModel.get(),
-                        falsingManager.get(),
-                        vibratorHelper.get(),
-                    )
-            }
-        } else {
-            constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let {
-                lockIconViewController.get().setLockIconView(it)
-            }
+        constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
+            disposableHandle?.dispose()
+            disposableHandle =
+                DeviceEntryIconViewBinder.bind(
+                    applicationScope,
+                    it,
+                    deviceEntryIconViewModel.get(),
+                    deviceEntryForegroundViewModel.get(),
+                    deviceEntryBackgroundViewModel.get(),
+                    falsingManager.get(),
+                    vibratorHelper.get(),
+                )
         }
     }
 
     override fun applyConstraints(constraintSet: ConstraintSet) {
-        val isUdfpsSupported =
-            if (DeviceEntryUdfpsRefactor.isEnabled) {
-                Log.d(
-                    "DefaultDeviceEntrySection",
-                    "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}"
-                )
-                deviceEntryIconViewModel.get().isUdfpsSupported.value
-            } else {
-                authController.isUdfpsSupported
-            }
+        Log.d(
+            "DefaultDeviceEntrySection",
+            "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}",
+        )
+        val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
+
         val scaleFactor: Float = authController.scaleFactor
         val mBottomPaddingPx =
             context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
@@ -160,31 +130,24 @@
         val iconRadiusPx = (defaultDensity * 36).toInt()
 
         if (isUdfpsSupported) {
-            if (DeviceEntryUdfpsRefactor.isEnabled) {
-                deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation ->
-                    Log.d(
-                        "DeviceEntrySection",
-                        "udfpsLocation=$udfpsLocation, " +
-                            "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " +
-                            "unusedAuthController=${authController.udfpsLocation}"
-                    )
-                    centerIcon(
-                        Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()),
-                        udfpsLocation.radius,
-                        constraintSet
-                    )
-                }
-            } else {
-                authController.udfpsLocation?.let { udfpsLocation ->
-                    Log.d("DeviceEntrySection", "udfpsLocation=$udfpsLocation")
-                    centerIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
-                }
+            deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation ->
+                Log.d(
+                    "DeviceEntrySection",
+                    "udfpsLocation=$udfpsLocation, " +
+                        "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " +
+                        "unusedAuthController=${authController.udfpsLocation}",
+                )
+                centerIcon(
+                    Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()),
+                    udfpsLocation.radius,
+                    constraintSet,
+                )
             }
         } else {
             centerIcon(
                 Point(
                     (widthPixels / 2).toInt(),
-                    (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt()
+                    (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt(),
                 ),
                 iconRadiusPx * scaleFactor,
                 constraintSet,
@@ -193,12 +156,8 @@
     }
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
-        if (DeviceEntryUdfpsRefactor.isEnabled) {
-            constraintLayout.removeView(deviceEntryIconViewId)
-            disposableHandle?.dispose()
-        } else {
-            constraintLayout.removeView(R.id.lock_icon_view)
-        }
+        constraintLayout.removeView(deviceEntryIconViewId)
+        disposableHandle?.dispose()
     }
 
     @VisibleForTesting
@@ -213,12 +172,7 @@
                 )
             }
 
-        val iconId =
-            if (DeviceEntryUdfpsRefactor.isEnabled) {
-                deviceEntryIconViewId
-            } else {
-                R.id.lock_icon_view
-            }
+        val iconId = deviceEntryIconViewId
 
         constraintSet.apply {
             constrainWidth(iconId, sensorRect.right - sensorRect.left)
@@ -228,14 +182,14 @@
                 ConstraintSet.TOP,
                 ConstraintSet.PARENT_ID,
                 ConstraintSet.TOP,
-                sensorRect.top
+                sensorRect.top,
             )
             connect(
                 iconId,
                 ConstraintSet.START,
                 ConstraintSet.PARENT_ID,
                 ConstraintSet.START,
-                sensorRect.left
+                sensorRect.left,
             )
         }
 
@@ -243,8 +197,8 @@
         // Without this logic, the lock icon location changes but the KeyguardBottomAreaView is not
         // updated and visible ui layout jank occurs. This is due to AmbientIndicationContainer
         // being in NPVC and laying out prior to the KeyguardRootView.
-        // Remove when both DeviceEntryUdfpsRefactor and KeyguardBottomAreaRefactor are enabled.
-        if (DeviceEntryUdfpsRefactor.isEnabled && !KeyguardBottomAreaRefactor.isEnabled) {
+        // Remove when KeyguardBottomAreaRefactor is enabled.
+        if (!KeyguardBottomAreaRefactor.isEnabled) {
             with(notificationPanelView) {
                 val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
                 val bottomAreaViewRight = findViewById<View>(R.id.keyguard_bottom_area)?.right ?: 0
@@ -256,7 +210,7 @@
                             ambientLeft,
                             sensorRect.bottom,
                             bottomAreaViewRight - ambientLeft,
-                            ambientTop + it.measuredHeight
+                            ambientTop + it.measuredHeight,
                         )
                     } else {
                         // make bottom of ambient indication view the top of the lock icon
@@ -264,7 +218,7 @@
                             ambientLeft,
                             sensorRect.top - it.measuredHeight,
                             bottomAreaViewRight - ambientLeft,
-                            sensorRect.top
+                            sensorRect.top,
                         )
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
index b33d552..604318a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
@@ -22,6 +22,7 @@
 import androidx.constraintlayout.widget.Barrier
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.res.R
@@ -67,7 +68,7 @@
             connect(
                 R.id.keyguard_slice_view,
                 ConstraintSet.TOP,
-                R.id.lockscreen_clock_view,
+                customR.id.lockscreen_clock_view,
                 ConstraintSet.BOTTOM
             )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index edcf97a..620cc13 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -55,11 +55,7 @@
                 R.id.nssl_placeholder_barrier_bottom,
                 Barrier.TOP,
                 0,
-                *intArrayOf(
-                    R.id.device_entry_icon_view,
-                    R.id.lock_icon_view,
-                    R.id.ambient_indication_container
-                )
+                *intArrayOf(R.id.device_entry_icon_view, R.id.ambient_indication_container),
             )
             connect(placeHolderId, BOTTOM, R.id.nssl_placeholder_barrier_bottom, TOP)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 99160f8..6ddcae3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -23,6 +23,7 @@
 import androidx.constraintlayout.widget.Barrier
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.customization.R as customR
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
@@ -157,7 +158,7 @@
                 connect(
                     sharedR.id.date_smartspace_view,
                     ConstraintSet.TOP,
-                    R.id.lockscreen_clock_view,
+                    customR.id.lockscreen_clock_view,
                     ConstraintSet.BOTTOM
                 )
                 connect(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 4d914c7..c11005d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -29,6 +29,7 @@
 import android.view.ViewGroup
 import android.view.ViewTreeObserver.OnPreDrawListener
 import com.android.app.animation.Interpolators
+import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
 import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
@@ -242,11 +243,11 @@
                 }
                     ?: run {
                         Log.e(TAG, "No large clock set, falling back")
-                        addTarget(R.id.lockscreen_clock_view_large)
+                        addTarget(customR.id.lockscreen_clock_view_large)
                     }
             } else {
                 if (DEBUG) Log.i(TAG, "Adding small clock")
-                addTarget(R.id.lockscreen_clock_view)
+                addTarget(customR.id.lockscreen_clock_view)
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
deleted file mode 100644
index 57ed455..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
-import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.flow.Flow
-
-@SysUISingleton
-class DreamingHostedToLockscreenTransitionViewModel
-@Inject
-constructor(
-    animationFlow: KeyguardTransitionAnimationFlow,
-) {
-
-    private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_LOCKSCREEN_DURATION,
-            edge = Edge.create(from = DREAMING_LOCKSCREEN_HOSTED, to = LOCKSCREEN),
-        )
-
-    val shortcutsAlpha: Flow<Float> =
-        transitionAnimation.sharedFlow(
-            duration = 250.milliseconds,
-            onStep = { it },
-            onCancel = { 0f },
-        )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
deleted file mode 100644
index 627f0de..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import com.android.systemui.scene.shared.model.Scenes
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Breaks down GONE->DREAMING_LOCKSCREEN_HOSTED transition into discrete steps for corresponding
- * views to consume.
- */
-@SysUISingleton
-class GoneToDreamingLockscreenHostedTransitionViewModel
-@Inject
-constructor(
-    animationFlow: KeyguardTransitionAnimationFlow,
-) {
-
-    private val transitionAnimation =
-        animationFlow
-            .setup(
-                duration = TO_DREAMING_DURATION,
-                edge = Edge.create(from = Scenes.Gone, to = DREAMING_LOCKSCREEN_HOSTED),
-            )
-            .setupWithoutSceneContainer(
-                edge = Edge.create(from = GONE, to = DREAMING_LOCKSCREEN_HOSTED),
-            )
-
-    /** Lockscreen views alpha - hide immediately */
-    val lockscreenAlpha: Flow<Float> =
-        transitionAnimation.sharedFlow(
-            duration = 1.milliseconds,
-            onStep = { 0f },
-        )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 0d55709..df3c782 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -19,6 +19,7 @@
 
 import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.FlowTracing.traceEmissionCount
+import com.android.app.tracing.coroutines.flow.flowName
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
@@ -54,7 +55,6 @@
     shadeInteractor: ShadeInteractor,
     aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
-    dreamingHostedToLockscreenTransitionViewModel: DreamingHostedToLockscreenTransitionViewModel,
     dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
     goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
     occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
@@ -63,7 +63,6 @@
     glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel,
     lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
     lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
-    lockscreenToDreamingHostedTransitionViewModel: LockscreenToDreamingHostedTransitionViewModel,
     lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
     lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel,
     lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel,
@@ -89,10 +88,7 @@
 
     /** The only time the expansion is important is while lockscreen is actively displayed */
     private val shadeExpansionAlpha =
-        combine(
-            showingLockscreen,
-            shadeInteractor.anyExpansion,
-        ) { showingLockscreen, expansion ->
+        combine(showingLockscreen, shadeInteractor.anyExpansion) { showingLockscreen, expansion ->
             if (showingLockscreen) {
                 1 - expansion
             } else {
@@ -112,7 +108,6 @@
         merge(
             aodToLockscreenTransitionViewModel.shortcutsAlpha,
             dozingToLockscreenTransitionViewModel.shortcutsAlpha,
-            dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha,
             dreamingToLockscreenTransitionViewModel.shortcutsAlpha,
             goneToLockscreenTransitionViewModel.shortcutsAlpha,
             occludedToLockscreenTransitionViewModel.shortcutsAlpha,
@@ -126,7 +121,6 @@
         merge(
             lockscreenToAodTransitionViewModel.shortcutsAlpha,
             lockscreenToDozingTransitionViewModel.shortcutsAlpha,
-            lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha,
             lockscreenToDreamingTransitionViewModel.shortcutsAlpha,
             lockscreenToGoneTransitionViewModel.shortcutsAlpha,
             lockscreenToOccludedTransitionViewModel.shortcutsAlpha,
@@ -137,10 +131,8 @@
 
     /** The source of truth of alpha for all of the quick affordances on lockscreen */
     val transitionAlpha: Flow<Float> =
-        merge(
-                fadeInAlpha,
-                fadeOutAlpha,
-            )
+        merge(fadeInAlpha, fadeOutAlpha)
+            .flowName("transitionAlpha")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
@@ -323,9 +315,7 @@
                     slotId = slotId,
                 )
             is KeyguardQuickAffordanceModel.Hidden ->
-                KeyguardQuickAffordanceViewModel(
-                    slotId = slotId,
-                )
+                KeyguardQuickAffordanceViewModel(slotId = slotId)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
index 82f40bf..af6cd16 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
@@ -27,7 +27,14 @@
  * draw a gradient that reveals/hides the contents of the screen.
  */
 @OptIn(ExperimentalCoroutinesApi::class)
-class LightRevealScrimViewModel @Inject constructor(interactor: LightRevealScrimInteractor) {
+class LightRevealScrimViewModel
+@Inject
+constructor(private val interactor: LightRevealScrimInteractor) {
     val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect
     val revealAmount: Flow<Float> = interactor.revealAmount
+    val maxAlpha: Flow<Float> = interactor.maxAlpha
+
+    fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
+        interactor.setWallpaperSupportsAmbientMode(supportsAmbientMode)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
deleted file mode 100644
index 778dbed..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.viewmodel
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
-import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
-import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.flow.Flow
-
-@SysUISingleton
-class LockscreenToDreamingHostedTransitionViewModel
-@Inject
-constructor(
-    animationFlow: KeyguardTransitionAnimationFlow,
-) {
-
-    private val transitionAnimation =
-        animationFlow.setup(
-            duration = TO_DREAMING_HOSTED_DURATION,
-            edge = Edge.create(from = LOCKSCREEN, to = DREAMING_LOCKSCREEN_HOSTED),
-        )
-
-    val shortcutsAlpha: Flow<Float> =
-        transitionAnimation.sharedFlow(
-            duration = 250.milliseconds,
-            onStep = { 1 - it },
-            onFinish = { 0f },
-            onCancel = { 1f },
-        )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
index df1394b..7c02f28 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
@@ -19,7 +19,7 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.snapshots.StateFactoryMarker
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.app.tracing.coroutines.traceCoroutine
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index 5559698..a86bfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.lifecycle
 
-import android.os.Trace
 import android.view.View
 import android.view.ViewTreeObserver
 import androidx.annotation.MainThread
@@ -25,11 +24,8 @@
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LifecycleRegistry
 import androidx.lifecycle.lifecycleScope
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
-import com.android.app.tracing.coroutines.traceCoroutine
-import com.android.systemui.Flags.coroutineTracing
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.util.Assert
-import com.android.systemui.util.Compile
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import kotlin.coroutines.CoroutineContext
@@ -83,25 +79,13 @@
     // default behavior. Instead, we want it to run on the view's UI thread since the user will
     // presumably want to call view methods that require being called from said UI thread.
     val lifecycleCoroutineContext = MAIN_DISPATCHER_SINGLETON + coroutineContext
-    val traceName =
-        if (Compile.IS_DEBUG && coroutineTracing()) {
-            inferTraceSectionName()
-        } else {
-            DEFAULT_TRACE_NAME
-        }
     var lifecycleOwner: ViewLifecycleOwner? = null
     val onAttachListener =
         object : View.OnAttachStateChangeListener {
             override fun onViewAttachedToWindow(v: View) {
                 Assert.isMainThread()
                 lifecycleOwner?.onDestroy()
-                lifecycleOwner =
-                    createLifecycleOwnerAndRun(
-                        traceName,
-                        view,
-                        lifecycleCoroutineContext,
-                        block,
-                    )
+                lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block)
             }
 
             override fun onViewDetachedFromWindow(v: View) {
@@ -112,13 +96,7 @@
 
     addOnAttachStateChangeListener(onAttachListener)
     if (view.isAttachedToWindow) {
-        lifecycleOwner =
-            createLifecycleOwnerAndRun(
-                traceName,
-                view,
-                lifecycleCoroutineContext,
-                block,
-            )
+        lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block)
     }
 
     return DisposableHandle {
@@ -131,14 +109,15 @@
 }
 
 private fun createLifecycleOwnerAndRun(
-    nameForTrace: String,
     view: View,
     coroutineContext: CoroutineContext,
     block: suspend LifecycleOwner.(View) -> Unit,
 ): ViewLifecycleOwner {
     return ViewLifecycleOwner(view).apply {
         onCreate()
-        lifecycleScope.launch(coroutineContext) { traceCoroutine(nameForTrace) { block(view) } }
+        // TODO(b/370595466): Refactor to support installing CoroutineTracingContext on the
+        //                    top-level CoroutineScope used as the lifecycleScope
+        lifecycleScope.launch(coroutineContext) { block(view) }
     }
 }
 
@@ -167,9 +146,7 @@
  * └───────────────┴───────────────────┴──────────────┴─────────────────┘
  * ```
  */
-class ViewLifecycleOwner(
-    private val view: View,
-) : LifecycleOwner {
+class ViewLifecycleOwner(private val view: View) : LifecycleOwner {
 
     private val windowVisibleListener =
         ViewTreeObserver.OnWindowVisibilityChangeListener { updateState() }
@@ -205,28 +182,6 @@
     }
 }
 
-private fun isFrameInteresting(frame: StackWalker.StackFrame): Boolean =
-    frame.className != CURRENT_CLASS_NAME && frame.className != JAVA_ADAPTER_CLASS_NAME
-
-/** Get a name for the trace section include the name of the call site. */
-private fun inferTraceSectionName(): String {
-    try {
-        Trace.traceBegin(Trace.TRACE_TAG_APP, "RepeatWhenAttachedKt#inferTraceSectionName")
-        val interestingFrame =
-            StackWalker.getInstance().walk { stream ->
-                stream.filter(::isFrameInteresting).limit(5).findFirst()
-            }
-        return if (interestingFrame.isPresent) {
-            val f = interestingFrame.get()
-            "${f.className}#${f.methodName}:${f.lineNumber} [$DEFAULT_TRACE_NAME]"
-        } else {
-            DEFAULT_TRACE_NAME
-        }
-    } finally {
-        Trace.traceEnd(Trace.TRACE_TAG_APP)
-    }
-}
-
 /**
  * Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is
  * at least at [state] (or immediately after calling this function if the window is already at least
@@ -368,8 +323,7 @@
  * an extension function, and plumbing dagger-injected instances for static usage has little
  * benefit.
  */
-private val MAIN_DISPATCHER_SINGLETON =
-    Dispatchers.Main + createCoroutineTracingContext("RepeatWhenAttached")
+private val MAIN_DISPATCHER_SINGLETON = Dispatchers.Main + newTracingContext("RepeatWhenAttached")
 private const val DEFAULT_TRACE_NAME = "repeatWhenAttached"
 private const val CURRENT_CLASS_NAME = "com.android.systemui.lifecycle.RepeatWhenAttachedKt"
 private const val JAVA_ADAPTER_CLASS_NAME = "com.android.systemui.util.kotlin.JavaAdapterKt"
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 2961d05..742f435 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -170,12 +170,11 @@
                 // Set different layout for each device
                 if (device.isMutingExpectedDevice()
                         && !mController.isCurrentConnectedDeviceRemote()) {
-                    updateTitleIcon(R.drawable.media_output_icon_volume,
-                            mController.getColorItemContent());
+                    updateUnmutedVolumeIcon(device);
                     mCurrentActivePosition = position;
                     updateFullItemClickListener(v -> onItemClick(v, device));
                     setSingleLineLayout(getItemTitle(device));
-                    initFakeActiveDevice();
+                    initFakeActiveDevice(device);
                 } else if (device.hasSubtext()) {
                     boolean isActiveWithOngoingSession =
                             (device.hasOngoingSession() && (currentlyConnected || isDeviceIncluded(
@@ -184,8 +183,7 @@
                             && isActiveWithOngoingSession;
                     if (isActiveWithOngoingSession) {
                         mCurrentActivePosition = position;
-                        updateTitleIcon(R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
+                        updateUnmutedVolumeIcon(device);
                         mSubTitleText.setText(device.getSubtextString());
                         updateTwoLineLayoutContentAlpha(DEVICE_CONNECTED_ALPHA);
                         updateEndClickAreaAsSessionEditing(device,
@@ -199,9 +197,7 @@
                     } else {
                         if (currentlyConnected) {
                             mCurrentActivePosition = position;
-                            updateTitleIcon(R.drawable.media_output_icon_volume,
-                                    mController.getColorItemContent());
-                            initSeekbar(device, isCurrentSeekbarInvisible);
+                            updateUnmutedVolumeIcon(device);
                         } else {
                             setUpDeviceIcon(device);
                         }
@@ -243,8 +239,7 @@
                     // selected device in group
                     boolean isDeviceDeselectable = isDeviceIncluded(
                             mController.getDeselectableMediaDevice(), device);
-                    updateTitleIcon(R.drawable.media_output_icon_volume,
-                            mController.getColorItemContent());
+                    updateUnmutedVolumeIcon(device);
                     updateGroupableCheckBox(true, isDeviceDeselectable, device);
                     updateEndClickArea(device, isDeviceDeselectable);
                     disableFocusPropertyForView(mContainerLayout);
@@ -264,8 +259,7 @@
                         setSingleLineLayout(getItemTitle(device));
                     } else if (device.hasOngoingSession()) {
                         mCurrentActivePosition = position;
-                        updateTitleIcon(R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
+                        updateUnmutedVolumeIcon(device);
                         updateEndClickAreaAsSessionEditing(device, device.isHostForOngoingSession()
                                 ? R.drawable.media_output_status_edit_session
                                 : R.drawable.ic_sound_bars_anim);
@@ -278,8 +272,7 @@
                             && !mController.getSelectableMediaDevice().isEmpty()) {
                         //If device is connected and there's other selectable devices, layout as
                         // one of selected devices.
-                        updateTitleIcon(R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
+                        updateUnmutedVolumeIcon(device);
                         boolean isDeviceDeselectable = isDeviceIncluded(
                                 mController.getDeselectableMediaDevice(), device);
                         updateGroupableCheckBox(true, isDeviceDeselectable, device);
@@ -291,8 +284,7 @@
                                 true /* showEndTouchArea */);
                         initSeekbar(device, isCurrentSeekbarInvisible);
                     } else {
-                        updateTitleIcon(R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
+                        updateUnmutedVolumeIcon(device);
                         disableFocusPropertyForView(mContainerLayout);
                         setUpContentDescriptionForView(mSeekBar, device);
                         mCurrentActivePosition = position;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 63a7e01..574ccee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -47,6 +47,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.settingslib.media.InputMediaDevice;
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.res.R;
@@ -321,18 +322,20 @@
                     // Check if response volume match with the latest request, to ignore obsolete
                     // response
                     if (isCurrentSeekbarInvisible && !mIsInitVolumeFirstTime) {
-                        updateTitleIcon(currentVolume == 0 ? R.drawable.media_output_icon_volume_off
-                                        : R.drawable.media_output_icon_volume,
-                                mController.getColorItemContent());
+                        if (currentVolume == 0) {
+                            updateMutedVolumeIcon(device);
+                        } else {
+                            updateUnmutedVolumeIcon(device);
+                        }
                     } else {
                         if (!mVolumeAnimator.isStarted()) {
                             int percentage =
                                     (int) ((double) currentVolume * VOLUME_PERCENTAGE_SCALE_SIZE
                                             / (double) mSeekBar.getMax());
                             if (percentage == 0) {
-                                updateMutedVolumeIcon();
+                                updateMutedVolumeIcon(device);
                             } else {
-                                updateUnmutedVolumeIcon();
+                                updateUnmutedVolumeIcon(device);
                             }
                             mSeekBar.setVolume(currentVolume);
                             mLatestUpdateVolume = -1;
@@ -340,7 +343,7 @@
                     }
                 } else if (currentVolume == 0) {
                     mSeekBar.resetVolume();
-                    updateMutedVolumeIcon();
+                    updateMutedVolumeIcon(device);
                 }
                 if (currentVolume == mLatestUpdateVolume) {
                     mLatestUpdateVolume = -1;
@@ -365,7 +368,7 @@
                             R.string.media_output_dialog_volume_percentage, percentage));
                     mVolumeValueText.setVisibility(View.VISIBLE);
                     if (mStartFromMute) {
-                        updateUnmutedVolumeIcon();
+                        updateUnmutedVolumeIcon(device);
                         mStartFromMute = false;
                     }
                     if (progressToVolume != deviceVolume) {
@@ -390,9 +393,9 @@
                             seekBar.getProgress());
                     if (currentVolume == 0) {
                         seekBar.setProgress(0);
-                        updateMutedVolumeIcon();
+                        updateMutedVolumeIcon(device);
                     } else {
-                        updateUnmutedVolumeIcon();
+                        updateUnmutedVolumeIcon(device);
                     }
                     mTitleIcon.setVisibility(View.VISIBLE);
                     mVolumeValueText.setVisibility(View.GONE);
@@ -402,36 +405,48 @@
             });
         }
 
-        void updateMutedVolumeIcon() {
+        void updateMutedVolumeIcon(MediaDevice device) {
             mIconAreaLayout.setBackground(
                     mContext.getDrawable(R.drawable.media_output_item_background_active));
-            updateTitleIcon(R.drawable.media_output_icon_volume_off,
-                    mController.getColorItemContent());
+            updateTitleIcon(device, true /* isMutedVolumeIcon */);
         }
 
-        void updateUnmutedVolumeIcon() {
+        void updateUnmutedVolumeIcon(MediaDevice device) {
             mIconAreaLayout.setBackground(
                     mContext.getDrawable(R.drawable.media_output_title_icon_area)
             );
-            updateTitleIcon(R.drawable.media_output_icon_volume,
-                    mController.getColorItemContent());
+            updateTitleIcon(device, false /* isMutedVolumeIcon */);
         }
 
-        void updateTitleIcon(@DrawableRes int id, int color) {
+        void updateTitleIcon(MediaDevice device, boolean isMutedVolumeIcon) {
+            boolean isInputMediaDevice = device instanceof InputMediaDevice;
+            int id = getDrawableId(isInputMediaDevice, isMutedVolumeIcon);
             mTitleIcon.setImageDrawable(mContext.getDrawable(id));
-            mTitleIcon.setImageTintList(ColorStateList.valueOf(color));
+            mTitleIcon.setImageTintList(ColorStateList.valueOf(mController.getColorItemContent()));
             mIconAreaLayout.setBackgroundTintList(
                     ColorStateList.valueOf(mController.getColorSeekbarProgress()));
         }
 
+        @VisibleForTesting
+        int getDrawableId(boolean isInputDevice, boolean isMutedVolumeIcon) {
+            // Returns the microphone icon when the flag is enabled and the device is an input
+            // device.
+            if (com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl()
+                    && isInputDevice) {
+                return isMutedVolumeIcon ? R.drawable.ic_mic_off : R.drawable.ic_mic_26dp;
+            }
+            return isMutedVolumeIcon
+                    ? R.drawable.media_output_icon_volume_off
+                    : R.drawable.media_output_icon_volume;
+        }
+
         void updateIconAreaClickListener(View.OnClickListener listener) {
             mIconAreaLayout.setOnClickListener(listener);
         }
 
-        void initFakeActiveDevice() {
+        void initFakeActiveDevice(MediaDevice device) {
             disableSeekBar();
-            updateTitleIcon(R.drawable.media_output_icon_volume,
-                    mController.getColorItemContent());
+            updateTitleIcon(device, false /* isMutedIcon */);
             final Drawable backgroundDrawable = mContext.getDrawable(
                                     R.drawable.media_output_item_background_active)
                             .mutate();
@@ -518,13 +533,13 @@
                     mController.logInteractionUnmuteDevice(device);
                     mSeekBar.setVolume(UNMUTE_DEFAULT_VOLUME);
                     mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME);
-                    updateUnmutedVolumeIcon();
+                    updateUnmutedVolumeIcon(device);
                     mIconAreaLayout.setOnTouchListener(((iconV, event) -> false));
                 } else {
                     mController.logInteractionMuteDevice(device);
                     mSeekBar.resetVolume();
                     mController.adjustVolume(device, 0);
-                    updateMutedVolumeIcon();
+                    updateMutedVolumeIcon(device);
                     mIconAreaLayout.setOnTouchListener(((iconV, event) -> {
                         mSeekBar.dispatchTouchEvent(event);
                         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 544dbdd..f40ad06 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -16,13 +16,13 @@
 
 package com.android.systemui.mediaprojection.appselector
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.app.Activity
 import android.content.ComponentName
 import android.content.Context
 import android.os.UserHandle
 import androidx.lifecycle.DefaultLifecycleObserver
 import com.android.launcher3.icons.IconFactory
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -134,7 +134,11 @@
         @MediaProjectionAppSelector
         @MediaProjectionAppSelectorScope
         fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
-            CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("MediaProjectionAppSelectorScope"))
+            CoroutineScope(
+                applicationScope.coroutineContext +
+                    SupervisorJob() +
+                    newTracingContext("MediaProjectionAppSelectorScope")
+            )
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index c3729c0..212da9f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -122,8 +122,11 @@
         final Intent launchingIntent = getIntent();
         mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
                 EXTRA_USER_REVIEW_GRANTED_CONSENT, false);
-
-        mPackageName = getCallingPackage();
+        if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) {
+            mPackageName = getLaunchedFromPackage();
+        } else {
+            mPackageName = getCallingPackage();
+        }
 
         // This activity is launched directly by an app, or system server. System server provides
         // the package name through the intent if so.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 5e8c2c9..b019c13 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -58,6 +59,7 @@
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.Utils;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
@@ -128,7 +130,8 @@
             Optional<Pip> pipOptional,
             Optional<BackAnimation> backAnimation,
             SecureSettings secureSettings,
-            DisplayTracker displayTracker) {
+            DisplayTracker displayTracker,
+            DeviceStateManager deviceStateManager) {
         mContext = context;
         mExecutor = mainExecutor;
         mNavigationBarComponentFactory = navigationBarComponentFactory;
@@ -146,11 +149,19 @@
                 dumpManager, autoHideController, lightBarController, pipOptional,
                 backAnimation.orElse(null), taskStackChangeListeners);
         mIsLargeScreen = isLargeScreen(mContext);
-        mIsPhone =
-                mContext.getResources().getIntArray(R.array.config_foldedDeviceStates).length == 0;
+        mIsPhone = determineIfPhone(mContext, deviceStateManager);
         dumpManager.registerDumpable(this);
     }
 
+    private boolean determineIfPhone(Context context, DeviceStateManager deviceStateManager) {
+        if (android.hardware.devicestate.feature.flags.Flags.deviceStatePropertyMigration()) {
+            return !Utils.isDeviceFoldable(context.getResources(), deviceStateManager);
+        } else {
+            return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length
+                    == 0;
+        }
+    }
+
     @Override
     public void onConfigChanged(Configuration newConfig) {
         boolean isOldConfigLargeScreen = mIsLargeScreen;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
index 96386e5..0166176 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.navigationbar.gestural.domain
 
-import com.android.app.tracing.coroutines.flow.flowOn
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -35,6 +34,7 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 3aa9daa..d0f6f79 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -37,7 +37,7 @@
 import android.provider.Settings
 import android.widget.Toast
 import androidx.annotation.VisibleForTesting
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 1511f31..fa987dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -255,7 +255,8 @@
      * @return size in pixels of QQS
      */
     public int getQqsHeight() {
-        return mHeader.getHeight();
+        SceneContainerFlag.assertInNewMode();
+        return mHeader.getMeasuredHeight();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 65c29b8..6db91ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -23,7 +23,9 @@
 import android.os.Bundle
 import android.util.IndentingPrintWriter
 import android.view.LayoutInflater
+import android.view.MotionEvent
 import android.view.View
+import android.view.ViewConfiguration
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.activity.OnBackPressedDispatcher
@@ -35,6 +37,7 @@
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
@@ -42,7 +45,9 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.verticalScroll
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
@@ -51,9 +56,12 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.res.dimensionResource
@@ -61,7 +69,9 @@
 import androidx.compose.ui.semantics.CustomAccessibilityAction
 import androidx.compose.ui.semantics.customActions
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.round
+import androidx.compose.ui.util.fastRoundToInt
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.lifecycleScope
@@ -83,6 +93,7 @@
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.dagger.MediaModule.QS_PANEL
@@ -92,6 +103,7 @@
 import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
 import com.android.systemui.qs.composefragment.SceneKeys.QuickSettings
 import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
+import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
 import com.android.systemui.qs.composefragment.ui.notificationScrimClip
 import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
 import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
@@ -137,32 +149,22 @@
 
     private lateinit var viewModel: QSFragmentComposeViewModel
 
-    // Starting with a non-zero value makes it so that it has a non-zero height on first expansion
-    // This is important for `QuickSettingsControllerImpl.mMinExpansionHeight` to detect a "change".
-    private val qqsHeight = MutableStateFlow(1)
     private val qsHeight = MutableStateFlow(0)
     private val qqsVisible = MutableStateFlow(false)
     private val qqsPositionOnRoot = Rect()
     private val composeViewPositionOnScreen = Rect()
+    private val scrollState = ScrollState(0)
 
     // Inside object for namespacing
     private val notificationScrimClippingParams =
         object {
             var isEnabled by mutableStateOf(false)
-            var leftInset by mutableStateOf(0)
-            var rightInset by mutableStateOf(0)
-            var top by mutableStateOf(0)
-            var bottom by mutableStateOf(0)
-            var radius by mutableStateOf(0)
+            var params by mutableStateOf(NotificationScrimClipParams())
 
             fun dump(pw: IndentingPrintWriter) {
                 pw.printSection("NotificationScrimClippingParams") {
                     pw.println("isEnabled", isEnabled)
-                    pw.println("leftInset", "${leftInset}px")
-                    pw.println("rightInset", "${rightInset}px")
-                    pw.println("top", "${top}px")
-                    pw.println("bottom", "${bottom}px")
-                    pw.println("radius", "${radius}px")
+                    pw.println("params", params)
                 }
             }
         }
@@ -216,7 +218,10 @@
             FrameLayoutTouchPassthrough(
                 context,
                 { notificationScrimClippingParams.isEnabled },
-                { notificationScrimClippingParams.top },
+                { notificationScrimClippingParams.params.top },
+                // Only allow scrolling when we are fully expanded. That way, we don't intercept
+                // swipes in lockscreen (when somehow QS is receiving touches).
+                { scrollState.canScrollForward && viewModel.isQsFullyExpanded },
             )
         frame.addView(
             composeView,
@@ -229,22 +234,20 @@
     @Composable
     private fun Content() {
         PlatformTheme {
-            val visible by viewModel.qsVisible.collectAsStateWithLifecycle()
-
             AnimatedVisibility(
-                visible = visible,
+                visible = viewModel.isQsVisible,
                 modifier =
-                    Modifier.windowInsetsPadding(WindowInsets.navigationBars).thenIf(
-                        notificationScrimClippingParams.isEnabled
-                    ) {
-                        Modifier.notificationScrimClip(
-                            notificationScrimClippingParams.leftInset,
-                            notificationScrimClippingParams.top,
-                            notificationScrimClippingParams.rightInset,
-                            notificationScrimClippingParams.bottom,
-                            notificationScrimClippingParams.radius,
-                        )
-                    },
+                    Modifier.graphicsLayer { alpha = viewModel.viewAlpha }
+                        .windowInsetsPadding(WindowInsets.navigationBars)
+                        // Clipping before translation to match QSContainerImpl.onDraw
+                        .offset {
+                            IntOffset(x = 0, y = viewModel.viewTranslationY.fastRoundToInt())
+                        }
+                        .thenIf(notificationScrimClippingParams.isEnabled) {
+                            Modifier.notificationScrimClip {
+                                notificationScrimClippingParams.params
+                            }
+                        },
             ) {
                 val isEditing by
                     viewModel.containerViewModel.editModeViewModel.isEditing
@@ -258,7 +261,7 @@
                     label = "EditModeAnimatedContent",
                 ) { editing ->
                     if (editing) {
-                        val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+                        val qqsPadding = viewModel.qqsHeaderHeight
                         EditMode(
                             viewModel = viewModel.containerViewModel.editModeViewModel,
                             modifier =
@@ -287,7 +290,7 @@
     private fun CollapsableQuickSettingsSTL() {
         val sceneState = remember {
             MutableSceneTransitionLayoutState(
-                viewModel.expansionState.value.toIdleSceneKey(),
+                viewModel.expansionState.toIdleSceneKey(),
                 transitions =
                     transitions {
                         from(QuickQuickSettings, QuickSettings) {
@@ -298,7 +301,10 @@
         }
 
         LaunchedEffect(Unit) {
-            synchronizeQsState(sceneState, viewModel.expansionState.map { it.progress })
+            synchronizeQsState(
+                sceneState,
+                snapshotFlow { viewModel.expansionState }.map { it.progress },
+            )
         }
 
         SceneTransitionLayout(state = sceneState, modifier = Modifier.fillMaxSize()) {
@@ -319,7 +325,7 @@
 
     override fun getQsMinExpansionHeight(): Int {
         // TODO (b/353253277) implement split screen
-        return qqsHeight.value
+        return viewModel.qqsHeight
     }
 
     override fun getDesiredHeight(): Int {
@@ -333,7 +339,7 @@
     }
 
     override fun setHeightOverride(desiredHeight: Int) {
-        viewModel.heightOverrideValue = desiredHeight
+        viewModel.heightOverride = desiredHeight
     }
 
     override fun setHeaderClickable(qsExpansionEnabled: Boolean) {
@@ -353,7 +359,7 @@
     }
 
     override fun setExpanded(qsExpanded: Boolean) {
-        viewModel.isQSExpanded = qsExpanded
+        viewModel.isQsExpanded = qsExpanded
     }
 
     override fun setListening(listening: Boolean) {
@@ -361,7 +367,7 @@
     }
 
     override fun setQsVisible(qsVisible: Boolean) {
-        viewModel.isQSVisible = qsVisible
+        viewModel.isQsVisible = qsVisible
     }
 
     override fun isShowingDetail(): Boolean {
@@ -382,11 +388,10 @@
         headerTranslation: Float,
         squishinessFraction: Float,
     ) {
-        viewModel.qsExpansionValue = qsExpansionFraction
-        viewModel.panelExpansionFractionValue = panelExpansionFraction
-        viewModel.squishinessFractionValue = squishinessFraction
-
-        // TODO(b/353254353) Handle header translation
+        viewModel.setQsExpansionValue(qsExpansionFraction)
+        viewModel.panelExpansionFraction = panelExpansionFraction
+        viewModel.squishinessFraction = squishinessFraction
+        viewModel.proposedTranslation = headerTranslation
     }
 
     override fun setHeaderListening(listening: Boolean) {
@@ -406,7 +411,7 @@
     }
 
     override fun getHeightDiff(): Int {
-        return 0 // For now TODO(b/353254353)
+        return viewModel.heightDiff
     }
 
     override fun getHeader(): View? {
@@ -419,8 +424,8 @@
         // TODO (b/353253280)
     }
 
-    override fun setInSplitShade(shouldTranslate: Boolean) {
-        // TODO (b/356435605)
+    override fun setInSplitShade(isInSplitShade: Boolean) {
+        viewModel.isInSplitShade = isInSplitShade
     }
 
     override fun setTransitionToFullShadeProgress(
@@ -429,9 +434,9 @@
         qsSquishinessFraction: Float,
     ) {
         viewModel.isTransitioningToFullShade = isTransitioningToFullShade
-        viewModel.lockscreenToShadeProgressValue = qsTransitionFraction
+        viewModel.lockscreenToShadeProgress = qsTransitionFraction
         if (isTransitioningToFullShade) {
-            viewModel.squishinessFractionValue = qsSquishinessFraction
+            viewModel.squishinessFraction = qsSquishinessFraction
         }
     }
 
@@ -445,17 +450,18 @@
         fullWidth: Boolean,
     ) {
         notificationScrimClippingParams.isEnabled = visible
-        notificationScrimClippingParams.top = top
-        notificationScrimClippingParams.bottom = bottom
-        // Full width means that QS will show in the entire width allocated to it (for example
-        // phone) vs. showing in a narrower column (for example, tablet portrait).
-        notificationScrimClippingParams.leftInset = if (fullWidth) 0 else leftInset
-        notificationScrimClippingParams.rightInset = if (fullWidth) 0 else rightInset
-        notificationScrimClippingParams.radius = cornerRadius
+        notificationScrimClippingParams.params =
+            NotificationScrimClipParams(
+                top,
+                bottom,
+                if (fullWidth) 0 else leftInset,
+                if (fullWidth) 0 else rightInset,
+                cornerRadius,
+            )
     }
 
     override fun isFullyCollapsed(): Boolean {
-        return viewModel.qsExpansionValue <= 0f
+        return viewModel.isQsFullyCollapsed
     }
 
     override fun setCollapsedMediaVisibilityChangedListener(listener: Consumer<Boolean>?) {
@@ -467,11 +473,11 @@
     }
 
     override fun setOverScrollAmount(overScrollAmount: Int) {
-        super.setOverScrollAmount(overScrollAmount)
+        viewModel.overScrollAmount = overScrollAmount
     }
 
     override fun setIsNotificationPanelFullWidth(isFullWidth: Boolean) {
-        viewModel.isSmallScreenValue = isFullWidth
+        viewModel.isSmallScreen = isFullWidth
     }
 
     override fun getHeaderTop(): Int {
@@ -500,12 +506,8 @@
     private fun setListenerCollections() {
         lifecycleScope.launch {
             lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch {
-                    //                    TODO
-                    //                    setListenerJob(
-                    //                            scrollListener,
-                    //
-                    //                    )
+                this@QSFragmentCompose.view?.setSnapshotBinding {
+                    scrollListener.value?.onQsPanelScrollChanged(scrollState.value)
                 }
                 launch {
                     setListenerJob(
@@ -529,8 +531,8 @@
 
     @Composable
     private fun SceneScope.QuickQuickSettingsElement() {
-        val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
-        val bottomPadding = dimensionResource(id = R.dimen.qqs_layout_padding_bottom)
+        val qqsPadding = viewModel.qqsHeaderHeight
+        val bottomPadding = viewModel.qqsBottomPadding
         DisposableEffect(Unit) {
             qqsVisible.value = true
 
@@ -540,6 +542,7 @@
             viewModel.containerViewModel.quickQuickSettingsViewModel.squishinessViewModel
                 .squishiness
                 .collectAsStateWithLifecycle()
+
         Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) {
             Box(
                 modifier =
@@ -559,14 +562,13 @@
                         .approachLayout(isMeasurementApproachInProgress = { squishiness < 1f }) {
                             measurable,
                             constraints ->
-                            qqsHeight.value = lookaheadSize.height
+                            viewModel.qqsHeight = lookaheadSize.height
                             val placeable = measurable.measure(constraints)
                             layout(placeable.width, placeable.height) { placeable.place(0, 0) }
                         }
-                        .padding(top = { qqsPadding }, bottom = { bottomPadding.roundToPx() })
+                        .padding(top = { qqsPadding }, bottom = { bottomPadding })
             ) {
-                val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
-                if (qsEnabled) {
+                if (viewModel.isQsEnabled) {
                     QuickQuickSettings(
                         viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel,
                         modifier =
@@ -589,7 +591,7 @@
 
     @Composable
     private fun SceneScope.QuickSettingsElement() {
-        val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+        val qqsPadding = viewModel.qqsHeaderHeight
         val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top)
         Column(
             modifier =
@@ -597,13 +599,27 @@
                     stringResource(id = R.string.accessibility_quick_settings_collapse)
                 )
         ) {
-            val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
-            if (qsEnabled) {
+            if (viewModel.isQsEnabled) {
                 Box(
                     modifier =
                         Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f)
                 ) {
-                    Column {
+                    DisposableEffect(Unit) {
+                        lifecycleScope.launch { scrollState.scrollTo(0) }
+                        onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } }
+                    }
+
+                    Column(
+                        modifier =
+                            Modifier.offset {
+                                    IntOffset(
+                                        x = 0,
+                                        y = viewModel.qsScrollTranslationY.fastRoundToInt(),
+                                    )
+                                }
+                                .onSizeChanged { viewModel.qsScrollHeight = it.height }
+                                .verticalScroll(scrollState)
+                    ) {
                         Spacer(
                             modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
                         )
@@ -613,15 +629,14 @@
                         )
                     }
                 }
-                QuickSettingsTheme {
-                    FooterActions(
-                        viewModel = viewModel.footerActionsViewModel,
-                        qsVisibilityLifecycleOwner = this@QSFragmentCompose,
-                        modifier =
-                            Modifier.sysuiResTag("qs_footer_actions")
-                                .element(ElementKeys.FooterActions),
-                    )
-                }
+            }
+            QuickSettingsTheme {
+                FooterActions(
+                    viewModel = viewModel.footerActionsViewModel,
+                    qsVisibilityLifecycleOwner = this@QSFragmentCompose,
+                    modifier =
+                        Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions),
+                )
             }
         }
     }
@@ -803,13 +818,17 @@
 private const val EDIT_MODE_TIME_MILLIS = 500
 
 /**
- * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as
- * per [clippingEnabledProvider].
+ * Performs different touch handling based on the state of the ComposeView:
+ * * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as
+ *   per [clippingEnabledProvider].
+ * * Intercept touches that would overscroll QS forward and instead allow them to be used to close
+ *   the shade.
  */
 private class FrameLayoutTouchPassthrough(
     context: Context,
     private val clippingEnabledProvider: () -> Boolean,
     private val clippingTopProvider: () -> Int,
+    private val canScrollForwardQs: () -> Boolean,
 ) : FrameLayout(context) {
     override fun isTransformedTouchPointInView(
         x: Float,
@@ -823,4 +842,32 @@
             super.isTransformedTouchPointInView(x, y, child, outLocalPoint)
         }
     }
+
+    val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
+    var downY = 0f
+
+    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
+        // If there's a touch on this view and we can scroll down, we don't want to be intercepted
+        val action = ev.actionMasked
+
+        when (action) {
+            MotionEvent.ACTION_DOWN -> {
+                // If we can scroll down, make sure none of our parents intercepts us.
+                if (canScrollForwardQs()) {
+                    parent?.requestDisallowInterceptTouchEvent(true)
+                }
+                downY = ev.y
+            }
+
+            MotionEvent.ACTION_MOVE -> {
+                val y = ev.y.toInt()
+                val yDiff: Float = y - downY
+                if (yDiff < -touchSlop && !canScrollForwardQs()) {
+                    // Intercept touches that are overscrolling.
+                    return true
+                }
+            }
+        }
+        return super.onInterceptTouchEvent(ev)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
index 93c6445..c912bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
@@ -31,87 +31,73 @@
  * ([ClipOp.Difference]) a `RoundRect(-leftInset, top, width + rightInset, bottom, radius, radius)`
  * from the QS container.
  */
-fun Modifier.notificationScrimClip(
-    leftInset: Int,
-    top: Int,
-    rightInset: Int,
-    bottom: Int,
-    radius: Int
-): Modifier {
-    return this then NotificationScrimClipElement(leftInset, top, rightInset, bottom, radius)
+fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
+    return this then NotificationScrimClipElement(clipParams)
 }
 
-private class NotificationScrimClipNode(
-    var leftInset: Float,
-    var top: Float,
-    var rightInset: Float,
-    var bottom: Float,
-    var radius: Float,
-) : DrawModifierNode, Modifier.Node() {
+private class NotificationScrimClipNode(var clipParams: () -> NotificationScrimClipParams) :
+    DrawModifierNode, Modifier.Node() {
     private val path = Path()
 
-    var invalidated = true
+    private var lastClipParams = NotificationScrimClipParams()
 
     override fun ContentDrawScope.draw() {
-        if (invalidated) {
+        val newClipParams = clipParams()
+        if (newClipParams != lastClipParams) {
+            lastClipParams = newClipParams
+            applyClipParams(path, lastClipParams)
+        }
+        clipPath(path, ClipOp.Difference) { this@draw.drawContent() }
+    }
+
+    private fun ContentDrawScope.applyClipParams(
+        path: Path,
+        clipParams: NotificationScrimClipParams,
+    ) {
+        with(clipParams) {
             path.rewind()
             path
                 .asAndroidPath()
                 .addRoundRect(
-                    -leftInset,
-                    top,
+                    -leftInset.toFloat(),
+                    top.toFloat(),
                     size.width + rightInset,
-                    bottom,
-                    radius,
-                    radius,
-                    android.graphics.Path.Direction.CW
+                    bottom.toFloat(),
+                    radius.toFloat(),
+                    radius.toFloat(),
+                    android.graphics.Path.Direction.CW,
                 )
-            invalidated = false
         }
-        clipPath(path, ClipOp.Difference) { this@draw.drawContent() }
     }
 }
 
-private data class NotificationScrimClipElement(
-    val leftInset: Int,
-    val top: Int,
-    val rightInset: Int,
-    val bottom: Int,
-    val radius: Int,
-) : ModifierNodeElement<NotificationScrimClipNode>() {
+private data class NotificationScrimClipElement(val clipParams: () -> NotificationScrimClipParams) :
+    ModifierNodeElement<NotificationScrimClipNode>() {
     override fun create(): NotificationScrimClipNode {
-        return NotificationScrimClipNode(
-            leftInset.toFloat(),
-            top.toFloat(),
-            rightInset.toFloat(),
-            bottom.toFloat(),
-            radius.toFloat(),
-        )
+        return NotificationScrimClipNode(clipParams)
     }
 
     override fun update(node: NotificationScrimClipNode) {
-        val changed =
-            node.leftInset != leftInset.toFloat() ||
-                node.top != top.toFloat() ||
-                node.rightInset != rightInset.toFloat() ||
-                node.bottom != bottom.toFloat() ||
-                node.radius != radius.toFloat()
-        if (changed) {
-            node.leftInset = leftInset.toFloat()
-            node.top = top.toFloat()
-            node.rightInset = rightInset.toFloat()
-            node.bottom = bottom.toFloat()
-            node.radius = radius.toFloat()
-            node.invalidated = true
-        }
+        node.clipParams = clipParams
     }
 
     override fun InspectorInfo.inspectableProperties() {
         name = "notificationScrimClip"
-        properties["leftInset"] = leftInset
-        properties["top"] = top
-        properties["rightInset"] = rightInset
-        properties["bottom"] = bottom
-        properties["radius"] = radius
+        with(clipParams()) {
+            properties["leftInset"] = leftInset
+            properties["top"] = top
+            properties["rightInset"] = rightInset
+            properties["bottom"] = bottom
+            properties["radius"] = radius
+        }
     }
 }
+
+/** Params for [notificationScrimClip]. */
+data class NotificationScrimClipParams(
+    val top: Int = 0,
+    val bottom: Int = 0,
+    val leftInset: Int = 0,
+    val rightInset: Int = 0,
+    val radius: Int = 0,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 7a8b2c2..e21485b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -20,19 +20,31 @@
 import android.graphics.Rect
 import androidx.annotation.FloatRange
 import androidx.annotation.VisibleForTesting
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.keyguard.BouncerPanelExpansionCalculator
 import com.android.systemui.Dumpable
+import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.FooterActionsController
-import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel.QSExpansionState
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor
 import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
+import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.StatusBarState
@@ -47,246 +59,128 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import java.io.PrintWriter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
+@OptIn(ExperimentalCoroutinesApi::class)
 class QSFragmentComposeViewModel
 @AssistedInject
 constructor(
     val containerViewModel: QuickSettingsContainerViewModel,
     @Main private val resources: Resources,
-    private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
+    footerActionsViewModelFactory: FooterActionsViewModel.Factory,
     private val footerActionsController: FooterActionsController,
     private val sysuiStatusBarStateController: SysuiStatusBarStateController,
-    private val deviceEntryInteractor: DeviceEntryInteractor,
-    private val disableFlagsRepository: DisableFlagsRepository,
+    deviceEntryInteractor: DeviceEntryInteractor,
+    disableFlagsRepository: DisableFlagsRepository,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
-    private val configurationInteractor: ConfigurationInteractor,
+    configurationInteractor: ConfigurationInteractor,
     private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
     private val squishinessInteractor: TileSquishinessInteractor,
     private val paginatedGridViewModel: PaginatedGridViewModel,
     @Assisted private val lifecycleScope: LifecycleCoroutineScope,
 ) : Dumpable, ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator")
+
     val footerActionsViewModel =
         footerActionsViewModelFactory.create(lifecycleScope).also {
             lifecycleScope.launch { footerActionsController.init() }
         }
 
-    private val _qsBounds = MutableStateFlow(Rect())
+    var isQsExpanded by mutableStateOf(false)
 
-    private val _qsExpanded = MutableStateFlow(false)
-    var isQSExpanded: Boolean
-        get() = _qsExpanded.value
-        set(value) {
-            _qsExpanded.value = value
-        }
-
-    private val _qsVisible = MutableStateFlow(false)
-    val qsVisible = _qsVisible.asStateFlow()
-    var isQSVisible: Boolean
-        get() = qsVisible.value
-        set(value) {
-            _qsVisible.value = value
-        }
+    var isQsVisible by mutableStateOf(false)
 
     // This can only be negative if undefined (in which case it will be -1f), else it will be
     // in [0, 1]. In some cases, it could be set back to -1f internally to indicate that it's
     // different to every value in [0, 1].
-    @FloatRange(from = -1.0, to = 1.0) private val _qsExpansion = MutableStateFlow(-1f)
-    var qsExpansionValue: Float
-        get() = _qsExpansion.value
-        set(value) {
-            if (value < 0f) {
-                _qsExpansion.value = -1f
-            }
-            _qsExpansion.value = value.coerceIn(0f, 1f)
+    private var qsExpansion by mutableStateOf(-1f)
+
+    fun setQsExpansionValue(value: Float) {
+        if (value < 0f) {
+            qsExpansion = -1f
+        } else {
+            qsExpansion = value.coerceIn(0f, 1f)
         }
+    }
 
-    private val _panelFraction = MutableStateFlow(0f)
-    var panelExpansionFractionValue: Float
-        get() = _panelFraction.value
-        set(value) {
-            _panelFraction.value = value
-        }
+    val isQsFullyCollapsed by derivedStateOf { qsExpansion <= 0f }
 
-    private val _squishinessFraction = MutableStateFlow(1f)
-    var squishinessFractionValue: Float
-        get() = _squishinessFraction.value
-        set(value) {
-            _squishinessFraction.value = value
-        }
+    var panelExpansionFraction by mutableStateOf(0f)
 
-    val qqsHeaderHeight =
-        configurationInteractor.onAnyConfigurationChange
-            .map {
-                if (LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources)) {
-                    0
-                } else {
-                    largeScreenHeaderHelper.getLargeScreenHeaderHeight()
-                }
-            }
-            .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), 0)
+    var squishinessFraction by mutableStateOf(1f)
 
-    private val _headerAnimating = MutableStateFlow(false)
+    val qqsHeaderHeight by
+        hydrator.hydratedStateOf(
+            traceName = "qqsHeaderHeight",
+            initialValue = 0,
+            source =
+                configurationInteractor.onAnyConfigurationChange.map {
+                    if (LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources)) {
+                        0
+                    } else {
+                        largeScreenHeaderHelper.getLargeScreenHeaderHeight()
+                    }
+                },
+        )
 
-    private val _stackScrollerOverscrolling = MutableStateFlow(false)
-    var isStackScrollerOverscrolling: Boolean
-        get() = _stackScrollerOverscrolling.value
-        set(value) {
-            _stackScrollerOverscrolling.value = value
-        }
+    val qqsBottomPadding by
+        hydrator.hydratedStateOf(
+            traceName = "qqsBottomPadding",
+            initialValue = resources.getDimensionPixelSize(R.dimen.qqs_layout_padding_bottom),
+            source = configurationInteractor.dimensionPixelSize(R.dimen.qqs_layout_padding_bottom),
+        )
+
+    // Starting with a non-zero value makes it so that it has a non-zero height on first expansion
+    // This is important for `QuickSettingsControllerImpl.mMinExpansionHeight` to detect a "change".
+    var qqsHeight by mutableStateOf(1)
+
+    var qsScrollHeight by mutableStateOf(0)
+
+    val heightDiff: Int
+        get() = qsScrollHeight - qqsHeight + qqsBottomPadding
+
+    var isStackScrollerOverscrolling by mutableStateOf(false)
+
+    var proposedTranslation by mutableStateOf(0f)
 
     /**
      * Whether QS is enabled by policy. This is normally true, except when it's disabled by some
      * policy. See [DisableFlagsRepository].
      */
-    val qsEnabled =
-        disableFlagsRepository.disableFlags
-            .map { it.isQuickSettingsEnabled() }
-            .stateIn(
-                lifecycleScope,
-                SharingStarted.WhileSubscribed(),
-                disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled(),
-            )
+    val isQsEnabled by
+        hydrator.hydratedStateOf(
+            traceName = "isQsEnabled",
+            initialValue = disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled(),
+            source = disableFlagsRepository.disableFlags.map { it.isQuickSettingsEnabled() },
+        )
 
-    private val _keyguardAndExpanded = MutableStateFlow(false)
+    var isInSplitShade by mutableStateOf(false)
 
-    /**
-     * Tracks the current [StatusBarState]. It will switch early if the upcoming state is
-     * [StatusBarState.KEYGUARD]
-     */
-    @get:VisibleForTesting
-    val statusBarState =
-        conflatedCallbackFlow {
-                val callback =
-                    object : StatusBarStateController.StateListener {
-                        override fun onStateChanged(newState: Int) {
-                            trySend(newState)
-                        }
+    var isTransitioningToFullShade by mutableStateOf(false)
 
-                        override fun onUpcomingStateChanged(upcomingState: Int) {
-                            if (upcomingState == StatusBarState.KEYGUARD) {
-                                trySend(upcomingState)
-                            }
-                        }
-                    }
-                sysuiStatusBarStateController.addCallback(callback)
+    var lockscreenToShadeProgress by mutableStateOf(0f)
 
-                awaitClose { sysuiStatusBarStateController.removeCallback(callback) }
-            }
-            .onStart { emit(sysuiStatusBarStateController.state) }
-            .stateIn(
-                lifecycleScope,
-                SharingStarted.WhileSubscribed(),
-                sysuiStatusBarStateController.state,
-            )
+    var isSmallScreen by mutableStateOf(false)
 
-    private val isKeyguardState =
-        statusBarState
-            .map { it == StatusBarState.KEYGUARD }
-            .stateIn(
-                lifecycleScope,
-                SharingStarted.WhileSubscribed(),
-                statusBarState.value == StatusBarState.KEYGUARD,
-            )
+    var heightOverride by mutableStateOf(-1)
 
-    private val _viewHeight = MutableStateFlow(0)
-
-    private val _headerTranslation = MutableStateFlow(0f)
-
-    private val _inSplitShade = MutableStateFlow(false)
-    var isInSplitShade: Boolean
-        get() = _inSplitShade.value
-        set(value) {
-            _inSplitShade.value = value
+    val expansionState by derivedStateOf {
+        if (forceQs) {
+            QSExpansionState(1f)
+        } else {
+            QSExpansionState(qsExpansion.coerceIn(0f, 1f))
         }
+    }
 
-    private val _transitioningToFullShade = MutableStateFlow(false)
-    var isTransitioningToFullShade: Boolean
-        get() = _transitioningToFullShade.value
-        set(value) {
-            _transitioningToFullShade.value = value
-        }
-
-    private val isBypassEnabled = deviceEntryInteractor.isBypassEnabled
-
-    private val showCollapsedOnKeyguard =
-        combine(
-                isBypassEnabled,
-                _transitioningToFullShade,
-                _inSplitShade,
-                ::calculateShowCollapsedOnKeyguard,
-            )
-            .stateIn(
-                lifecycleScope,
-                SharingStarted.WhileSubscribed(),
-                calculateShowCollapsedOnKeyguard(
-                    isBypassEnabled.value,
-                    isTransitioningToFullShade,
-                    isInSplitShade,
-                ),
-            )
-
-    private val _lockscreenToShadeProgress = MutableStateFlow(0.0f)
-    var lockscreenToShadeProgressValue: Float
-        get() = _lockscreenToShadeProgress.value
-        set(value) {
-            _lockscreenToShadeProgress.value = value
-        }
-
-    private val _overscrolling = MutableStateFlow(false)
-
-    private val _isSmallScreen = MutableStateFlow(false)
-    var isSmallScreenValue: Boolean
-        get() = _isSmallScreen.value
-        set(value) {
-            _isSmallScreen.value = value
-        }
-
-    private val _shouldUpdateMediaSquishiness = MutableStateFlow(false)
-
-    private val _heightOverride = MutableStateFlow(-1)
-    val heightOverride = _heightOverride.asStateFlow()
-    var heightOverrideValue: Int
-        get() = heightOverride.value
-        set(value) {
-            _heightOverride.value = value
-        }
-
-    private val forceQS =
-        combine(
-                _qsExpanded,
-                _stackScrollerOverscrolling,
-                isKeyguardState,
-                showCollapsedOnKeyguard,
-                ::calculateForceQs,
-            )
-            .stateIn(
-                lifecycleScope,
-                SharingStarted.WhileSubscribed(),
-                calculateForceQs(
-                    isQSExpanded,
-                    isStackScrollerOverscrolling,
-                    isKeyguardState.value,
-                    showCollapsedOnKeyguard.value,
-                ),
-            )
-
-    val expansionState: StateFlow<QSExpansionState> =
-        combine(_qsExpansion, forceQS, ::calculateExpansionState)
-            .stateIn(
-                lifecycleScope,
-                SharingStarted.WhileSubscribed(),
-                calculateExpansionState(_qsExpansion.value, forceQS.value),
-            )
+    val isQsFullyExpanded by derivedStateOf { expansionState.progress >= 1f }
 
     /**
      * Accessibility action for collapsing/expanding QS. The provided runnable is responsible for
@@ -297,40 +191,181 @@
     val inFirstPage: Boolean
         get() = paginatedGridViewModel.inFirstPage
 
-    override suspend fun onActivated(): Nothing {
-        hydrateSquishinessInteractor()
+    var overScrollAmount by mutableStateOf(0)
+
+    val viewTranslationY by derivedStateOf {
+        if (isOverscrolling) {
+            overScrollAmount.toFloat()
+        } else {
+            if (onKeyguardAndExpanded) {
+                translationScaleY * qqsHeight
+            } else {
+                headerTranslation
+            }
+        }
     }
 
-    private suspend fun hydrateSquishinessInteractor(): Nothing {
-        _squishinessFraction.collect {
-            squishinessInteractor.setSquishinessValue(it.constrainSquishiness())
+    val qsScrollTranslationY by derivedStateOf {
+        val panelTranslationY = translationScaleY * heightDiff
+        if (onKeyguardAndExpanded) panelTranslationY else 0f
+    }
+
+    val viewAlpha by derivedStateOf {
+        when {
+            isInBouncerTransit ->
+                BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(alphaProgress)
+            isKeyguardState -> alphaProgress
+            isSmallScreen -> ShadeInterpolation.getContentAlpha(alphaProgress)
+            else -> largeScreenShadeInterpolator.getQsAlpha(alphaProgress)
         }
     }
 
+    private var qsBounds by mutableStateOf(Rect())
+
+    private val constrainedSquishinessFraction: Float
+        get() = squishinessFraction.constrainSquishiness()
+
+    private var _headerAnimating by mutableStateOf(false)
+
+    /**
+     * Tracks the current [StatusBarState]. It will switch early if the upcoming state is
+     * [StatusBarState.KEYGUARD]
+     */
+    @get:VisibleForTesting
+    val statusBarState by
+        hydrator.hydratedStateOf(
+            traceName = "statusBarState",
+            initialValue = sysuiStatusBarStateController.state,
+            source =
+                conflatedCallbackFlow {
+                        val callback =
+                            object : StatusBarStateController.StateListener {
+                                override fun onStateChanged(newState: Int) {
+                                    trySend(newState)
+                                }
+
+                                override fun onUpcomingStateChanged(upcomingState: Int) {
+                                    if (upcomingState == StatusBarState.KEYGUARD) {
+                                        trySend(upcomingState)
+                                    }
+                                }
+                            }
+                        sysuiStatusBarStateController.addCallback(callback)
+
+                        awaitClose { sysuiStatusBarStateController.removeCallback(callback) }
+                    }
+                    .onStart { emit(sysuiStatusBarStateController.state) },
+        )
+
+    private val isKeyguardState: Boolean
+        get() = statusBarState == StatusBarState.KEYGUARD
+
+    private var viewHeight by mutableStateOf(0)
+
+    private val isBypassEnabled by
+        hydrator.hydratedStateOf(
+            traceName = "isBypassEnabled",
+            source = deviceEntryInteractor.isBypassEnabled,
+        )
+
+    private val showCollapsedOnKeyguard by derivedStateOf {
+        isBypassEnabled || (isTransitioningToFullShade && !isInSplitShade)
+    }
+
+    private val onKeyguardAndExpanded: Boolean
+        get() = isKeyguardState && !showCollapsedOnKeyguard
+
+    private val isOverscrolling: Boolean
+        get() = overScrollAmount != 0
+
+    private var shouldUpdateMediaSquishiness by mutableStateOf(false)
+
+    private val forceQs by derivedStateOf {
+        (isQsExpanded || isStackScrollerOverscrolling) &&
+            (isKeyguardState && !showCollapsedOnKeyguard)
+    }
+
+    private val translationScaleY: Float
+        get() = ((qsExpansion - 1) * (if (isInSplitShade) 1f else SHORT_PARALLAX_AMOUNT))
+
+    private val headerTranslation by derivedStateOf {
+        if (isTransitioningToFullShade) 0f else proposedTranslation
+    }
+
+    private val alphaProgress by derivedStateOf {
+        when {
+            isSmallScreen -> 1f
+            isInSplitShade ->
+                if (isTransitioningToFullShade || isKeyguardState) {
+                    lockscreenToShadeProgress
+                } else {
+                    panelExpansionFraction
+                }
+            isTransitioningToFullShade -> lockscreenToShadeProgress
+            else -> panelExpansionFraction
+        }
+    }
+
+    private val isInBouncerTransit by
+        hydrator.hydratedStateOf(
+            traceName = "isInBouncerTransit",
+            initialValue = false,
+            source =
+                keyguardTransitionInteractor.isInTransition(
+                    Edge.create(to = Scenes.Bouncer),
+                    Edge.create(to = KeyguardState.PRIMARY_BOUNCER),
+                ),
+        )
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { hydrateSquishinessInteractor() }
+            launch { hydrator.activate() }
+            awaitCancellation()
+        }
+    }
+
+    private suspend fun hydrateSquishinessInteractor() {
+        snapshotFlow { constrainedSquishinessFraction }
+            .collect { squishinessInteractor.setSquishinessValue(it) }
+    }
+
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.asIndenting().run {
             printSection("Quick Settings state") {
-                println("isQSExpanded", isQSExpanded)
-                println("isQSVisible", isQSVisible)
-                println("isQSEnabled", qsEnabled.value)
+                println("isQSExpanded", isQsExpanded)
+                println("isQSVisible", isQsVisible)
+                println("isQSEnabled", isQsEnabled)
                 println("isCustomizing", containerViewModel.editModeViewModel.isEditing.value)
             }
             printSection("Expansion state") {
-                println("qsExpansion", qsExpansionValue)
-                println("panelExpansionFraction", panelExpansionFractionValue)
-                println("squishinessFraction", squishinessFractionValue)
-                println("expansionState", expansionState.value)
-                println("forceQS", forceQS.value)
+                println("qsExpansion", qsExpansion)
+                println("panelExpansionFraction", panelExpansionFraction)
+                println("squishinessFraction", squishinessFraction)
+                println("proposedTranslation", proposedTranslation)
+                println("expansionState", expansionState)
+                println("forceQS", forceQs)
+                printSection("Derived values") {
+                    println("headerTranslation", headerTranslation)
+                    println("translationScaleY", translationScaleY)
+                    println("viewTranslationY", viewTranslationY)
+                    println("qsScrollTranslationY", qsScrollTranslationY)
+                    println("viewAlpha", viewAlpha)
+                }
             }
             printSection("Shade state") {
                 println("stackOverscrolling", isStackScrollerOverscrolling)
-                println("statusBarState", StatusBarState.toString(statusBarState.value))
-                println("isKeyguardState", isKeyguardState.value)
-                println("isSmallScreen", isSmallScreenValue)
-                println("heightOverride", "${heightOverrideValue}px")
-                println("qqsHeaderHeight", "${qqsHeaderHeight.value}px")
+                println("overscrollAmount", overScrollAmount)
+                println("statusBarState", StatusBarState.toString(statusBarState))
+                println("isKeyguardState", isKeyguardState)
+                println("isSmallScreen", isSmallScreen)
+                println("heightOverride", "${heightOverride}px")
+                println("qqsHeaderHeight", "${qqsHeaderHeight}px")
+                println("qqsBottomPadding", "${qqsBottomPadding}px")
                 println("isSplitShade", isInSplitShade)
-                println("showCollapsedOnKeyguard", showCollapsedOnKeyguard.value)
+                println("showCollapsedOnKeyguard", showCollapsedOnKeyguard)
+                println("qqsHeight", "${qqsHeight}px")
+                println("qsScrollHeight", "${qsScrollHeight}px")
             }
         }
     }
@@ -340,7 +375,7 @@
         fun create(lifecycleScope: LifecycleCoroutineScope): QSFragmentComposeViewModel
     }
 
-    // In the future, this will have other relevant elements like squishiness.
+    // In the future, this may have other relevant elements.
     data class QSExpansionState(@FloatRange(0.0, 1.0) val progress: Float)
 }
 
@@ -348,30 +383,4 @@
     return (0.1f + this * 0.9f).coerceIn(0f, 1f)
 }
 
-// Helper methods for combining flows.
-
-private fun calculateExpansionState(expansion: Float, forceQs: Boolean): QSExpansionState {
-    return if (forceQs) {
-        QSExpansionState(1f)
-    } else {
-        QSExpansionState(expansion.coerceIn(0f, 1f))
-    }
-}
-
-private fun calculateForceQs(
-    isQSExpanded: Boolean,
-    isStackOverScrolling: Boolean,
-    isKeyguardShowing: Boolean,
-    shouldShowCollapsedOnKeyguard: Boolean,
-): Boolean {
-    return (isQSExpanded || isStackOverScrolling) &&
-        (isKeyguardShowing && !shouldShowCollapsedOnKeyguard)
-}
-
-private fun calculateShowCollapsedOnKeyguard(
-    isBypassEnabled: Boolean,
-    isTransitioningToFullShade: Boolean,
-    isInSplitShade: Boolean,
-): Boolean {
-    return isBypassEnabled || (isTransitioningToFullShade && !isInSplitShade)
-}
+private val SHORT_PARALLAX_AMOUNT = 0.1f
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 1fe54e4..31e867e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -31,12 +31,12 @@
 import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
 import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModelImpl
 import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModelImpl
 import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModelImpl
+import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsSizeViewModelImpl
+import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -55,7 +55,7 @@
 
     @Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
 
-    @Binds fun bindGridSizeViewModel(impl: FixedColumnsSizeViewModelImpl): FixedColumnsSizeViewModel
+    @Binds fun bindQSColumnsViewModel(impl: QSColumnsSizeViewModelImpl): QSColumnsViewModel
 
     @Binds
     fun bindIconLabelVisibilityViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepository.kt
deleted file mode 100644
index 32ce973..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepository.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.panels.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-@SysUISingleton
-class FixedColumnsRepository @Inject constructor() {
-    // Number of columns in the narrowest state for consistency
-    private val _columns = MutableStateFlow(4)
-    val columns: StateFlow<Int> = _columns.asStateFlow()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
index 26b2e2b..424be90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
@@ -38,6 +38,6 @@
 ) {
     val rows =
         configurationRepository.onConfigurationChange.emitOnStart().map {
-            resources.getInteger(R.integer.quick_settings_max_rows)
+            resources.getInteger(R.integer.quick_settings_paginated_grid_num_rows)
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
new file mode 100644
index 0000000..a9205c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepository.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.emitOnStart
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.mapLatest
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class QSColumnsRepository
+@Inject
+constructor(
+    @Main private val resources: Resources,
+    configurationRepository: ConfigurationRepository,
+) {
+    val splitShadeColumns: Flow<Int> =
+        flowOf(resources.getInteger(R.integer.quick_settings_split_shade_num_columns))
+    val dualShadeColumns: Flow<Int> =
+        flowOf(resources.getInteger(R.integer.quick_settings_dual_shade_num_columns))
+    val columns: Flow<Int> =
+        configurationRepository.onConfigurationChange.emitOnStart().mapLatest {
+            resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns)
+        }
+    val defaultColumns: Int =
+        resources.getInteger(R.integer.quick_settings_infinite_grid_num_columns)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
index f7c71ce..ee0cfb3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
@@ -34,6 +34,6 @@
 ) {
     val rows =
         configurationRepository.onConfigurationChange.emitOnStart().map {
-            resources.getInteger(R.integer.quick_qs_panel_max_rows)
+            resources.getInteger(R.integer.quick_qs_paginated_grid_num_rows)
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
new file mode 100644
index 0000000..11ea60e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractor.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.qs.panels.data.repository.QSColumnsRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class QSColumnsInteractor
+@Inject
+constructor(
+    @Application scope: CoroutineScope,
+    repo: QSColumnsRepository,
+    shadeInteractor: ShadeInteractor,
+) {
+    @OptIn(ExperimentalCoroutinesApi::class)
+    val columns: StateFlow<Int> =
+        shadeInteractor.shadeMode
+            .flatMapLatest {
+                when (it) {
+                    ShadeMode.Dual -> repo.dualShadeColumns
+                    ShadeMode.Split -> repo.splitShadeColumns
+                    ShadeMode.Single -> repo.columns
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), repo.defaultColumns)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
new file mode 100644
index 0000000..b9994d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import com.android.compose.animation.Bounceable
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.ui.model.GridCell
+import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+
+data class BounceableInfo(
+    val bounceable: BounceableTileViewModel,
+    val previousTile: Bounceable?,
+    val nextTile: Bounceable?,
+    val bounceEnd: Boolean,
+)
+
+fun List<Pair<GridCell, BounceableTileViewModel>>.bounceableInfo(
+    index: Int,
+    columns: Int,
+): BounceableInfo {
+    val cell = this[index].first as TileGridCell
+    // Only look for neighbor bounceables if they are on the same row
+    val onLastColumn = cell.onLastColumn(cell.column, columns)
+    val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 }
+    val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
+    return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn)
+}
+
+fun List<BounceableTileViewModel>.bounceableInfo(
+    sizedTile: SizedTile<TileViewModel>,
+    index: Int,
+    column: Int,
+    columns: Int,
+): BounceableInfo {
+    // Only look for neighbor bounceables if they are on the same row
+    val onLastColumn = sizedTile.onLastColumn(column, columns)
+    val previousTile = getOrNull(index - 1)?.takeIf { column != 0 }
+    val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
+    return BounceableInfo(this[index], previousTile, nextTile, !onLastColumn)
+}
+
+private fun <T> SizedTile<T>.onLastColumn(column: Int, columns: Int): Boolean {
+    return column == columns - width
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 770fd78..74fa0fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -116,9 +116,9 @@
             regenerateGrid()
             _tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell)
         } else {
-            // Add the tile with a temporary row which will get reassigned when
+            // Add the tile with a temporary row/col which will get reassigned when
             // regenerating spacers
-            _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0))
+            _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0, 0))
         }
 
         regenerateGrid()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index e749475..d55763a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.pager.HorizontalPager
 import androidx.compose.foundation.pager.rememberPagerState
 import androidx.compose.material.icons.Icons
@@ -97,7 +97,9 @@
                     TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
                 }
             }
-            Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) {
+            // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is
+            // expected to be inside a scrollable container, this should not be an issue.
+            Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) {
                 PagerDots(
                     pagerState = pagerState,
                     activeColor = MaterialTheme.colorScheme.primary,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index a645b51..1c7a334 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -20,6 +20,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.util.fastMap
@@ -29,6 +31,7 @@
 import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
 import com.android.systemui.qs.composefragment.ui.GridAnchor
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
 import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
 import com.android.systemui.res.R
@@ -38,10 +41,11 @@
     viewModel: QuickQuickSettingsViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val sizedTiles by
-        viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList())
+    val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle()
     val tiles = sizedTiles.fastMap { it.tile }
+    val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
     val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
+    val scope = rememberCoroutineScope()
 
     DisposableEffect(tiles) {
         val token = Any()
@@ -49,6 +53,7 @@
         onDispose { tiles.forEach { it.stopListening(token) } }
     }
     val columns by viewModel.columns.collectAsStateWithLifecycle()
+    var cellIndex = 0
     Box(modifier = modifier) {
         GridAnchor()
         VerticalSpannedGrid(
@@ -59,11 +64,15 @@
             modifier = Modifier.sysuiResTag("qqs_tile_layout"),
         ) { spanIndex ->
             val it = sizedTiles[spanIndex]
+            val column = cellIndex % columns
+            cellIndex += it.width
             Tile(
                 tile = it.tile,
                 iconOnly = it.isIcon,
                 modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
                 squishiness = { squishiness },
+                coroutineScope = scope,
+                bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 9ec5a82..71fa0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -31,6 +31,7 @@
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -54,6 +55,7 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.semantics.toggleableState
+import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import com.android.compose.modifiers.background
 import com.android.compose.modifiers.thenIf
@@ -64,7 +66,6 @@
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
 import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
 import com.android.systemui.res.R
-import kotlinx.coroutines.delay
 
 private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target"
 
@@ -138,13 +139,20 @@
     accessibilityUiState: AccessibilityUiState? = null,
 ) {
     Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
-        Text(label, color = colors.label, modifier = Modifier.tileMarquee())
+        Text(
+            label,
+            style = MaterialTheme.typography.labelLarge,
+            color = colors.label,
+            maxLines = 1,
+            overflow = TextOverflow.Ellipsis,
+        )
         if (!TextUtils.isEmpty(secondaryLabel)) {
             Text(
                 secondaryLabel ?: "",
                 color = colors.secondaryLabel,
+                style = MaterialTheme.typography.bodyMedium,
                 modifier =
-                    Modifier.tileMarquee().thenIf(
+                    Modifier.thenIf(
                         accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") ==
                             true
                     ) {
@@ -182,10 +190,7 @@
                     rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
                 } else {
                     var atEnd by remember(icon.res) { mutableStateOf(false) }
-                    LaunchedEffect(key1 = icon.res) {
-                        delay(350)
-                        atEnd = true
-                    }
+                    LaunchedEffect(key1 = icon.res) { atEnd = true }
                     rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 45c1e48..5c2a2bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -22,13 +22,13 @@
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.animateIntAsState
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.LocalOverscrollConfiguration
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Arrangement.spacedBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
@@ -46,7 +46,6 @@
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.LazyGridItemScope
 import androidx.compose.foundation.lazy.grid.LazyGridScope
 import androidx.compose.foundation.lazy.grid.rememberLazyGridState
 import androidx.compose.foundation.rememberScrollState
@@ -66,19 +65,17 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.BiasAlignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.positionInRoot
@@ -98,23 +95,26 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastMap
-import androidx.compose.ui.zIndex
+import com.android.compose.animation.bounceable
 import com.android.compose.modifiers.background
 import com.android.compose.modifiers.height
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
+import com.android.systemui.qs.panels.ui.compose.BounceableInfo
 import com.android.systemui.qs.panels.ui.compose.DragAndDropState
 import com.android.systemui.qs.panels.ui.compose.EditTileListState
+import com.android.systemui.qs.panels.ui.compose.bounceableInfo
 import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone
 import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList
 import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
 import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
-import com.android.systemui.qs.panels.ui.compose.selection.ResizingHandle
+import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer
 import com.android.systemui.qs.panels.ui.compose.selection.TileWidths
 import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile
 import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState
@@ -122,11 +122,14 @@
 import com.android.systemui.qs.panels.ui.model.GridCell
 import com.android.systemui.qs.panels.ui.model.SpacerGridCell
 import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.shared.model.groupAndSort
 import com.android.systemui.res.R
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 
 object TileType
 
@@ -241,15 +244,20 @@
     onSetTiles: (List<TileSpec>) -> Unit,
 ) {
     val currentListState by rememberUpdatedState(listState)
-    val tileHeight = CommonTileDefaults.TileHeight
     val totalRows = listState.tiles.lastOrNull()?.row ?: 0
     val totalHeight by
         animateDpAsState(
-            gridHeight(totalRows + 1, tileHeight, TileArrangementPadding, CurrentTilesGridPadding),
+            gridHeight(totalRows + 1, TileHeight, TileArrangementPadding, CurrentTilesGridPadding),
             label = "QSEditCurrentTilesGridHeight",
         )
     val gridState = rememberLazyGridState()
     var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) }
+    val coroutineScope = rememberCoroutineScope()
+
+    val cells =
+        remember(listState.tiles) {
+            listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
+        }
 
     TileLazyGrid(
         state = gridState,
@@ -272,7 +280,7 @@
                 }
                 .testTag(CURRENT_TILES_GRID_TEST_TAG),
     ) {
-        EditTiles(listState.tiles, listState, selectionState) { spec ->
+        EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec ->
             // Toggle the current size of the tile
             currentListState.isIcon(spec)?.let { onResize(spec, !it) }
         }
@@ -286,10 +294,10 @@
     columns: Int,
     dragAndDropState: DragAndDropState,
 ) {
-    // Available tiles aren't visible during drag and drop, so the row isn't needed
+    // Available tiles aren't visible during drag and drop, so the row/col isn't needed
     val groupedTiles =
         remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
-            groupAndSort(tiles.fastMap { TileGridCell(it, 0) })
+            groupAndSort(tiles.fastMap { TileGridCell(it, 0, 0) })
         }
     val labelColors = EditModeTileDefaults.editTileColors()
 
@@ -349,24 +357,26 @@
 /**
  * Adds a list of [GridCell] to the lazy grid
  *
- * @param cells the list of [GridCell]
+ * @param cells the pairs of [GridCell] to [BounceableTileViewModel]
  * @param dragAndDropState the [DragAndDropState] for this grid
  * @param selectionState the [MutableSelectionState] for this grid
  * @param onToggleSize the callback when a tile's size is toggled
  */
 fun LazyGridScope.EditTiles(
-    cells: List<GridCell>,
+    cells: List<Pair<GridCell, BounceableTileViewModel>>,
+    columns: Int,
     dragAndDropState: DragAndDropState,
     selectionState: MutableSelectionState,
+    coroutineScope: CoroutineScope,
     onToggleSize: (spec: TileSpec) -> Unit,
 ) {
     items(
         count = cells.size,
-        key = { cells[it].key(it, dragAndDropState) },
-        span = { cells[it].span },
+        key = { cells[it].first.key(it, dragAndDropState) },
+        span = { cells[it].first.span },
         contentType = { TileType },
     ) { index ->
-        when (val cell = cells[index]) {
+        when (val cell = cells[index].first) {
             is TileGridCell ->
                 if (dragAndDropState.isMoving(cell.tile.tileSpec)) {
                     // If the tile is being moved, replace it with a visible spacer
@@ -385,6 +395,9 @@
                         dragAndDropState = dragAndDropState,
                         selectionState = selectionState,
                         onToggleSize = onToggleSize,
+                        coroutineScope = coroutineScope,
+                        bounceableInfo = cells.bounceableInfo(index, columns),
+                        modifier = Modifier.animateItem(),
                     )
                 }
             is SpacerGridCell -> SpacerGridCell()
@@ -393,12 +406,15 @@
 }
 
 @Composable
-private fun LazyGridItemScope.TileGridCell(
+private fun TileGridCell(
     cell: TileGridCell,
     index: Int,
     dragAndDropState: DragAndDropState,
     selectionState: MutableSelectionState,
     onToggleSize: (spec: TileSpec) -> Unit,
+    coroutineScope: CoroutineScope,
+    bounceableInfo: BounceableInfo,
+    modifier: Modifier = Modifier,
 ) {
     val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
     var selected by remember { mutableStateOf(false) }
@@ -407,6 +423,9 @@
             targetValue = if (selected) 1f else 0f,
             label = "QSEditTileSelectionAlpha",
         )
+    val selectionColor = MaterialTheme.colorScheme.primary
+    val colors = EditModeTileDefaults.editTileColors()
+    val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
 
     LaunchedEffect(selectionState.selection?.tileSpec) {
         selectionState.selection?.let {
@@ -420,152 +439,61 @@
         selected = selectionState.selection?.tileSpec == cell.tile.tileSpec
     }
 
-    val modifier =
-        Modifier.animateItem()
-            .semantics(mergeDescendants = true) {
-                this.stateDescription = stateDescription
-                contentDescription = cell.tile.label.text
-                customActions =
-                    listOf(
-                        // TODO(b/367748260): Add final accessibility actions
-                        CustomAccessibilityAction("Toggle size") {
-                            onToggleSize(cell.tile.tileSpec)
-                            true
-                        }
-                    )
-            }
-            .height(CommonTileDefaults.TileHeight)
-            .fillMaxWidth()
-
-    val content =
-        @Composable {
-            EditTile(
-                tileViewModel = cell.tile,
-                iconOnly = cell.isIcon,
-                selectionAlpha = { selectionAlpha },
-                modifier =
-                    Modifier.fillMaxSize()
-                        .selectableTile(cell.tile.tileSpec, selectionState)
-                        .dragAndDropTileSource(
-                            SizedTileImpl(cell.tile, cell.width),
-                            dragAndDropState,
-                            selectionState::unSelect,
-                        ),
-            )
-        }
-
-    if (selected) {
-        SelectedTile(
-            isIcon = cell.isIcon,
-            selectionAlpha = { selectionAlpha },
-            selectionState = selectionState,
-            modifier = modifier.zIndex(2f), // 2f to display this tile over neighbors when dragged
-            content = content,
-        )
-    } else {
-        UnselectedTile(
-            selectionAlpha = { selectionAlpha },
-            selectionState = selectionState,
-            modifier = modifier,
-            content = content,
-        )
-    }
-}
-
-@Composable
-private fun SelectedTile(
-    isIcon: Boolean,
-    selectionAlpha: () -> Float,
-    selectionState: MutableSelectionState,
-    modifier: Modifier = Modifier,
-    content: @Composable () -> Unit,
-) {
     // Current base, min and max width of this tile
     var tileWidths: TileWidths? by remember { mutableStateOf(null) }
-
-    // Animated diff between the current width and the resized width of the tile. We can't use
-    // animateContentSize here as the tile is sometimes unbounded.
-    val remainingOffset by
-        animateIntAsState(
-            selectionState.resizingState?.let { tileWidths?.base?.minus(it.width) ?: 0 } ?: 0,
-            label = "QSEditTileWidthOffset",
-        )
-
     val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() }
-    Box(
-        modifier.onSizeChanged {
-            val min = if (isIcon) it.width else (it.width - padding) / 2
-            val max = if (isIcon) (it.width * 2) + padding else it.width
-            tileWidths = TileWidths(it.width, min, max)
-        }
+
+    ResizableTileContainer(
+        selected = selected,
+        selectionState = selectionState,
+        selectionAlpha = { selectionAlpha },
+        selectionColor = selectionColor,
+        tileWidths = { tileWidths },
+        modifier =
+            modifier
+                .height(TileHeight)
+                .fillMaxWidth()
+                .onSizeChanged {
+                    // Grab the size before the bounceable to get the idle width
+                    val min = if (cell.isIcon) it.width else (it.width - padding) / 2
+                    val max = if (cell.isIcon) (it.width * 2) + padding else it.width
+                    tileWidths = TileWidths(it.width, min, max)
+                }
+                .bounceable(
+                    bounceable = currentBounceableInfo.bounceable,
+                    previousBounceable = currentBounceableInfo.previousTile,
+                    nextBounceable = currentBounceableInfo.nextTile,
+                    orientation = Orientation.Horizontal,
+                    bounceEnd = currentBounceableInfo.bounceEnd,
+                ),
     ) {
-        val handle =
-            @Composable {
-                ResizingHandle(
-                    enabled = true,
-                    selectionState = selectionState,
-                    transition = selectionAlpha,
-                    tileWidths = { tileWidths },
+        Box(
+            modifier
+                .fillMaxSize()
+                .semantics(mergeDescendants = true) {
+                    this.stateDescription = stateDescription
+                    contentDescription = cell.tile.label.text
+                    customActions =
+                        listOf(
+                            // TODO(b/367748260): Add final accessibility actions
+                            CustomAccessibilityAction("Toggle size") {
+                                onToggleSize(cell.tile.tileSpec)
+                                true
+                            }
+                        )
+                }
+                .selectableTile(cell.tile.tileSpec, selectionState) {
+                    coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
+                }
+                .dragAndDropTileSource(
+                    SizedTileImpl(cell.tile, cell.width),
+                    dragAndDropState,
+                    selectionState::unSelect,
                 )
-            }
-
-        Layout(contents = listOf(content, handle)) {
-            (contentMeasurables, handleMeasurables),
-            constraints ->
-            // Grab the width from the resizing state if a resize is in progress, otherwise fill the
-            // max width
-            val width =
-                selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset)
-            val contentPlaceable =
-                contentMeasurables.first().measure(constraints.copy(maxWidth = width))
-            val handlePlaceable = handleMeasurables.first().measure(constraints)
-
-            // Place the dot vertically centered on the right edge
-            val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
-            val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
-
-            layout(constraints.maxWidth, constraints.maxHeight) {
-                contentPlaceable.place(0, 0)
-                handlePlaceable.place(handleX, handleY)
-            }
-        }
-    }
-}
-
-@Composable
-private fun UnselectedTile(
-    selectionAlpha: () -> Float,
-    selectionState: MutableSelectionState,
-    modifier: Modifier = Modifier,
-    content: @Composable () -> Unit,
-) {
-    val handle =
-        @Composable {
-            ResizingHandle(
-                enabled = false,
-                selectionState = selectionState,
-                transition = selectionAlpha,
-            )
-        }
-
-    Box(modifier) {
-        Layout(contents = listOf(content, handle)) {
-            (contentMeasurables, handleMeasurables),
-            constraints ->
-            val contentPlaceable =
-                contentMeasurables
-                    .first()
-                    .measure(constraints.copy(maxWidth = constraints.maxWidth))
-            val handlePlaceable = handleMeasurables.first().measure(constraints)
-
-            // Place the dot vertically centered on the right edge
-            val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
-            val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
-
-            layout(constraints.maxWidth, constraints.maxHeight) {
-                contentPlaceable.place(0, 0)
-                handlePlaceable.place(handleX, handleY)
-            }
+                .tileBackground(colors.background)
+                .tilePadding()
+        ) {
+            EditTile(tile = cell.tile, iconOnly = cell.isIcon)
         }
     }
 }
@@ -588,19 +516,19 @@
         verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top),
         modifier = modifier,
     ) {
-        EditTileContainer(
-            colors = colors,
-            modifier =
-                Modifier.fillMaxWidth()
-                    .height(CommonTileDefaults.TileHeight)
-                    .clearSelectionTile(selectionState)
-                    .semantics(mergeDescendants = true) {
-                        onClick(onClickActionName) { false }
-                        this.stateDescription = stateDescription
-                    }
-                    .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
-                        selectionState.unSelect()
-                    },
+        Box(
+            Modifier.fillMaxWidth()
+                .height(TileHeight)
+                .clearSelectionTile(selectionState)
+                .semantics(mergeDescendants = true) {
+                    onClick(onClickActionName) { false }
+                    this.stateDescription = stateDescription
+                }
+                .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
+                    selectionState.unSelect()
+                }
+                .tileBackground(colors.background)
+                .tilePadding()
         ) {
             // Icon
             SmallTileContent(
@@ -626,16 +554,14 @@
 @Composable
 private fun SpacerGridCell(modifier: Modifier = Modifier) {
     // By default, spacers are invisible and exist purely to catch drag movements
-    Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth())
+    Box(modifier.height(TileHeight).fillMaxWidth())
 }
 
 @Composable
-fun EditTile(
-    tileViewModel: EditTileViewModel,
+fun BoxScope.EditTile(
+    tile: EditTileViewModel,
     iconOnly: Boolean,
-    modifier: Modifier = Modifier,
     colors: TileColors = EditModeTileDefaults.editTileColors(),
-    selectionAlpha: () -> Float = { 1f },
 ) {
     // Animated horizontal alignment from center (0f) to start (-1f)
     val alignmentValue by
@@ -646,68 +572,36 @@
     val alignment by remember {
         derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) }
     }
+    // Icon
+    Box(Modifier.size(ToggleTargetSize).align(alignment)) {
+        SmallTileContent(
+            icon = tile.icon,
+            color = colors.icon,
+            animateToEnd = true,
+            modifier = Modifier.align(Alignment.Center),
+        )
+    }
 
-    EditTileContainer(colors = colors, selectionAlpha = selectionAlpha, modifier = modifier) {
-        // Icon
-        Box(Modifier.size(ToggleTargetSize).align(alignment)) {
-            SmallTileContent(
-                icon = tileViewModel.icon,
-                color = colors.icon,
-                animateToEnd = true,
-                modifier = Modifier.align(Alignment.Center),
-            )
-        }
-
-        // Labels, positioned after the icon
-        AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
-            LargeTileLabels(
-                label = tileViewModel.label.text,
-                secondaryLabel = tileViewModel.appName?.text,
-                colors = colors,
-                modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
-            )
-        }
+    // Labels, positioned after the icon
+    AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
+        LargeTileLabels(
+            label = tile.label.text,
+            secondaryLabel = tile.appName?.text,
+            colors = colors,
+            modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
+        )
     }
 }
 
-@Composable
-private fun EditTileContainer(
-    colors: TileColors,
-    modifier: Modifier = Modifier,
-    selectionAlpha: () -> Float = { 0f },
-    selectionColor: Color = MaterialTheme.colorScheme.primary,
-    content: @Composable BoxScope.() -> Unit = {},
-) {
-    Box(
-        Modifier.wrapContentSize().drawWithContent {
-            drawContent()
-            drawRoundRect(
-                SolidColor(selectionColor),
-                cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
-                style = Stroke(EditModeTileDefaults.SelectedBorderWidth.toPx()),
-                alpha = selectionAlpha(),
-            )
-        }
-    ) {
-        Box(
-            modifier =
-                modifier
-                    .drawBehind {
-                        drawRoundRect(
-                            SolidColor(colors.background),
-                            cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
-                        )
-                    }
-                    .tilePadding(),
-            content = content,
-        )
+private fun Modifier.tileBackground(color: Color): Modifier {
+    return drawBehind {
+        drawRoundRect(SolidColor(color), cornerRadius = CornerRadius(InactiveCornerRadius.toPx()))
     }
 }
 
 private object EditModeTileDefaults {
     const val PLACEHOLDER_ALPHA = .3f
     val EditGridHeaderHeight = 60.dp
-    val SelectedBorderWidth = 2.dp
     val CurrentTilesGridPadding = 8.dp
 
     @Composable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 3ba49ad..e5c2135 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -20,6 +20,7 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.util.fastMap
@@ -29,10 +30,12 @@
 import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
 import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
+import com.android.systemui.qs.panels.ui.compose.bounceableInfo
 import com.android.systemui.qs.panels.ui.compose.rememberEditListState
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileSquishinessViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -45,7 +48,7 @@
 @Inject
 constructor(
     private val iconTilesViewModel: IconTilesViewModel,
-    private val gridSizeViewModel: FixedColumnsSizeViewModel,
+    private val gridSizeViewModel: QSColumnsViewModel,
     private val squishinessViewModel: TileSquishinessViewModel,
 ) : PaginatableGridLayout {
 
@@ -62,7 +65,11 @@
         }
         val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
         val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
+        val bounceables =
+            remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
         val squishiness by squishinessViewModel.squishiness.collectAsStateWithLifecycle()
+        val scope = rememberCoroutineScope()
+        var cellIndex = 0
 
         VerticalSpannedGrid(
             columns = columns,
@@ -71,11 +78,15 @@
             spans = sizedTiles.fastMap { it.width },
         ) { spanIndex ->
             val it = sizedTiles[spanIndex]
+            val column = cellIndex % columns
+            cellIndex += it.width
             Tile(
                 tile = it.tile,
                 iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
                 modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
                 squishiness = { squishiness },
+                coroutineScope = scope,
+                bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 4bd5b2d..52d5261 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -23,8 +23,8 @@
 import android.service.quicksettings.Tile.STATE_INACTIVE
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Arrangement.spacedBy
 import androidx.compose.foundation.layout.Box
@@ -44,6 +44,7 @@
 import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
@@ -61,11 +62,13 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
+import com.android.compose.animation.bounceable
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.panels.ui.compose.BounceableInfo
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
 import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
@@ -74,6 +77,8 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.res.R
 import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 private const val TEST_TAG_SMALL = "qs_tile_small"
 private const val TEST_TAG_LARGE = "qs_tile_large"
@@ -102,9 +107,12 @@
     tile: TileViewModel,
     iconOnly: Boolean,
     squishiness: () -> Float,
+    coroutineScope: CoroutineScope,
+    bounceableInfo: BounceableInfo,
     modifier: Modifier = Modifier,
 ) {
     val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
+    val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
     val resources = resources()
     val uiState = remember(state, resources) { state.toUiState(resources) }
     val colors = TileDefaults.getColorForState(uiState)
@@ -112,7 +120,7 @@
     // TODO(b/361789146): Draw the shapes instead of clipping
     val tileShape = TileDefaults.animateTileShape(uiState.state)
 
-    TileContainer(
+    TileExpandable(
         color =
             if (iconOnly || !uiState.handlesSecondaryClick) {
                 colors.iconBackground
@@ -120,93 +128,101 @@
                 colors.background
             },
         shape = tileShape,
-        iconOnly = iconOnly,
-        onClick = tile::onClick,
-        onLongClick = tile::onLongClick,
-        uiState = uiState,
         squishiness = squishiness,
-        modifier = modifier,
+        modifier =
+            modifier
+                .fillMaxWidth()
+                .bounceable(
+                    bounceable = currentBounceableInfo.bounceable,
+                    previousBounceable = currentBounceableInfo.previousTile,
+                    nextBounceable = currentBounceableInfo.nextTile,
+                    orientation = Orientation.Horizontal,
+                    bounceEnd = currentBounceableInfo.bounceEnd,
+                ),
     ) { expandable ->
-        val icon = getTileIcon(icon = uiState.icon)
-        if (iconOnly) {
-            SmallTileContent(
-                icon = icon,
-                color = colors.icon,
-                modifier = Modifier.align(Alignment.Center),
-            )
-        } else {
-            val iconShape = TileDefaults.animateIconShape(uiState.state)
-            LargeTileContent(
-                label = uiState.label,
-                secondaryLabel = uiState.secondaryLabel,
-                icon = icon,
-                colors = colors,
-                iconShape = iconShape,
-                toggleClickSupported = state.handlesSecondaryClick,
-                onClick = {
-                    if (state.handlesSecondaryClick) {
-                        tile.onSecondaryClick()
-                    }
-                },
-                onLongClick = { tile.onLongClick(expandable) },
-                accessibilityUiState = uiState.accessibilityUiState,
-                squishiness = squishiness,
-            )
+        TileContainer(
+            onClick = {
+                tile.onClick(expandable)
+                if (uiState.accessibilityUiState.toggleableState != null) {
+                    coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
+                }
+            },
+            onLongClick = { tile.onLongClick(expandable) },
+            uiState = uiState,
+            iconOnly = iconOnly,
+        ) {
+            val icon = getTileIcon(icon = uiState.icon)
+            if (iconOnly) {
+                SmallTileContent(
+                    icon = icon,
+                    color = colors.icon,
+                    modifier = Modifier.align(Alignment.Center),
+                )
+            } else {
+                val iconShape = TileDefaults.animateIconShape(uiState.state)
+                LargeTileContent(
+                    label = uiState.label,
+                    secondaryLabel = uiState.secondaryLabel,
+                    icon = icon,
+                    colors = colors,
+                    iconShape = iconShape,
+                    toggleClickSupported = state.handlesSecondaryClick,
+                    onClick = {
+                        if (state.handlesSecondaryClick) {
+                            tile.onSecondaryClick()
+                        }
+                    },
+                    onLongClick = { tile.onLongClick(expandable) },
+                    accessibilityUiState = uiState.accessibilityUiState,
+                    squishiness = squishiness,
+                )
+            }
         }
     }
 }
 
 @Composable
-private fun TileContainer(
+private fun TileExpandable(
     color: Color,
     shape: Shape,
-    iconOnly: Boolean,
-    uiState: TileUiState,
     squishiness: () -> Float,
     modifier: Modifier = Modifier,
-    onClick: (Expandable) -> Unit = {},
-    onLongClick: (Expandable) -> Unit = {},
-    content: @Composable BoxScope.(Expandable) -> Unit,
+    content: @Composable (Expandable) -> Unit,
 ) {
     Expandable(
         color = color,
         shape = shape,
         modifier = modifier.clip(shape).verticalSquish(squishiness),
     ) {
-        val longPressLabel = longPressLabel()
-        Box(
-            modifier =
-                Modifier.height(CommonTileDefaults.TileHeight)
-                    .fillMaxWidth()
-                    .combinedClickable(
-                        onClick = { onClick(it) },
-                        onLongClick = { onLongClick(it) },
-                        onClickLabel = uiState.accessibilityUiState.clickLabel,
-                        onLongClickLabel = longPressLabel,
-                    )
-                    .semantics {
-                        role = uiState.accessibilityUiState.accessibilityRole
-                        if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) {
-                            uiState.accessibilityUiState.toggleableState?.let {
-                                toggleableState = it
-                            }
-                        }
-                        stateDescription = uiState.accessibilityUiState.stateDescription
-                    }
-                    .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
-                    .thenIf(iconOnly) {
-                        Modifier.semantics {
-                            contentDescription = uiState.accessibilityUiState.contentDescription
-                        }
-                    }
-                    .tilePadding()
-        ) {
-            content(it)
-        }
+        content(it)
     }
 }
 
 @Composable
+fun TileContainer(
+    onClick: () -> Unit,
+    onLongClick: () -> Unit,
+    uiState: TileUiState,
+    iconOnly: Boolean,
+    content: @Composable BoxScope.() -> Unit,
+) {
+    Box(
+        modifier =
+            Modifier.height(CommonTileDefaults.TileHeight)
+                .fillMaxWidth()
+                .tileCombinedClickable(
+                    onClick = onClick,
+                    onLongClick = onLongClick,
+                    uiState = uiState,
+                    iconOnly = iconOnly,
+                )
+                .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
+                .tilePadding(),
+        content = content,
+    )
+}
+
+@Composable
 private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon {
     val context = LocalContext.current
     return icon.get()?.let {
@@ -222,14 +238,38 @@
     return spacedBy(space = CommonTileDefaults.TileArrangementPadding, alignment = Alignment.Start)
 }
 
-fun Modifier.tileMarquee(): Modifier {
-    return basicMarquee(iterations = 1, initialDelayMillis = 200)
-}
-
 fun Modifier.tilePadding(): Modifier {
     return padding(CommonTileDefaults.TilePadding)
 }
 
+@Composable
+fun Modifier.tileCombinedClickable(
+    onClick: () -> Unit,
+    onLongClick: () -> Unit,
+    uiState: TileUiState,
+    iconOnly: Boolean,
+): Modifier {
+    val longPressLabel = longPressLabel()
+    return combinedClickable(
+            onClick = onClick,
+            onLongClick = onLongClick,
+            onClickLabel = uiState.accessibilityUiState.clickLabel,
+            onLongClickLabel = longPressLabel,
+        )
+        .semantics {
+            role = uiState.accessibilityUiState.accessibilityRole
+            if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) {
+                uiState.accessibilityUiState.toggleableState?.let { toggleableState = it }
+            }
+            stateDescription = uiState.accessibilityUiState.stateDescription
+        }
+        .thenIf(iconOnly) {
+            Modifier.semantics {
+                contentDescription = uiState.accessibilityUiState.contentDescription
+            }
+        }
+}
+
 data class TileColors(
     val background: Color,
     val iconBackground: Color,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
index 441d962..1d36aee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
@@ -89,8 +89,11 @@
  * Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as
  * they can be selected.
  */
-@Composable
-fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier {
+fun Modifier.selectableTile(
+    tileSpec: TileSpec,
+    selectionState: MutableSelectionState,
+    onClick: () -> Unit = {},
+): Modifier {
     return pointerInput(Unit) {
         detectTapGestures(
             onTap = {
@@ -99,6 +102,7 @@
                 } else {
                     selectionState.select(tileSpec, manual = true)
                 }
+                onClick()
             }
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index e0f0b6a..9f13a37 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -16,63 +16,117 @@
 
 package com.android.systemui.qs.panels.ui.compose.selection
 
+import androidx.compose.animation.core.animateIntAsState
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.gestures.detectHorizontalDragGestures
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.systemGestureExclusion
 import androidx.compose.material3.LocalMinimumInteractiveComponentSize
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.zIndex
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
 import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize
+import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth
 
 /**
- * Dot handling resizing drag events. Use this on the selected tile to resize it
+ * Places a dot to handle resizing drag events. Use this on tiles to resize.
  *
- * @param enabled whether resizing drag events should be handled
+ * The dot is placed vertically centered on the right border. The [content] will have a border when
+ * selected.
+ *
+ * @param selected whether resizing drag events should be handled
  * @param selectionState the [MutableSelectionState] on the grid
- * @param transition the animated value for the dot, used for its alpha and scale
+ * @param selectionAlpha the animated value for the dot and border alpha
+ * @param selectionColor the [Color] of the dot and border
  * @param tileWidths the [TileWidths] of the selected tile
- * @param onResize the callback when the drag passes the resizing threshold
  */
 @Composable
-fun ResizingHandle(
+fun ResizableTileContainer(
+    selected: Boolean,
+    selectionState: MutableSelectionState,
+    selectionAlpha: () -> Float,
+    selectionColor: Color,
+    tileWidths: () -> TileWidths?,
+    modifier: Modifier = Modifier,
+    content: @Composable BoxScope.() -> Unit = {},
+) {
+    Box(
+        modifier
+            .resizable(selected, selectionState, tileWidths)
+            .selectionBorder(selectionColor, selectionAlpha)
+    ) {
+        content()
+        ResizingHandle(
+            enabled = selected,
+            selectionState = selectionState,
+            transition = selectionAlpha,
+            tileWidths = tileWidths,
+            modifier =
+                // Higher zIndex to make sure the handle is drawn above the content
+                Modifier.zIndex(2f),
+        )
+    }
+}
+
+@Composable
+private fun ResizingHandle(
     enabled: Boolean,
     selectionState: MutableSelectionState,
     transition: () -> Float,
-    tileWidths: () -> TileWidths? = { null },
+    tileWidths: () -> TileWidths?,
+    modifier: Modifier = Modifier,
 ) {
-    if (enabled) {
-        // Manually creating the touch target around the resizing dot to ensure that the next tile
-        // does
-        // not receive the touch input accidentally.
-        val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current
-        Box(
-            Modifier.size(minTouchTargetSize)
-                .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) }
-                .pointerInput(Unit) {
-                    detectHorizontalDragGestures(
-                        onHorizontalDrag = { _, offset -> selectionState.onResizingDrag(offset) },
-                        onDragStart = {
-                            tileWidths()?.let { selectionState.onResizingDragStart(it) }
-                        },
-                        onDragEnd = selectionState::onResizingDragEnd,
-                        onDragCancel = selectionState::onResizingDragEnd,
+    // Manually creating the touch target around the resizing dot to ensure that the next tile
+    // does not receive the touch input accidentally.
+    val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current
+    Box(
+        modifier
+            .layout { measurable, constraints ->
+                val size = minTouchTargetSize.roundToPx()
+                val placeable = measurable.measure(Constraints(size, size, size, size))
+                layout(placeable.width, placeable.height) {
+                    placeable.place(
+                        x = constraints.maxWidth - placeable.width / 2,
+                        y = constraints.maxHeight / 2 - placeable.height / 2,
                     )
                 }
-        ) {
-            ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
-        }
-    } else {
-        ResizingDot(transition = transition)
+            }
+            .thenIf(enabled) {
+                Modifier.systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) }
+                    .pointerInput(Unit) {
+                        detectHorizontalDragGestures(
+                            onHorizontalDrag = { _, offset ->
+                                selectionState.onResizingDrag(offset)
+                            },
+                            onDragStart = {
+                                tileWidths()?.let { selectionState.onResizingDragStart(it) }
+                            },
+                            onDragEnd = selectionState::onResizingDragEnd,
+                            onDragCancel = selectionState::onResizingDragEnd,
+                        )
+                    }
+            }
+    ) {
+        ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
     }
 }
 
@@ -88,6 +142,45 @@
     }
 }
 
+private fun Modifier.selectionBorder(
+    selectionColor: Color,
+    selectionAlpha: () -> Float = { 0f },
+): Modifier {
+    return drawWithContent {
+        drawContent()
+        drawRoundRect(
+            SolidColor(selectionColor),
+            cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
+            style = Stroke(SelectedBorderWidth.toPx()),
+            alpha = selectionAlpha(),
+        )
+    }
+}
+
+@Composable
+private fun Modifier.resizable(
+    selected: Boolean,
+    selectionState: MutableSelectionState,
+    tileWidths: () -> TileWidths?,
+): Modifier {
+    if (!selected) return zIndex(1f)
+
+    // Animated diff between the current width and the resized width of the tile. We can't use
+    // animateContentSize here as the tile is sometimes unbounded.
+    val remainingOffset by
+        animateIntAsState(
+            selectionState.resizingState?.let { tileWidths()?.base?.minus(it.width) ?: 0 } ?: 0,
+            label = "QSEditTileWidthOffset",
+        )
+    return zIndex(2f).layout { measurable, constraints ->
+        // Grab the width from the resizing state if a resize is in progress
+        val width = selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset)
+        val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width))
+        layout(constraints.maxWidth, placeable.height) { placeable.place(0, 0) }
+    }
+}
+
 private object SelectionDefaults {
     val ResizingDotSize = 16.dp
+    val SelectedBorderWidth = 2.dp
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
index b1841c4..c0441f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
@@ -31,8 +31,8 @@
 }
 
 /**
- * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's
- * positioned at
+ * Represents a [EditTileViewModel] from a grid associated with a tile format and the row and column
+ * it's positioned at
  */
 @Immutable
 data class TileGridCell(
@@ -41,13 +41,15 @@
     override val width: Int,
     override val span: GridItemSpan = GridItemSpan(width),
     override val s: String = "${tile.tileSpec.spec}-$row-$width",
+    val column: Int,
 ) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile {
     val key: String = "${tile.tileSpec.spec}-$row"
 
     constructor(
         sizedTile: SizedTile<EditTileViewModel>,
         row: Int,
-    ) : this(tile = sizedTile.tile, row = row, width = sizedTile.width)
+        column: Int,
+    ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width)
 }
 
 /** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */
@@ -73,7 +75,13 @@
     return splitInRowsSequence(this, columns)
         .flatMapIndexed { rowIndex, sizedTiles ->
             val correctedRowIndex = rowIndex + startingRow
-            val row: List<GridCell> = sizedTiles.map { TileGridCell(it, correctedRowIndex) }
+            var column = 0
+            val row: List<GridCell> =
+                sizedTiles.map {
+                    TileGridCell(it, correctedRowIndex, column).also { cell ->
+                        column += cell.width
+                    }
+                }
 
             // Fill the incomplete rows with spacers
             val numSpacers = columns - sizedTiles.sumOf { it.width }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt
new file mode 100644
index 0000000..506b052
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Bounceable
+
+class BounceableTileViewModel : Bounceable {
+    private val animatableBounce = Animatable(0.dp, Dp.VectorConverter)
+    override val bounce: Dp
+        get() = animatableBounce.value
+
+    suspend fun animateBounce() {
+        animatableBounce.animateTo(BounceSize)
+        animatableBounce.animateTo(0.dp)
+    }
+
+    private companion object {
+        val BounceSize = 8.dp
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
index ee12736f..be6ce5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
@@ -43,13 +43,13 @@
 ) {
     fun load(context: Context): EditTileViewModel {
         return EditTileViewModel(
-            tileSpec,
-            icon,
-            label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
-            appName?.toAnnotatedString(context),
-            isCurrent,
-            availableEditActions,
-            category,
+            tileSpec = tileSpec,
+            icon = icon,
+            label = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
+            appName = appName?.toAnnotatedString(context),
+            isCurrent = isCurrent,
+            availableEditActions = availableEditActions,
+            category = category,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index d4f8298..78212b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -29,13 +29,13 @@
 @Inject
 constructor(
     iconTilesViewModel: IconTilesViewModel,
-    gridSizeViewModel: FixedColumnsSizeViewModel,
+    gridSizeViewModel: QSColumnsViewModel,
     iconLabelVisibilityViewModel: IconLabelVisibilityViewModel,
     paginatedGridInteractor: PaginatedGridInteractor,
     @Application applicationScope: CoroutineScope,
 ) :
     IconTilesViewModel by iconTilesViewModel,
-    FixedColumnsSizeViewModel by gridSizeViewModel,
+    QSColumnsViewModel by gridSizeViewModel,
     IconLabelVisibilityViewModel by iconLabelVisibilityViewModel {
     val rows =
         paginatedGridInteractor.rows.stateIn(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt
deleted file mode 100644
index 2049edb..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.panels.ui.viewmodel
-
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
-
-@SysUISingleton
-class PartitionedGridViewModel
-@Inject
-constructor(
-    iconTilesViewModel: IconTilesViewModel,
-    gridSizeViewModel: FixedColumnsSizeViewModel,
-    iconLabelVisibilityViewModel: IconLabelVisibilityViewModel,
-) :
-    IconTilesViewModel by iconTilesViewModel,
-    FixedColumnsSizeViewModel by gridSizeViewModel,
-    IconLabelVisibilityViewModel by iconLabelVisibilityViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
similarity index 78%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
index 865c86b..0f1c77e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
@@ -17,16 +17,16 @@
 package com.android.systemui.qs.panels.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.domain.interactor.FixedColumnsSizeInteractor
+import com.android.systemui.qs.panels.domain.interactor.QSColumnsInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.StateFlow
 
-interface FixedColumnsSizeViewModel {
+interface QSColumnsViewModel {
     val columns: StateFlow<Int>
 }
 
 @SysUISingleton
-class FixedColumnsSizeViewModelImpl @Inject constructor(interactor: FixedColumnsSizeInteractor) :
-    FixedColumnsSizeViewModel {
+class QSColumnsSizeViewModelImpl @Inject constructor(interactor: QSColumnsInteractor) :
+    QSColumnsViewModel {
     override val columns: StateFlow<Int> = interactor.columns
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
index 88e3019..72b586a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
@@ -40,14 +40,14 @@
 @Inject
 constructor(
     tilesInteractor: CurrentTilesInteractor,
-    fixedColumnsSizeViewModel: FixedColumnsSizeViewModel,
+    qsColumnsViewModel: QSColumnsViewModel,
     quickQuickSettingsRowInteractor: QuickQuickSettingsRowInteractor,
     val squishinessViewModel: TileSquishinessViewModel,
     private val iconTilesViewModel: IconTilesViewModel,
     @Application private val applicationScope: CoroutineScope,
 ) {
 
-    val columns = fixedColumnsSizeViewModel.columns
+    val columns = qsColumnsViewModel.columns
 
     private val rows =
         quickQuickSettingsRowInteractor.rows.stateIn(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
index b8f4ab4..dde3628 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.tiles.base.viewmodel
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Application
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -28,5 +28,7 @@
 constructor(@Application private val applicationScope: CoroutineScope) {
 
     fun create(): CoroutineScope =
-        CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("QSTileScope"))
+        CoroutineScope(
+            applicationScope.coroutineContext + SupervisorJob() + newTracingContext("QSTileScope")
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index ae56c2a..f674971 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -15,12 +15,12 @@
  */
 package com.android.systemui.qs.tiles.dialog
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.util.Log
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -63,7 +63,7 @@
             }
             return
         } else {
-            coroutineScope = CoroutineScope(bgDispatcher + createCoroutineTracingContext("InternetDialogScope"))
+            coroutineScope = CoroutineScope(bgDispatcher + newTracingContext("InternetDialogScope"))
             dialog =
                 dialogFactory
                     .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
new file mode 100644
index 0000000..8dd611f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.hearingdevices.domain
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [HearingDevicesTileModel] to [QSTileState]. */
+class HearingDevicesTileMapper
+@Inject
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+    QSTileDataToStateMapper<HearingDevicesTileModel> {
+
+    override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState =
+        QSTileState.build(resources, theme, config.uiConfig) {
+            label = resources.getString(R.string.quick_settings_hearing_devices_label)
+            iconRes = R.drawable.qs_hearing_devices_icon
+            val loadedIcon =
+                Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+            icon = { loadedIcon }
+            sideViewIcon = QSTileState.SideViewIcon.Chevron
+            contentDescription = label
+            if (data.isAnyActiveHearingDevice) {
+                activationState = QSTileState.ActivationState.ACTIVE
+                secondaryLabel =
+                    resources.getString(R.string.quick_settings_hearing_devices_connected)
+            } else if (data.isAnyPairedHearingDevice) {
+                activationState = QSTileState.ActivationState.INACTIVE
+                secondaryLabel =
+                    resources.getString(R.string.quick_settings_hearing_devices_disconnected)
+            } else {
+                activationState = QSTileState.ActivationState.INACTIVE
+                secondaryLabel = ""
+            }
+            supportedActions =
+                setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
new file mode 100644
index 0000000..ec0a4e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.Flags
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.BluetoothController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+
+/** Observes hearing devices state changes providing the [HearingDevicesTileModel]. */
+class HearingDevicesTileDataInteractor
+@Inject
+constructor(
+    @Background private val backgroundContext: CoroutineContext,
+    private val bluetoothController: BluetoothController,
+    private val hearingDevicesChecker: HearingDevicesChecker,
+) : QSTileDataInteractor<HearingDevicesTileModel> {
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>,
+    ): Flow<HearingDevicesTileModel> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : BluetoothController.Callback {
+                        override fun onBluetoothStateChange(enabled: Boolean) {
+                            trySend(getModel())
+                        }
+
+                        override fun onBluetoothDevicesChanged() {
+                            trySend(getModel())
+                        }
+                    }
+                bluetoothController.addCallback(callback)
+                awaitClose { bluetoothController.removeCallback(callback) }
+            }
+            .flowOn(backgroundContext)
+            .distinctUntilChanged()
+
+    override fun availability(user: UserHandle): Flow<Boolean> =
+        flowOf(Flags.hearingAidsQsTileDialog())
+
+    private fun getModel() =
+        HearingDevicesTileModel(
+            hearingDevicesChecker.isAnyActiveHearingDevice,
+            hearingDevicesChecker.isAnyPairedHearingDevice,
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
new file mode 100644
index 0000000..5e7172e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+/** Handles hearing devices tile clicks. */
+class HearingDevicesTileUserActionInteractor
+@Inject
+constructor(
+    @Main private val mainContext: CoroutineContext,
+    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+    private val hearingDevicesDialogManager: HearingDevicesDialogManager,
+) : QSTileUserActionInteractor<HearingDevicesTileModel> {
+
+    override suspend fun handleInput(input: QSTileInput<HearingDevicesTileModel>) =
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click -> {
+                    withContext(mainContext) {
+                        hearingDevicesDialogManager.showDialog(
+                            action.expandable,
+                            LAUNCH_SOURCE_QS_TILE,
+                        )
+                    }
+                }
+                is QSTileUserAction.LongClick -> {
+                    qsTileIntentUserActionHandler.handle(
+                        action.expandable,
+                        Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS),
+                    )
+                }
+                is QSTileUserAction.ToggleClick -> {}
+            }
+        }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
index 2f5daaa..4e37b77 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.qs.tiles.impl.hearingdevices.domain.model
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+/** Hearing devices tile model */
+data class HearingDevicesTileModel(
+    val isAnyActiveHearingDevice: Boolean,
+    val isAnyPairedHearingDevice: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
index ac75932..1411544 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.qs.tiles.impl.location.domain.interactor
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.content.Intent
 import android.provider.Settings
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.ActivityStarter
@@ -53,9 +53,11 @@
                     val wasEnabled: Boolean = input.data.isEnabled
                     if (keyguardController.isMethodSecure() && keyguardController.isShowing()) {
                         activityStarter.postQSRunnableDismissingKeyguard {
-                            CoroutineScope(applicationScope.coroutineContext + createCoroutineTracingContext("LocationTileScope")).launch {
-                                locationController.setLocationEnabled(!wasEnabled)
-                            }
+                            CoroutineScope(
+                                    applicationScope.coroutineContext +
+                                        newTracingContext("LocationTileScope")
+                                )
+                                .launch { locationController.setLocationEnabled(!wasEnabled) }
                         }
                     } else {
                         withContext(coroutineContext) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 5d44ead..3e442582 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -18,7 +18,7 @@
 
 import android.content.Context
 import android.os.UserHandle
-import com.android.app.tracing.coroutines.flow.map
+import com.android.app.tracing.coroutines.flow.flowName
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.asIcon
 import com.android.systemui.dagger.qualifiers.Background
@@ -37,6 +37,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 
 class ModesTileDataInteractor
 @Inject
@@ -59,6 +60,7 @@
     fun tileData() =
         zenModeInteractor.activeModes
             .map { activeModes -> buildTileData(activeModes) }
+            .flowName("tileData")
             .flowOn(bgDispatcher)
             .distinctUntilChanged()
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
index 8e80fb0..33dc6ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.impl.rotation.ui.mapper
 
 import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
@@ -25,6 +26,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.util.Utils.isDeviceFoldable
 import javax.inject.Inject
 
 /** Maps [RotationLockTileModel] to [QSTileState]. */
@@ -33,7 +35,8 @@
 constructor(
     @Main private val resources: Resources,
     private val theme: Resources.Theme,
-    private val devicePostureController: DevicePostureController
+    private val devicePostureController: DevicePostureController,
+    private val deviceStateManager: DeviceStateManager
 ) : QSTileDataToStateMapper<RotationLockTileModel> {
     override fun map(config: QSTileConfig, data: RotationLockTileModel): QSTileState =
         QSTileState.build(resources, theme, config.uiConfig) {
@@ -58,7 +61,7 @@
             this.icon = {
                 Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
             }
-            if (isDeviceFoldable()) {
+            if (isDeviceFoldable(resources, deviceStateManager)) {
                 this.secondaryLabel = getSecondaryLabelWithPosture(this.activationState)
             }
             this.stateDescription = this.secondaryLabel
@@ -67,11 +70,6 @@
                 setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
         }
 
-    private fun isDeviceFoldable(): Boolean {
-        val intArray = resources.getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
-        return intArray.isNotEmpty()
-    }
-
     private fun getSecondaryLabelWithPosture(activationState: QSTileState.ActivationState): String {
         val stateNames = resources.getStringArray(R.array.tile_states_rotation)
         val stateName =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
index 468e180..f03c752 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
@@ -16,12 +16,12 @@
 
 package com.android.systemui.qs.tiles.impl.saver.domain
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.content.Context
 import android.content.DialogInterface
 import android.content.SharedPreferences
 import android.os.Bundle
 import com.android.internal.R
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.DataSaverController
@@ -45,9 +45,8 @@
             setTitle(R.string.data_saver_enable_title)
             setMessage(R.string.data_saver_description)
             setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ ->
-                CoroutineScope(backgroundContext + createCoroutineTracingContext("DataSaverDialogScope")).launch {
-                    dataSaverController.setDataSaverEnabled(true)
-                }
+                CoroutineScope(backgroundContext + newTracingContext("DataSaverDialogScope"))
+                    .launch { dataSaverController.setDataSaverEnabled(true) }
 
                 sharedPreferences
                     .edit()
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
index f02b871..a32bf1e 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.reardisplay;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -26,6 +28,7 @@
 import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.devicestate.DeviceStateManagerGlobal;
+import android.hardware.devicestate.feature.flags.Flags;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
@@ -42,6 +45,8 @@
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieDrawable;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -66,11 +71,12 @@
         ConfigurationController.ConfigurationListener,
         CommandQueue.Callbacks {
 
-    private int[] mFoldedStates;
+    private List<Integer> mFoldedStates;
     private boolean mStartedFolded;
     private boolean mServiceNotified = false;
     private int mAnimationRepeatCount = LottieDrawable.INFINITE;
 
+    private final DeviceStateManager mDeviceStateManager;
     private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
     private DeviceStateManager.DeviceStateCallback mDeviceStateManagerCallback =
             new DeviceStateManagerCallback();
@@ -90,12 +96,14 @@
             @Main Executor executor,
             @Main Resources resources,
             LayoutInflater layoutInflater,
-            SystemUIDialog.Factory systemUIDialogFactory) {
+            SystemUIDialog.Factory systemUIDialogFactory,
+            DeviceStateManager deviceStateManager) {
         mCommandQueue = commandQueue;
         mExecutor = executor;
         mResources = resources;
         mLayoutInflater = layoutInflater;
         mSystemUIDialogFactory = systemUIDialogFactory;
+        mDeviceStateManager = deviceStateManager;
     }
 
     @Override
@@ -180,10 +188,23 @@
      */
     private void initializeValues(int startingBaseState) {
         mRearDisplayEducationDialog = mSystemUIDialogFactory.create();
-        // TODO(b/329170810): Refactor and remove with updated DeviceStateManager values.
-        if (mFoldedStates == null) {
-            mFoldedStates = mResources.getIntArray(
-                    com.android.internal.R.array.config_foldedDeviceStates);
+        if (Flags.deviceStatePropertyMigration()) {
+            if (mFoldedStates == null) {
+                mFoldedStates = new ArrayList<>();
+                List<DeviceState> deviceStates = mDeviceStateManager.getSupportedDeviceStates();
+                for (int i = 0; i < deviceStates.size(); i++) {
+                    DeviceState state = deviceStates.get(i);
+                    if (state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) {
+                        mFoldedStates.add(state.getIdentifier());
+                    }
+                }
+            }
+        } else {
+            // TODO(b/329170810): Refactor and remove with updated DeviceStateManager values.
+            if (mFoldedStates == null) {
+                mFoldedStates = copyIntArrayToList(mResources.getIntArray(
+                        com.android.internal.R.array.config_foldedDeviceStates));
+            }
         }
         mStartedFolded = isFoldedState(startingBaseState);
         mDeviceStateManagerGlobal = DeviceStateManagerGlobal.getInstance();
@@ -191,9 +212,17 @@
                 mExecutor);
     }
 
+    private List<Integer> copyIntArrayToList(int[] intArray) {
+        List<Integer> integerList = new ArrayList<>(intArray.length);
+        for (int i = 0; i < intArray.length; i++) {
+            integerList.add(intArray[i]);
+        }
+        return integerList;
+    }
+
     private boolean isFoldedState(int state) {
-        for (int i = 0; i < mFoldedStates.length; i++) {
-            if (mFoldedStates[i] == state) return true;
+        for (int i = 0; i < mFoldedStates.size(); i++) {
+            if (mFoldedStates.get(i) == state) return true;
         }
         return false;
     }
@@ -213,7 +242,7 @@
      * TestAPI to allow us to set the folded states array, instead of reading from resources.
      */
     @TestApi
-    void setFoldedStates(int[] foldedStates) {
+    void setFoldedStates(List<Integer> foldedStates) {
         mFoldedStates = foldedStates;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 559c263..ce9c441 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -87,7 +87,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -160,8 +159,6 @@
     private final Provider<SceneInteractor> mSceneInteractor;
     private final Provider<ShadeInteractor> mShadeInteractor;
 
-    private final KeyboardTouchpadEduStatsInteractor mKeyboardTouchpadEduStatsInteractor;
-
     private final Runnable mConnectionRunnable = () ->
             internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
     private final ComponentName mRecentsComponentName;
@@ -660,8 +657,7 @@
             AssistUtils assistUtils,
             DumpManager dumpManager,
             Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
-            BroadcastDispatcher broadcastDispatcher,
-            KeyboardTouchpadEduStatsInteractor keyboardTouchpadEduStatsInteractor
+            BroadcastDispatcher broadcastDispatcher
     ) {
         // b/241601880: This component should only be running for primary users or
         // secondaryUsers when visibleBackgroundUsers are supported.
@@ -699,7 +695,6 @@
         mDisplayTracker = displayTracker;
         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
         mBroadcastDispatcher = broadcastDispatcher;
-        mKeyboardTouchpadEduStatsInteractor = keyboardTouchpadEduStatsInteractor;
 
         if (!KeyguardWmStateRefactor.isEnabled()) {
             mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
@@ -940,19 +935,6 @@
         return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode);
     }
 
-    /**
-     * Updates contextual education stats when a gesture is triggered
-     * @param isTrackpadGesture indicates if the gesture is triggered by trackpad
-     * @param gestureType type of gesture triggered
-     */
-    public void updateContextualEduStats(boolean isTrackpadGesture, GestureType gestureType) {
-        if (isTrackpadGesture) {
-            mKeyboardTouchpadEduStatsInteractor.updateShortcutTriggerTime(gestureType);
-        } else {
-            mKeyboardTouchpadEduStatsInteractor.incrementSignalCount(gestureType);
-        }
-    }
-
     public boolean isEnabled() {
         return mIsEnabled;
     }
@@ -978,6 +960,17 @@
         }
     }
 
+    /**
+     * Updates contextual education stats when a gesture is triggered
+     * @param isTrackpadGesture indicates if the gesture is triggered by trackpad
+     * @param gestureType type of gesture triggered
+     */
+    public void updateContextualEduStats(boolean isTrackpadGesture, GestureType gestureType) {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).updateContextualEduStats(isTrackpadGesture, gestureType);
+        }
+    }
+
     private void notifyHomeRotationEnabled(boolean enabled) {
         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
             mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled);
@@ -1207,6 +1200,9 @@
         /** Set override of home button long press duration, touch slop multiplier, and haptic. */
         default void setOverrideHomeButtonLongPress(
                 long override, float slopMultiplier, boolean haptic) {}
+        /** Updates contextual education stats when target gesture type is triggered. */
+        default void updateContextualEduStats(
+                boolean isTrackpadGesture, GestureType gestureType) {}
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 7b6b0f6..6097ef5 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -20,7 +20,6 @@
 
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 import com.android.systemui.Flags.sceneContainer
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -42,8 +41,7 @@
                 KeyguardWmStateRefactor.isEnabled &&
                 MigrateClocksToBlueprint.isEnabled &&
                 NotificationThrottleHun.isEnabled &&
-                PredictiveBackSysUiFlag.isEnabled &&
-                DeviceEntryUdfpsRefactor.isEnabled
+                PredictiveBackSysUiFlag.isEnabled
 
     // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
 
@@ -58,7 +56,6 @@
             MigrateClocksToBlueprint.token,
             NotificationThrottleHun.token,
             PredictiveBackSysUiFlag.token,
-            DeviceEntryUdfpsRefactor.token,
             // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index 8a2e274..cd38ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -6,6 +6,7 @@
 import android.view.View
 import android.view.WindowInsets
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.scene.ui.composable.Overlay
@@ -13,19 +14,13 @@
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
 import com.android.systemui.shade.TouchLogger
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import javax.inject.Provider
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 
 /** A root view of the main SysUI window that supports scenes. */
 @ExperimentalCoroutinesApi
-class SceneWindowRootView(
-    context: Context,
-    attrs: AttributeSet?,
-) :
-    WindowRootView(
-        context,
-        attrs,
-    ) {
+class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootView(context, attrs) {
 
     private var motionEventHandler: SceneContainerViewModel.MotionEventHandler? = null
     // TODO(b/298525212): remove once Compose exposes window inset bounds.
@@ -39,6 +34,7 @@
         overlays: Set<Overlay>,
         layoutInsetController: LayoutInsetsController,
         sceneDataSourceDelegator: SceneDataSourceDelegator,
+        qsSceneAdapter: Provider<QSSceneAdapter>,
         alternateBouncerDependencies: AlternateBouncerDependencies,
     ) {
         setLayoutInsetsController(layoutInsetController)
@@ -57,6 +53,7 @@
                 super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE)
             },
             dataSourceDelegator = sceneDataSourceDelegator,
+            qsSceneAdapter = qsSceneAdapter,
             alternateBouncerDependencies = alternateBouncerDependencies,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 38f4e73..1e3a233 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.lifecycle.setSnapshotBinding
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.SceneContainerConfig
@@ -51,6 +52,7 @@
 import com.android.systemui.scene.ui.composable.SceneContainer
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
@@ -74,6 +76,7 @@
         overlays: Set<Overlay>,
         onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
         dataSourceDelegator: SceneDataSourceDelegator,
+        qsSceneAdapter: Provider<QSSceneAdapter>,
         alternateBouncerDependencies: AlternateBouncerDependencies,
     ) {
         val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
@@ -132,6 +135,7 @@
                                 sceneByKey = sortedSceneByKey,
                                 overlayByKey = sortedOverlayByKey,
                                 dataSourceDelegator = dataSourceDelegator,
+                                qsSceneAdapter = qsSceneAdapter,
                                 containerConfig = containerConfig,
                             )
                             .also { it.id = R.id.scene_container_root_composable }
@@ -177,6 +181,7 @@
         sceneByKey: Map<SceneKey, Scene>,
         overlayByKey: Map<OverlayKey, Overlay>,
         dataSourceDelegator: SceneDataSourceDelegator,
+        qsSceneAdapter: Provider<QSSceneAdapter>,
         containerConfig: SceneContainerConfig,
     ): View {
         return ComposeView(context).apply {
@@ -192,6 +197,7 @@
                             overlayByKey = overlayByKey,
                             initialSceneKey = containerConfig.initialSceneKey,
                             dataSourceDelegator = dataSourceDelegator,
+                            qsSceneAdapter = qsSceneAdapter,
                         )
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 889380a..47254775 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -19,7 +19,7 @@
 import android.view.MotionEvent
 import android.view.View
 import androidx.compose.runtime.getValue
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.DefaultEdgeDetector
 import com.android.compose.animation.scene.ObservableTransitionState
@@ -73,6 +73,8 @@
     /** Whether the container is visible. */
     val isVisible: Boolean by hydrator.hydratedStateOf("isVisible", sceneInteractor.isVisible)
 
+    val allContentKeys: List<ContentKey> = sceneInteractor.allContentKeys
+
     private val hapticsViewModel = hapticsViewModelFactory.create(view)
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 700253ba..c3de067 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -61,6 +61,9 @@
 
     private static final int USER_ID_NOT_SPECIFIED = -1;
     protected static final int NOTIF_BASE_ID = 4273;
+    protected static final int NOTIF_GROUP_ID_SAVED = NOTIF_BASE_ID + 1;
+    protected static final int NOTIF_GROUP_ID_ERROR_SAVING = NOTIF_BASE_ID + 2;
+    protected static final int NOTIF_GROUP_ID_ERROR_STARTING = NOTIF_BASE_ID + 3;
     private static final String TAG = "RecordingService";
     private static final String CHANNEL_ID = "screen_record";
     @VisibleForTesting static final String GROUP_KEY_SAVED = "screen_record_saved";
@@ -280,7 +283,11 @@
      */
     @VisibleForTesting
     protected void createErrorStartingNotification(UserHandle currentUser) {
-        createErrorNotification(currentUser, strings().getStartError(), GROUP_KEY_ERROR_STARTING);
+        createErrorNotification(
+                currentUser,
+                strings().getStartError(),
+                GROUP_KEY_ERROR_STARTING,
+                NOTIF_GROUP_ID_ERROR_STARTING);
     }
 
     /**
@@ -289,13 +296,21 @@
      */
     @VisibleForTesting
     protected void createErrorSavingNotification(UserHandle currentUser) {
-        createErrorNotification(currentUser, strings().getSaveError(), GROUP_KEY_ERROR_SAVING);
+        createErrorNotification(
+                currentUser,
+                strings().getSaveError(),
+                GROUP_KEY_ERROR_SAVING,
+                NOTIF_GROUP_ID_ERROR_SAVING);
     }
 
     private void createErrorNotification(
-            UserHandle currentUser, String notificationContentTitle, String groupKey) {
+            UserHandle currentUser,
+            String notificationContentTitle,
+            String groupKey,
+            int notificationIdForGroup) {
         // Make sure error notifications get their own group.
-        postGroupSummaryNotification(currentUser, notificationContentTitle, groupKey);
+        postGroupSummaryNotification(
+                currentUser, notificationContentTitle, groupKey, notificationIdForGroup);
 
         Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, strings().getTitle());
@@ -410,17 +425,20 @@
     }
 
     /**
-     * Adds a group summary notification for save notifications so that save notifications from
-     * multiple recordings are grouped together, and the foreground service recording notification
-     * is not.
+     * Posts a group summary notification for the given group.
+     *
+     * Notifications that should be grouped:
+     *  - Save notifications
+     *  - Error saving notifications
+     *  - Error starting notifications
+     *
+     * The foreground service recording notification should never be grouped.
      */
-    private void postGroupSummaryNotificationForSaves(UserHandle currentUser) {
-        postGroupSummaryNotification(currentUser, strings().getSaveTitle(), GROUP_KEY_SAVED);
-    }
-
-    /** Posts a group summary notification for the given group. */
     private void postGroupSummaryNotification(
-            UserHandle currentUser, String notificationContentTitle, String groupKey) {
+            UserHandle currentUser,
+            String notificationContentTitle,
+            String groupKey,
+            int notificationIdForGroup) {
         Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
                 strings().getTitle());
@@ -431,7 +449,8 @@
                 .setGroupSummary(true)
                 .setExtras(extras)
                 .build();
-        mNotificationManager.notifyAsUser(getTag(), mNotificationId, groupNotif, currentUser);
+        mNotificationManager.notifyAsUser(
+                getTag(), notificationIdForGroup, groupNotif, currentUser);
     }
 
     private void stopService() {
@@ -484,7 +503,11 @@
                 Log.d(getTag(), "saving recording");
                 Notification notification = createSaveNotification(
                         getRecorder() != null ? getRecorder().save() : null);
-                postGroupSummaryNotificationForSaves(currentUser);
+                postGroupSummaryNotification(
+                        currentUser,
+                        strings().getSaveTitle(),
+                        GROUP_KEY_SAVED,
+                        NOTIF_GROUP_ID_SAVED);
                 mNotificationManager.notifyAsUser(null, mNotificationId,  notification,
                         currentUser);
             } catch (IOException | IllegalStateException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
index 54e0319..17b1b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
@@ -26,7 +26,7 @@
 import android.util.Log
 import android.util.Pair
 import android.view.Window
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.app.ChooserActivity
 import com.android.systemui.dagger.qualifiers.Application
 import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 9e62280..7b01c36 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -31,7 +31,7 @@
 import android.view.RemoteAnimationTarget
 import android.view.WindowManager
 import android.view.WindowManagerGlobal
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.infra.ServiceConnector
 import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt b/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt
index 7b56688..6d3b9aa 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt
@@ -19,7 +19,6 @@
 import android.net.Uri
 import android.os.UserManager
 import android.util.Log
-import android.view.WindowManager
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.res.R
@@ -51,10 +50,6 @@
         finisher: Consumer<Uri?>,
         requestCallback: TakeScreenshotService.RequestCallback,
     ) {
-        if (screenshot.type == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) {
-            screenshot.bitmap = imageCapture.captureDisplay(screenshot.displayId, crop = null)
-        }
-
         if (screenshot.bitmap == null) {
             Log.e(TAG, "handleScreenshot: Screenshot bitmap was null")
             notificationsControllerFactory
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
index 3920d58..2259b55 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
@@ -255,12 +255,6 @@
         Assert.isMainThread();
 
         mCurrentRequestCallback = requestCallback;
-        Rect bounds = screenshot.getOriginalScreenBounds();
-        if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-                && screenshot.getBitmap() == null) {
-            bounds = getFullScreenRect();
-            screenshot.setBitmap(mImageCapture.captureDisplay(mDisplay.getDisplayId(), bounds));
-        }
 
         if (screenshot.getBitmap() == null) {
             Log.e(TAG, "handleScreenshot: Screenshot bitmap was null");
@@ -319,10 +313,13 @@
         setWindowFocusable(true);
         mViewProxy.requestFocus();
 
-        enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+        if (screenshot.getType() != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+        }
 
         attachWindow();
 
+        Rect bounds = screenshot.getOriginalScreenBounds();
         boolean showFlash;
         if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
             if (bounds != null
@@ -646,7 +643,7 @@
     private void saveScreenshotInBackground(ScreenshotData screenshot, UUID requestId,
             Consumer<Uri> finisher, Consumer<ImageExporter.Result> onResult) {
         ListenableFuture<ImageExporter.Result> future = mImageExporter.export(mBgExecutor,
-                requestId, screenshot.getBitmap(), screenshot.getUserOrDefault(),
+                requestId, screenshot.getBitmap(), screenshot.getUserHandle(),
                 mDisplay.getDisplayId());
         future.addListener(() -> {
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index c216f1d..1c232e9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -18,13 +18,16 @@
 
 import android.app.assist.AssistContent
 import android.content.Context
+import android.net.Uri
 import android.util.Log
 import androidx.appcompat.content.res.AppCompatResources
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags.screenshotContextUrl
 import com.android.systemui.log.DebugLogger.debugLog
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.ActionIntentCreator.createEdit
 import com.android.systemui.screenshot.ActionIntentCreator.createShareWithSubject
+import com.android.systemui.screenshot.ActionIntentCreator.createShareWithText
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED
@@ -76,6 +79,7 @@
     private var onScrollClick: Runnable? = null
     private var pendingAction: ((ScreenshotSavedResult) -> Unit)? = null
     private var result: ScreenshotSavedResult? = null
+    private var webUri: Uri? = null
 
     init {
         actionsCallback.providePreviewAction(
@@ -86,7 +90,7 @@
                     actionExecutor.startSharedTransition(
                         createEdit(result.uri, context),
                         result.user,
-                        true
+                        true,
                     )
                 }
             }
@@ -103,11 +107,14 @@
             debugLog(LogConfig.DEBUG_ACTIONS) { "Share tapped" }
             uiEventLogger.log(SCREENSHOT_SHARE_TAPPED, 0, request.packageNameString)
             onDeferrableActionTapped { result ->
-                actionExecutor.startSharedTransition(
-                    createShareWithSubject(result.uri, result.subject),
-                    result.user,
-                    false
-                )
+                val uri = webUri
+                val shareIntent =
+                    if (screenshotContextUrl() && uri != null) {
+                        createShareWithText(result.uri, extraText = uri.toString())
+                    } else {
+                        createShareWithSubject(result.uri, result.subject)
+                    }
+                actionExecutor.startSharedTransition(shareIntent, result.user, false)
             }
         }
 
@@ -125,7 +132,7 @@
                 actionExecutor.startSharedTransition(
                     createEdit(result.uri, context),
                     result.user,
-                    true
+                    true,
                 )
             }
         }
@@ -161,6 +168,10 @@
         pendingAction?.invoke(result)
     }
 
+    override fun onAssistContent(assistContent: AssistContent?) {
+        webUri = assistContent?.webUri
+    }
+
     private fun onDeferrableActionTapped(onResult: (ScreenshotSavedResult) -> Unit) {
         result?.let { onResult.invoke(it) } ?: run { pendingAction = onResult }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index e3fbb60..08214c4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -35,7 +35,7 @@
 import android.view.Display
 import android.view.ScrollCaptureResponse
 import android.view.ViewRootImpl.ActivityConfigCallback
-import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import android.widget.Toast
 import android.window.WindowContext
@@ -162,11 +162,6 @@
         Assert.isMainThread()
         screenshotHandler.resetTimeout()
 
-        if (screenshot.type == TAKE_SCREENSHOT_FULLSCREEN && screenshot.bitmap == null) {
-            val bounds = fullScreenRect
-            screenshot.bitmap = imageCapture.captureDisplay(display.displayId, bounds)
-        }
-
         val currentBitmap = screenshot.bitmap
         if (currentBitmap == null) {
             Log.e(TAG, "handleScreenshot: Screenshot bitmap was null")
@@ -223,7 +218,9 @@
         window.setFocusable(true)
         viewProxy.requestFocus()
 
-        enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+        if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+        }
 
         window.attachWindow()
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
index c390e71..b5b15a9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
@@ -4,7 +4,6 @@
 import android.graphics.Bitmap
 import android.graphics.Insets
 import android.graphics.Rect
-import android.os.Process
 import android.os.UserHandle
 import android.view.Display
 import android.view.WindowManager
@@ -30,10 +29,6 @@
     val packageNameString
         get() = topComponent?.packageName ?: ""
 
-    fun getUserOrDefault(): UserHandle {
-        return userHandle ?: Process.myUserHandle()
-    }
-
     companion object {
         @JvmStatic
         fun fromRequest(request: ScreenshotRequest, displayId: Int = Display.DEFAULT_DISPLAY) =
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index c8067df..6df22f0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -21,7 +21,7 @@
 import android.util.Log
 import androidx.lifecycle.LifecycleService
 import androidx.lifecycle.lifecycleScope
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.shade.ShadeExpansionStateManager
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index d3a7fc4a..7aeec47 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -18,7 +18,7 @@
 
 import android.media.MediaPlayer
 import android.util.Log
-import com.android.app.tracing.coroutines.async
+import com.android.app.tracing.coroutines.asyncTraced as async
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
index 0ef5207..9455201 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt
@@ -24,8 +24,8 @@
     data class FullScreen(val displayId: Int) : CaptureType
 
     /** Capture the contents of the task only. */
-    data class IsolatedTask(
-        val taskId: Int,
-        val taskBounds: Rect?,
-    ) : CaptureType
+    data class IsolatedTask(val taskId: Int, val taskBounds: Rect?) : CaptureType
+
+    data class RootTask(val parentTaskId: Int, val taskBounds: Rect?, val childTaskIds: List<Int>) :
+        CaptureType
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
index a94393b..e840668 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
@@ -26,6 +26,7 @@
 import android.util.Log
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import com.android.systemui.Flags.screenshotPolicySplitAndDesktopMode
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.screenshot.ImageCapture
 import com.android.systemui.screenshot.ScreenshotData
@@ -47,14 +48,17 @@
     private val capture: ImageCapture,
     /** Provides information about the tasks on a given display */
     private val displayTasks: DisplayContentRepository,
-    /** The list of policies to apply, in order of priority */
+    /** The legacy list of policy implementations to apply, in order of priority */
     private val policies: List<CapturePolicy>,
+    /** Implements the combined policy rules for all profile types. */
+    private val policy: ScreenshotPolicy,
     /** The owner to assign for screenshot when a focused task isn't visible */
     private val defaultOwner: UserHandle = myUserHandle(),
     /** The assigned component when no application has focus, or not visible */
     private val defaultComponent: ComponentName,
 ) : ScreenshotRequestProcessor {
     override suspend fun process(original: ScreenshotData): ScreenshotData {
+
         if (original.type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
             // The request contains an already captured screenshot, accept it as is.
             Log.i(TAG, "Screenshot bitmap provided. No modifications applied.")
@@ -62,6 +66,12 @@
         }
         val displayContent = displayTasks.getDisplayContent(original.displayId)
 
+        if (screenshotPolicySplitAndDesktopMode()) {
+            Log.i(TAG, "Applying screenshot policy....")
+            val type = policy.apply(displayContent, defaultComponent, defaultOwner)
+            return modify(original, type)
+        }
+
         // If policies yield explicit modifications, apply them and return the result
         Log.i(TAG, "Applying policy checks....")
         policies.map { policy ->
@@ -79,10 +89,8 @@
     }
 
     /** Produce a new [ScreenshotData] using [CaptureParameters] */
-    private suspend fun modify(
-        original: ScreenshotData,
-        updates: CaptureParameters,
-    ): ScreenshotData {
+    suspend fun modify(original: ScreenshotData, updates: CaptureParameters): ScreenshotData {
+        Log.d(TAG, "[modify] CaptureParameters = $updates")
         // Update and apply bitmap capture depending on the parameters.
         val updated =
             when (val type = updates.type) {
@@ -94,6 +102,14 @@
                         type.taskId,
                         type.taskBounds,
                     )
+                is CaptureType.RootTask ->
+                    replaceWithTaskSnapshot(
+                        original,
+                        updates.component,
+                        updates.owner,
+                        type.parentTaskId,
+                        type.taskBounds,
+                    )
                 is FullScreen ->
                     replaceWithScreenshot(
                         original,
@@ -126,7 +142,7 @@
         )
     }
 
-    suspend fun replaceWithTaskSnapshot(
+    private suspend fun replaceWithTaskSnapshot(
         original: ScreenshotData,
         componentName: ComponentName?,
         owner: UserHandle,
@@ -134,7 +150,7 @@
         taskBounds: Rect?,
     ): ScreenshotData {
         Log.i(TAG, "Capturing task snapshot: $componentName / $owner")
-        val taskSnapshot = capture.captureTask(taskId)
+        val taskSnapshot = capture.captureTask(taskId) ?: error("Failed to capture task")
         return original.copy(
             type = TAKE_SCREENSHOT_PROVIDED_IMAGE,
             bitmap = taskSnapshot,
@@ -153,13 +169,13 @@
         taskId: Int? = null,
     ): ScreenshotData {
         Log.i(TAG, "Capturing screenshot: $componentName / $owner")
-        val screenshot = captureDisplay(displayId)
+        val screenshot = captureDisplay(displayId) ?: error("Failed to capture screenshot")
         return original.copy(
             type = TAKE_SCREENSHOT_FULLSCREEN,
             bitmap = screenshot,
             userHandle = owner,
             topComponent = componentName,
-            originalScreenBounds = Rect(0, 0, screenshot?.width ?: 0, screenshot?.height ?: 0),
+            originalScreenBounds = Rect(0, 0, screenshot.width, screenshot.height),
             taskId = taskId ?: -1,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
index f768cfb..dd39f92 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt
@@ -26,9 +26,11 @@
             childTaskIds[index],
             childTaskNames[index],
             childTaskBounds[index],
-            childTaskUserIds[index]
+            childTaskUserIds[index],
         )
     }
 }
 
 internal fun RootTaskInfo.hasChildTasks() = childTaskUserIds.isNotEmpty()
+
+internal fun RootTaskInfo.childTaskCount() = childTaskIds.size
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt
new file mode 100644
index 0000000..9967aff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicy.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.policy
+
+import android.app.ActivityTaskManager.RootTaskInfo
+import android.app.WindowConfiguration
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.content.ComponentName
+import android.os.UserHandle
+import android.util.Log
+import com.android.systemui.screenshot.data.model.DisplayContentModel
+import com.android.systemui.screenshot.data.model.ProfileType
+import com.android.systemui.screenshot.data.model.ProfileType.PRIVATE
+import com.android.systemui.screenshot.data.model.ProfileType.WORK
+import com.android.systemui.screenshot.data.repository.ProfileTypeRepository
+import com.android.systemui.screenshot.policy.CaptureType.FullScreen
+import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
+import com.android.systemui.screenshot.policy.CaptureType.RootTask
+import javax.inject.Inject
+
+private const val TAG = "ScreenshotPolicy"
+
+/** Determines what to capture and which user owns the output. */
+class ScreenshotPolicy @Inject constructor(private val profileTypes: ProfileTypeRepository) {
+    /**
+     * Apply the policy to the content, resulting in [CaptureParameters].
+     *
+     * @param content the content of the display
+     * @param defaultComponent the component associated with the screenshot by default
+     * @param defaultOwner the user to own the screenshot by default
+     */
+    suspend fun apply(
+        content: DisplayContentModel,
+        defaultComponent: ComponentName,
+        defaultOwner: UserHandle,
+    ): CaptureParameters {
+        val defaultFullScreen by lazy {
+            CaptureParameters(
+                type = FullScreen(displayId = content.displayId),
+                component = defaultComponent,
+                owner = defaultOwner,
+            )
+        }
+
+        // When the systemUI notification shade is open, disregard tasks.
+        if (content.systemUiState.shadeExpanded) {
+            return defaultFullScreen
+        }
+
+        // find the first (top) RootTask which is visible and not Picture-in-Picture
+        val topRootTask =
+            content.rootTasks.firstOrNull {
+                it.isVisible && it.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED
+            } ?: return defaultFullScreen
+
+        Log.d(TAG, "topRootTask: $topRootTask")
+        val rootTaskOwners = topRootTask.childTaskUserIds.distinct()
+
+        // Special case: Only WORK in top root task which is full-screen or maximized freeform
+        if (
+            rootTaskOwners.size == 1 &&
+                profileTypes.getProfileType(rootTaskOwners.single()) == WORK &&
+                (topRootTask.isFullScreen() || topRootTask.isMaximizedFreeform())
+        ) {
+            val type =
+                if (topRootTask.childTaskCount() > 1) {
+                    RootTask(
+                        parentTaskId = topRootTask.taskId,
+                        taskBounds = topRootTask.bounds,
+                        childTaskIds = topRootTask.childTasksTopDown().map { it.id }.toList(),
+                    )
+                } else {
+                    IsolatedTask(
+                        taskId = topRootTask.childTasksTopDown().first().id,
+                        taskBounds = topRootTask.bounds,
+                    )
+                }
+            // Capture the RootTask (and all children)
+            return CaptureParameters(
+                type = type,
+                component = topRootTask.topActivity,
+                owner = UserHandle.of(rootTaskOwners.single()),
+            )
+        }
+
+        // In every other case the output will be a full screen capture regardless of content.
+        // For this reason, consider all owners of all visible content on the display (in all
+        // root tasks). This includes all root tasks in free-form mode.
+        val visibleChildTasks =
+            content.rootTasks.filter { it.isVisible }.flatMap { it.childTasksTopDown() }
+
+        val allVisibleProfileTypes =
+            visibleChildTasks
+                .map { it.userId }
+                .distinct()
+                .associate { profileTypes.getProfileType(it) to UserHandle.of(it) }
+
+        // If any visible content belongs to the private profile user -> private profile
+        // otherwise the personal user (including partial screen work content).
+        val ownerHandle =
+            allVisibleProfileTypes[PRIVATE]
+                ?: allVisibleProfileTypes[ProfileType.NONE]
+                ?: defaultOwner
+
+        // Attribute to the component of top-most task owned by this user (or fallback to default)
+        val topComponent =
+            visibleChildTasks.firstOrNull { it.userId == ownerHandle.identifier }?.componentName
+
+        return CaptureParameters(
+            type = FullScreen(content.displayId),
+            component = topComponent ?: topRootTask.topActivity ?: defaultComponent,
+            owner = ownerHandle,
+        )
+    }
+
+    private fun RootTaskInfo.isFullScreen(): Boolean =
+        configuration.windowConfiguration.windowingMode == WINDOWING_MODE_FULLSCREEN
+
+    private fun RootTaskInfo.isMaximizedFreeform(): Boolean {
+        val bounds = configuration.windowConfiguration.bounds
+        val maxBounds = configuration.windowConfiguration.maxBounds
+
+        if (
+            windowingMode != WINDOWING_MODE_FREEFORM ||
+                childTaskCount() != 1 ||
+                childTaskBounds[0] != bounds
+        ) {
+            return false
+        }
+
+        // Maximized floating windows fill maxBounds width
+        if (bounds.width() != maxBounds.width()) {
+            return false
+        }
+
+        // Maximized floating windows fill nearly all the height
+        return (bounds.height().toFloat() / maxBounds.height()) >= 0.89f
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
index 2cb9fe7..a9c6370 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
@@ -37,7 +37,6 @@
 
 @Module
 interface ScreenshotPolicyModule {
-
     @Binds
     @SysUISingleton
     fun bindProfileTypeRepository(impl: ProfileTypeRepositoryImpl): ProfileTypeRepository
@@ -67,6 +66,7 @@
             imageCapture: ImageCapture,
             displayContentRepo: DisplayContentRepository,
             policyListProvider: Provider<List<CapturePolicy>>,
+            standardPolicy: ScreenshotPolicy,
         ): ScreenshotRequestProcessor {
             return PolicyRequestProcessor(
                 background = background,
@@ -75,7 +75,8 @@
                 policies = policyListProvider.get(),
                 defaultOwner = Process.myUserHandle(),
                 defaultComponent =
-                    ComponentName(context.packageName, SystemUIService::class.java.toString())
+                    ComponentName(context.packageName, SystemUIService::class.java.toString()),
+                policy = standardPolicy,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
index 29450a2..cf90c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import javax.inject.Inject
-import kotlinx.coroutines.flow.first
 
 /**
  * Condition: When the top visible task (excluding PIP mode) belongs to a work user.
@@ -37,10 +36,8 @@
  */
 class WorkProfilePolicy
 @Inject
-constructor(
-    private val profileTypes: ProfileTypeRepository,
-    private val context: Context,
-) : CapturePolicy {
+constructor(private val profileTypes: ProfileTypeRepository, private val context: Context) :
+    CapturePolicy {
 
     override suspend fun check(content: DisplayContentModel): PolicyResult {
         // The systemUI notification shade isn't a work app, skip.
@@ -65,11 +62,7 @@
                 .map { it to it.childTasksTopDown().first() }
                 .firstOrNull { (_, child) ->
                     profileTypes.getProfileType(child.userId) == ProfileType.WORK
-                }
-                ?: return NotMatched(
-                    policy = NAME,
-                    reason = WORK_TASK_NOT_TOP,
-                )
+                } ?: return NotMatched(policy = NAME, reason = WORK_TASK_NOT_TOP)
 
         // If matched, return parameters needed to modify the request.
         return PolicyResult.Matched(
@@ -79,7 +72,7 @@
                 type = IsolatedTask(taskId = childTask.id, taskBounds = childTask.bounds),
                 component = childTask.componentName ?: rootTask.topActivity,
                 owner = UserHandle.of(childTask.userId),
-            )
+            ),
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 757e37e..750f5ad 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -32,7 +32,6 @@
 import static com.android.systemui.classifier.Classifier.UNLOCK;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.AOD;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
-import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED;
@@ -123,7 +122,6 @@
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
@@ -144,7 +142,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
@@ -605,9 +602,6 @@
     private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
     private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
     private final GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
-    private final GoneToDreamingLockscreenHostedTransitionViewModel
-            mGoneToDreamingLockscreenHostedTransitionViewModel;
-
     private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
     private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
     private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
@@ -619,7 +613,6 @@
     private final CoroutineDispatcher mMainDispatcher;
     private boolean mIsAnyMultiShadeExpanded;
     private boolean mIsOcclusionTransitionRunning = false;
-    private boolean mIsGoneToDreamingLockscreenHostedTransitionRunning;
     private int mDreamingToLockscreenTransitionTranslationY;
     private int mLockscreenToDreamingTransitionTranslationY;
     private int mGoneToDreamingTransitionTranslationY;
@@ -663,24 +656,6 @@
                     step.getTransitionState() == TransitionState.RUNNING;
             };
 
-    private final Consumer<TransitionStep> mGoneToDreamingLockscreenHostedTransition =
-            (TransitionStep step) -> {
-                mIsOcclusionTransitionRunning =
-                        step.getTransitionState() == TransitionState.RUNNING;
-                mIsGoneToDreamingLockscreenHostedTransitionRunning = mIsOcclusionTransitionRunning;
-            };
-
-    private final Consumer<TransitionStep> mLockscreenToDreamingLockscreenHostedTransition =
-            (TransitionStep step) -> {
-                mIsOcclusionTransitionRunning =
-                        step.getTransitionState() == TransitionState.RUNNING;
-            };
-
-    private final Consumer<TransitionStep> mDreamingLockscreenHostedToLockscreenTransition =
-            (TransitionStep step) -> {
-                mIsOcclusionTransitionRunning =
-                        step.getTransitionState() == TransitionState.RUNNING;
-            };
 
     private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
             (TransitionStep step) -> {
@@ -765,8 +740,6 @@
             OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
             LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
             GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
-            GoneToDreamingLockscreenHostedTransitionViewModel
-                    goneToDreamingLockscreenHostedTransitionViewModel,
             LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
             PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
             @Main CoroutineDispatcher mainDispatcher,
@@ -805,8 +778,6 @@
         mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
         mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
         mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
-        mGoneToDreamingLockscreenHostedTransitionViewModel =
-                goneToDreamingLockscreenHostedTransitionViewModel;
         mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
         mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
@@ -1134,25 +1105,6 @@
                 mDreamingToLockscreenTransitionTranslationY),
                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
 
-        // Gone -> Dreaming hosted in lockscreen
-        collectFlow(mView, mKeyguardTransitionInteractor
-                        .transition(Edge.Companion.create(Scenes.Gone, DREAMING_LOCKSCREEN_HOSTED),
-                                Edge.Companion.create(GONE, DREAMING_LOCKSCREEN_HOSTED)),
-                mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
-        collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
-                setTransitionAlpha(mNotificationStackScrollLayoutController),
-                mMainDispatcher);
-
-        // Lockscreen -> Dreaming hosted in lockscreen
-        collectFlow(mView, mKeyguardTransitionInteractor
-                        .transition(Edge.Companion.create(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)),
-                mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
-
-        // Dreaming hosted in lockscreen -> Lockscreen
-        collectFlow(mView, mKeyguardTransitionInteractor
-                        .transition(Edge.Companion.create(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)),
-                mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
-
         // Occluded->Lockscreen
         collectFlow(mView, mKeyguardTransitionInteractor.transition(
                 Edge.Companion.create(OCCLUDED, LOCKSCREEN)),
@@ -1646,11 +1598,6 @@
         mAnimateNextPositionUpdate = false;
     }
 
-    private boolean shouldAnimateKeyguardStatusViewAlignment() {
-        // Do not animate when transitioning from Gone->DreamingLockscreenHosted
-        return !mIsGoneToDreamingLockscreenHostedTransitionRunning;
-    }
-
     private void updateClockAppearance() {
         int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
@@ -1661,7 +1608,7 @@
             mKeyguardStatusViewController.displayClock(computeDesiredClockSize(),
                     shouldAnimateClockChange);
         }
-        updateKeyguardStatusViewAlignment(/* animate= */shouldAnimateKeyguardStatusViewAlignment());
+        updateKeyguardStatusViewAlignment(/* animate= */true);
         int userSwitcherHeight = mKeyguardQsUserSwitchController != null
                 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
         if (mKeyguardUserSwitcherController != null) {
@@ -1808,10 +1755,6 @@
             // overlap.
             return true;
         }
-        if (isActiveDreamLockscreenHosted()) {
-            // Dreaming hosted in lockscreen, no "visible" notifications. Safe to center the clock.
-            return true;
-        }
         if (mNotificationListContainer.hasPulsingNotifications()) {
             // Pulsing notification appears on the right. Move clock left to avoid overlap.
             return false;
@@ -1841,10 +1784,6 @@
     }
 
 
-    private boolean isActiveDreamLockscreenHosted() {
-        return mKeyguardInteractor.isActiveDreamLockscreenHosted().getValue();
-    }
-
     private boolean hasVisibleNotifications() {
         if (FooterViewRefactor.isEnabled()) {
             return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
@@ -1859,16 +1798,11 @@
     /** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
     private float getLockIconPadding() {
         float lockIconPadding = 0f;
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
-                    .findViewById(R.id.device_entry_icon_view);
-            if (deviceEntryIconView != null) {
-                lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
-                    - deviceEntryIconView.getTop();
-            }
-        } else if (mLockIconViewController.getTop() != 0f) {
+        View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
+                .findViewById(R.id.device_entry_icon_view);
+        if (deviceEntryIconView != null) {
             lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
-                    - mLockIconViewController.getTop();
+                - deviceEntryIconView.getTop();
         }
         return lockIconPadding;
     }
@@ -5059,8 +4993,7 @@
                 return false;
             }
 
-            if (DeviceEntryUdfpsRefactor.isEnabled()
-                    && mAlternateBouncerInteractor.isVisibleState()) {
+            if (mAlternateBouncerInteractor.isVisibleState()) {
                 // never send touches to shade if the alternate bouncer is showing
                 return false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index cae141d..36449be 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.shade;
 
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING;
 import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -35,7 +34,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.keyguard.KeyguardUnfoldTransition;
-import com.android.keyguard.LockIconViewController;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -44,7 +42,6 @@
 import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlagsClassic;
@@ -75,7 +72,6 @@
 import com.android.systemui.statusbar.phone.DozeScrimController;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -101,14 +97,11 @@
     private final NotificationShadeDepthController mDepthController;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    private final LockIconViewController mLockIconViewController;
     private final ShadeLogger mShadeLogger;
-    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final StatusBarWindowStateController mStatusBarWindowStateController;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final AmbientState mAmbientState;
     private final PulsingGestureListener mPulsingGestureListener;
-    private final LockscreenHostedDreamGestureListener mLockscreenHostedDreamGestureListener;
     private final NotificationInsetsController mNotificationInsetsController;
     private final FeatureFlagsClassic mFeatureFlagsClassic;
     private final SysUIKeyEventHandler mSysUIKeyEventHandler;
@@ -119,7 +112,6 @@
     private final GlanceableHubContainerController
             mGlanceableHubContainerController;
     private GestureDetector mPulsingWakeupGestureHandler;
-    private GestureDetector mDreamingWakeupGestureHandler;
     private View mBrightnessMirror;
     private boolean mTouchActive;
     private boolean mTouchCancelled;
@@ -174,9 +166,7 @@
             PanelExpansionInteractor panelExpansionInteractor,
             ShadeExpansionStateManager shadeExpansionStateManager,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             StatusBarWindowStateController statusBarWindowStateController,
-            LockIconViewController lockIconViewController,
             CentralSurfaces centralSurfaces,
             DozeServiceHost dozeServiceHost,
             DozeScrimController dozeScrimController,
@@ -189,7 +179,6 @@
             ShadeLogger shadeLogger,
             DumpManager dumpManager,
             PulsingGestureListener pulsingGestureListener,
-            LockscreenHostedDreamGestureListener lockscreenHostedDreamGestureListener,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
             GlanceableHubContainerController glanceableHubContainerController,
             NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor,
@@ -210,9 +199,7 @@
         mShadeExpansionStateManager = shadeExpansionStateManager;
         mDepthController = depthController;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
-        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mStatusBarWindowStateController = statusBarWindowStateController;
-        mLockIconViewController = lockIconViewController;
         mShadeLogger = shadeLogger;
         mService = centralSurfaces;
         mDozeServiceHost = dozeServiceHost;
@@ -221,7 +208,6 @@
         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
         mAmbientState = ambientState;
         mPulsingGestureListener = pulsingGestureListener;
-        mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener;
         mNotificationInsetsController = notificationInsetsController;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mGlanceableHubContainerController = glanceableHubContainerController;
@@ -259,7 +245,6 @@
                             mDisableSubpixelTextTransitionListener));
         }
 
-        lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view));
         dumpManager.registerDumpable(this);
     }
 
@@ -337,10 +322,6 @@
         mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
         mPulsingWakeupGestureHandler = new GestureDetector(mView.getContext(),
                 mPulsingGestureListener);
-        if (mFeatureFlagsClassic.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
-            mDreamingWakeupGestureHandler = new GestureDetector(mView.getContext(),
-                    mLockscreenHostedDreamGestureListener);
-        }
         mView.setLayoutInsetsController(mNotificationInsetsController);
         mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
             boolean mUseDragDownHelperForTouch = false;
@@ -392,9 +373,6 @@
                 }
 
                 if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
-                    // If the user was sliding their finger across the lock screen,
-                    // we may have been intercepting the touch and forwarding it to the
-                    // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below).
                     // If this touch ended up unlocking the device, we want to cancel the touch
                     // immediately, so we don't cause swipe or expand animations afterwards.
                     cancelCurrentTouch();
@@ -415,13 +393,6 @@
                     // GlanceableHubContainerController is only used pre-flexiglass.
                     return logDownDispatch(ev, "dispatched to glanceable hub container", true);
                 }
-                if (mDreamingWakeupGestureHandler != null
-                        && mDreamingWakeupGestureHandler.onTouchEvent(ev)) {
-                    return logDownDispatch(ev, "dream wakeup gesture handled", true);
-                }
-                if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) {
-                    return logDownDispatch(ev, "dispatched to Keyguard", true);
-                }
                 if (mBrightnessMirror != null
                         && mBrightnessMirror.getVisibility() == View.VISIBLE) {
                     // Disallow new pointers while the brightness mirror is visible. This is so that
@@ -512,7 +483,6 @@
                 if (mStatusBarStateController.isDozing()
                         && !mDozeServiceHost.isPulsing()
                         && !mDockManager.isDocked()
-                        && !mLockIconViewController.willHandleTouchWhileDozing(ev)
                 ) {
                     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                         mShadeLogger.d("NSWVC: capture all touch events in always-on");
@@ -520,22 +490,8 @@
                     return true;
                 }
 
-                if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) {
-                    // Don't allow touches to proceed to underlying views if alternate
-                    // bouncer is showing
-                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-                        mShadeLogger.d("NSWVC: alt bouncer showing");
-                    }
-                    return true;
-                }
-
-                boolean bouncerShowing;
-                if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                    bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
+                boolean bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
                             || mAlternateBouncerInteractor.isVisibleState();
-                } else {
-                    bouncerShowing = mService.isBouncerShowing();
-                }
                 if (mPanelExpansionInteractor.isFullyExpanded()
                         && !bouncerShowing
                         && !mStatusBarStateController.isDozing()) {
@@ -603,9 +559,6 @@
                 if (mStatusBarStateController.isDozing()) {
                     handled = !mDozeServiceHost.isPulsing();
                 }
-                if (mStatusBarKeyguardViewManager.onTouch(ev)) {
-                    return true;
-                }
                 if (MigrateClocksToBlueprint.isEnabled()) {
                     if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
                         // we still want to finish our drag down gesture when locking the screen
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index b7a95e9..0e30f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -38,7 +38,7 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 
 import dagger.Lazy;
 
@@ -62,7 +62,7 @@
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final StatusBarStateController mStatusBarStateController;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private final StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
     private final DeviceProvisionedController mDeviceProvisionedController;
 
     private final Lazy<NotificationShadeWindowViewController> mNotifShadeWindowViewController;
@@ -83,7 +83,7 @@
             KeyguardStateController keyguardStateController,
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            StatusBarWindowController statusBarWindowController,
+            StatusBarWindowControllerStore statusBarWindowControllerStore,
             DeviceProvisionedController deviceProvisionedController,
             NotificationShadeWindowController notificationShadeWindowController,
             @DisplayId int displayId,
@@ -102,7 +102,7 @@
         mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
         mNpvc = shadeViewControllerLazy;
         mStatusBarStateController = statusBarStateController;
-        mStatusBarWindowController = statusBarWindowController;
+        mStatusBarWindowControllerStore = statusBarWindowControllerStore;
         mDeviceProvisionedController = deviceProvisionedController;
         mGutsManager = gutsManager;
         mNotificationShadeWindowController = notificationShadeWindowController;
@@ -315,7 +315,7 @@
 
         // Update the visibility of notification shade and status bar window.
         mNotificationShadeWindowController.setPanelVisible(false);
-        mStatusBarWindowController.setForceStatusBarVisible(false);
+        mStatusBarWindowControllerStore.getDefaultDisplay().setForceStatusBarVisible(false);
 
         // Close any guts that might be visible
         mGutsManager.get().closeAndSaveGuts(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index fc8a593..5afc539 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.SceneContainerConfig
@@ -89,6 +90,7 @@
             overlaysProvider: Provider<Set<@JvmSuppressWildcards Overlay>>,
             layoutInsetController: NotificationInsetsController,
             sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
+            qsSceneAdapter: Provider<QSSceneAdapter>,
             alternateBouncerDependencies: Provider<AlternateBouncerDependencies>,
         ): WindowRootView {
             return if (SceneContainerFlag.isEnabled) {
@@ -104,6 +106,7 @@
                     overlays = overlaysProvider.get(),
                     layoutInsetController = layoutInsetController,
                     sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
+                    qsSceneAdapter = qsSceneAdapter,
                     alternateBouncerDependencies = alternateBouncerDependencies.get(),
                 )
                 sceneWindowRootView
@@ -119,9 +122,7 @@
         //  {@link NotificationShadeWindowViewController} can inject this view.
         @Provides
         @SysUISingleton
-        fun providesNotificationShadeWindowView(
-            root: WindowRootView,
-        ): NotificationShadeWindowView {
+        fun providesNotificationShadeWindowView(root: WindowRootView): NotificationShadeWindowView {
             if (SceneContainerFlag.isEnabled) {
                 return root.requireViewById(R.id.legacy_window_root)
             }
@@ -133,7 +134,7 @@
         @Provides
         @SysUISingleton
         fun providesNotificationStackScrollLayout(
-            notificationShadeWindowView: NotificationShadeWindowView,
+            notificationShadeWindowView: NotificationShadeWindowView
         ): NotificationStackScrollLayout {
             return notificationShadeWindowView.requireViewById(R.id.notification_stack_scroller)
         }
@@ -142,7 +143,7 @@
         @Provides
         @SysUISingleton
         fun providesNotificationPanelView(
-            notificationShadeWindowView: NotificationShadeWindowView,
+            notificationShadeWindowView: NotificationShadeWindowView
         ): NotificationPanelView {
             return notificationShadeWindowView.requireViewById(R.id.notification_panel)
         }
@@ -178,7 +179,7 @@
         @Provides
         @SysUISingleton
         fun providesKeyguardRootView(
-            notificationShadeWindowView: NotificationShadeWindowView,
+            notificationShadeWindowView: NotificationShadeWindowView
         ): KeyguardRootView {
             return notificationShadeWindowView.requireViewById(R.id.keyguard_root_view)
         }
@@ -186,7 +187,7 @@
         @Provides
         @SysUISingleton
         fun providesSharedNotificationContainer(
-            notificationShadeWindowView: NotificationShadeWindowView,
+            notificationShadeWindowView: NotificationShadeWindowView
         ): SharedNotificationContainer {
             return notificationShadeWindowView.requireViewById(R.id.shared_notification_container)
         }
@@ -195,7 +196,7 @@
         @Provides
         @SysUISingleton
         fun providesAuthRippleView(
-            notificationShadeWindowView: NotificationShadeWindowView,
+            notificationShadeWindowView: NotificationShadeWindowView
         ): AuthRippleView? {
             return notificationShadeWindowView.requireViewById(R.id.auth_ripple)
         }
@@ -203,9 +204,7 @@
         // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
         @Provides
         @SysUISingleton
-        fun providesTapAgainView(
-            notificationPanelView: NotificationPanelView,
-        ): TapAgainView {
+        fun providesTapAgainView(notificationPanelView: NotificationPanelView): TapAgainView {
             return notificationPanelView.requireViewById(R.id.shade_falsing_tap_again)
         }
 
@@ -213,7 +212,7 @@
         @Provides
         @SysUISingleton
         fun providesNotificationsQuickSettingsContainer(
-            notificationShadeWindowView: NotificationShadeWindowView,
+            notificationShadeWindowView: NotificationShadeWindowView
         ): NotificationsQuickSettingsContainer {
             return notificationShadeWindowView.requireViewById(R.id.notification_container_parent)
         }
@@ -223,7 +222,7 @@
         @SysUISingleton
         @Named(SHADE_HEADER)
         fun providesShadeHeaderView(
-            notificationShadeWindowView: NotificationShadeWindowView,
+            notificationShadeWindowView: NotificationShadeWindowView
         ): MotionLayout {
             val stub = notificationShadeWindowView.requireViewById<ViewStub>(R.id.qs_header_stub)
             val layoutId = R.layout.combined_qs_header
@@ -275,7 +274,7 @@
         @SysUISingleton
         @Named(SHADE_HEADER)
         fun providesOngoingPrivacyChip(
-            @Named(SHADE_HEADER) header: MotionLayout,
+            @Named(SHADE_HEADER) header: MotionLayout
         ): OngoingPrivacyChip {
             return header.requireViewById(R.id.privacy_chip)
         }
@@ -284,7 +283,7 @@
         @SysUISingleton
         @Named(SHADE_HEADER)
         fun providesStatusIconContainer(
-            @Named(SHADE_HEADER) header: MotionLayout,
+            @Named(SHADE_HEADER) header: MotionLayout
         ): StatusIconContainer {
             return header.requireViewById(R.id.statusIcons)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index 949d2aa..460bfbb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade.domain.interactor
 
+import com.android.app.tracing.coroutines.flow.flowName
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -62,17 +63,20 @@
     override val isShadeEnabled: StateFlow<Boolean> =
         disableFlagsRepository.disableFlags
             .map { it.isShadeEnabled() }
+            .flowName("isShadeEnabled")
             .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
 
     override val isQsEnabled: StateFlow<Boolean> =
         disableFlagsRepository.disableFlags
             .map { it.isQuickSettingsEnabled() }
+            .flowName("isQsEnabled")
             .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
 
     override val isAnyFullyExpanded: StateFlow<Boolean> =
         anyExpansion
             .map { it >= 1f }
             .distinctUntilChanged()
+            .flowName("isAnyFullyExpanded")
             .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
 
     override val isShadeFullyExpanded: Flow<Boolean> =
@@ -81,6 +85,7 @@
     override val isShadeAnyExpanded: StateFlow<Boolean> =
         baseShadeInteractor.shadeExpansion
             .map { it > 0 }
+            .flowName("isShadeAnyExpanded")
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isShadeFullyCollapsed: Flow<Boolean> =
@@ -88,6 +93,7 @@
 
     override val isUserInteracting: StateFlow<Boolean> =
         combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
+            .flowName("isUserInteracting")
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isShadeTouchable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
index 3113dc4..4bdd367 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.shade.ui.viewmodel
 
-import com.android.app.tracing.coroutines.flow.map
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.UserAction
@@ -33,6 +32,7 @@
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
 
 /**
  * Models the UI state for the user actions that the user can perform to navigate to other scenes.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 769abaf..f99d8f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -32,7 +32,6 @@
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_IS_DISMISSIBLE;
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ADAPTIVE_AUTH;
@@ -226,18 +225,8 @@
     private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
 
     private boolean mDozing;
-    private boolean mIsActiveDreamLockscreenHosted;
     private final ScreenLifecycle mScreenLifecycle;
     @VisibleForTesting
-    final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
-            (Boolean isLockscreenHosted) -> {
-                if (mIsActiveDreamLockscreenHosted == isLockscreenHosted) {
-                    return;
-                }
-                mIsActiveDreamLockscreenHosted = isLockscreenHosted;
-                updateDeviceEntryIndication(false);
-            };
-    @VisibleForTesting
     final Consumer<Set<Integer>> mCoExAcquisitionMsgIdsToShowCallback =
             (Set<Integer> coExFaceAcquisitionMsgIdsToShow) -> mCoExFaceAcquisitionMsgIdsToShow =
                     coExFaceAcquisitionMsgIdsToShow;
@@ -423,10 +412,6 @@
             intentFilter.addAction(Intent.ACTION_USER_REMOVED);
             mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter);
         }
-        if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
-            collectFlow(mIndicationArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
-                    mIsActiveDreamLockscreenHostedCallback);
-        }
 
         collectFlow(mIndicationArea,
                 mBiometricMessageInteractor.getCoExFaceAcquisitionMsgIdsToShow(),
@@ -1032,12 +1017,6 @@
             return;
         }
 
-        // Device is dreaming and the dream is hosted in lockscreen
-        if (mIsActiveDreamLockscreenHosted) {
-            mIndicationArea.setVisibility(GONE);
-            return;
-        }
-
         // A few places might need to hide the indication, so always start by making it visible
         mIndicationArea.setVisibility(VISIBLE);
 
@@ -1247,7 +1226,6 @@
         pw.println("  mBiometricMessageFollowUp: " + mBiometricMessageFollowUp);
         pw.println("  mBatteryLevel: " + mBatteryLevel);
         pw.println("  mBatteryPresent: " + mBatteryPresent);
-        pw.println("  mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted);
         pw.println("  AOD text: " + (
                 mTopIndicationView == null ? null : mTopIndicationView.getText()));
         pw.println("  computePowerIndication(): " + computePowerIndication());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 1481b73..1ec5357 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -15,7 +15,6 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.ExpandHelper
 import com.android.systemui.Gefingerpoken
-import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy
 import com.android.systemui.classifier.Classifier
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
@@ -90,6 +89,7 @@
     @get:VisibleForTesting
     var fractionToShade: Float = 0f
         private set
+
     private var useSplitShade: Boolean = false
     private lateinit var nsslController: NotificationStackScrollLayoutController
     lateinit var centralSurfaces: CentralSurfaces
@@ -161,9 +161,6 @@
     val distanceUntilShowingPulsingNotifications
         get() = fullTransitionDistance
 
-    /** The udfpsKeyguardViewController if it exists. */
-    var mUdfpsKeyguardViewControllerLegacy: UdfpsKeyguardViewControllerLegacy? = null
-
     /** The touch helper responsible for the drag down animation. */
     val touchHelper =
         DragDownHelper(
@@ -171,7 +168,7 @@
             this,
             naturalScrollingSettingObserver,
             shadeRepository,
-            context
+            context,
         )
 
     private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy {
@@ -448,7 +445,6 @@
 
         val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance)
         shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress)
-        mUdfpsKeyguardViewControllerLegacy?.setTransitionToFullShadeProgress(udfpsProgress)
 
         val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance)
         centralSurfaces.setTransitionToFullShadeProgress(statusBarProgress)
@@ -457,7 +453,7 @@
     private fun setDragDownAmountAnimated(
         target: Float,
         delay: Long = 0,
-        endlistener: (() -> Unit)? = null
+        endlistener: (() -> Unit)? = null,
     ) {
         logger.logDragDownAnimation(target)
         val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
@@ -553,7 +549,7 @@
     private fun goToLockedShadeInternal(
         expandView: View?,
         animationHandler: ((Long) -> Unit)? = null,
-        cancelAction: Runnable? = null
+        cancelAction: Runnable? = null,
     ) {
         if (!shadeInteractor.isShadeEnabled.value) {
             cancelAction?.run()
@@ -564,10 +560,7 @@
         var entry: NotificationEntry? = null
         if (expandView is ExpandableNotificationRow) {
             entry = expandView.entry
-            entry.setUserExpanded(
-                /* userExpanded= */ true,
-                /* allowChildExpansion= */ true,
-            )
+            entry.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true)
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
             entry.setGroupExpansionChanging(true)
@@ -594,9 +587,7 @@
                 statusBarStateController.setLeaveOpenOnKeyguardHide(false)
                 draggedDownEntry?.apply {
                     setUserLocked(false)
-                    notifyHeightChanged(
-                        /* needsAnimation= */ false,
-                    )
+                    notifyHeightChanged(/* needsAnimation= */ false)
                     draggedDownEntry = null
                 }
                 cancelAction?.run()
@@ -614,9 +605,7 @@
             // This call needs to be after updating the shade state since otherwise
             // the scrimstate resets too early
             if (animationHandler != null) {
-                animationHandler.invoke(
-                    /* delay= */ 0,
-                )
+                animationHandler.invoke(/* delay= */ 0)
             } else {
                 performDefaultGoToFullShadeAnimation(0)
             }
@@ -757,7 +746,7 @@
     private val dragDownCallback: LockscreenShadeTransitionController,
     private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
     private val shadeRepository: ShadeRepository,
-    context: Context
+    context: Context,
 ) : Gefingerpoken {
 
     private var dragDownAmountOnStart = 0.0f
@@ -932,7 +921,7 @@
     @VisibleForTesting
     fun cancelChildExpansion(
         child: ExpandableView,
-        animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
+        animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS,
     ) {
         if (child.actualHeight == child.collapsedHeight) {
             expandCallback.setUserLockedChild(child, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 5c45f3d..1db7fb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -32,14 +32,15 @@
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
-import com.android.systemui.Dumpable
 import com.android.app.animation.Interpolators
+import com.android.systemui.Dumpable
 import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shared.Flags.ambientAod
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
 import com.android.systemui.statusbar.phone.DozeParameters
@@ -54,10 +55,13 @@
 import kotlin.math.sign
 
 /**
- * Controller responsible for statusbar window blur.
+ * Responsible for blurring the notification shade window, and applying a zoom effect to the
+ * wallpaper.
  */
 @SysUISingleton
-class NotificationShadeDepthController @Inject constructor(
+class NotificationShadeDepthController
+@Inject
+constructor(
     private val statusBarStateController: StatusBarStateController,
     private val blurUtils: BlurUtils,
     private val biometricUnlockController: BiometricUnlockController,
@@ -69,7 +73,7 @@
     private val context: Context,
     private val splitShadeStateController: SplitShadeStateController,
     dumpManager: DumpManager,
-    configurationController: ConfigurationController
+    configurationController: ConfigurationController,
 ) : ShadeExpansionListener, Dumpable {
     companion object {
         private const val WAKE_UP_ANIMATION_ENABLED = true
@@ -85,8 +89,7 @@
     private var keyguardAnimator: Animator? = null
     private var notificationAnimator: Animator? = null
     private var updateScheduled: Boolean = false
-    @VisibleForTesting
-    var shadeExpansion = 0f
+    @VisibleForTesting var shadeExpansion = 0f
     private var isClosed: Boolean = true
     private var isOpen: Boolean = false
     private var isBlurred: Boolean = false
@@ -106,13 +109,13 @@
 
     var shadeAnimation = DepthAnimation()
 
-    @VisibleForTesting
-    var brightnessMirrorSpring = DepthAnimation()
+    @VisibleForTesting var brightnessMirrorSpring = DepthAnimation()
     var brightnessMirrorVisible: Boolean = false
         set(value) {
             field = value
-            brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f).toInt()
-                else 0)
+            brightnessMirrorSpring.animateTo(
+                if (value) blurUtils.blurRadiusOfRatio(1f).toInt() else 0
+            )
         }
 
     var qsPanelExpansion = 0f
@@ -126,9 +129,7 @@
             scheduleUpdate()
         }
 
-    /**
-     * How much we're transitioning to the full shade
-     */
+    /** How much we're transitioning to the full shade */
     var transitionToFullShadeProgress = 0f
         set(value) {
             if (field == value) return
@@ -160,19 +161,15 @@
             shadeAnimation.finishIfRunning()
         }
 
-    /**
-     * We're unlocking, and should not blur as the panel expansion changes.
-     */
+    /** We're unlocking, and should not blur as the panel expansion changes. */
     var blursDisabledForUnlock: Boolean = false
-    set(value) {
-        if (field == value) return
-        field = value
-        scheduleUpdate()
-    }
+        set(value) {
+            if (field == value) return
+            field = value
+            scheduleUpdate()
+        }
 
-    /**
-     * Force stop blur effect when necessary.
-     */
+    /** Force stop blur effect when necessary. */
     private var scrimsVisible: Boolean = false
         set(value) {
             if (field == value) return
@@ -180,9 +177,7 @@
             scheduleUpdate()
         }
 
-    /**
-     * Blur radius of the wake-up animation on this frame.
-     */
+    /** Blur radius of the wake-up animation on this frame. */
     private var wakeAndUnlockBlurRadius = 0f
         set(value) {
             if (field == value) return
@@ -191,15 +186,23 @@
         }
 
     private fun computeBlurAndZoomOut(): Pair<Int, Float> {
-        val animationRadius = MathUtils.constrain(shadeAnimation.radius,
-                blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat())
-        val expansionRadius = blurUtils.blurRadiusOfRatio(
+        val animationRadius =
+            MathUtils.constrain(
+                shadeAnimation.radius,
+                blurUtils.minBlurRadius.toFloat(),
+                blurUtils.maxBlurRadius.toFloat(),
+            )
+        val expansionRadius =
+            blurUtils.blurRadiusOfRatio(
                 ShadeInterpolation.getNotificationScrimAlpha(
-                        if (shouldApplyShadeBlur()) shadeExpansion else 0f))
-        var combinedBlur = (expansionRadius * INTERACTION_BLUR_FRACTION +
+                    if (shouldApplyShadeBlur()) shadeExpansion else 0f
+                )
+            )
+        var combinedBlur =
+            (expansionRadius * INTERACTION_BLUR_FRACTION +
                 animationRadius * ANIMATION_BLUR_FRACTION)
-        val qsExpandedRatio = ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) *
-                shadeExpansion
+        val qsExpandedRatio =
+            ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) * shadeExpansion
         combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio))
         combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
         var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
@@ -231,83 +234,97 @@
         return Pair(blur, zoomOut)
     }
 
-    /**
-     * Callback that updates the window blur value and is called only once per frame.
-     */
+    /** Callback that updates the window blur value and is called only once per frame. */
     @VisibleForTesting
-    val updateBlurCallback = Choreographer.FrameCallback {
-        updateScheduled = false
-        val (blur, zoomOut) = computeBlurAndZoomOut()
-        val opaque = scrimsVisible && !blursDisabledForAppLaunch
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
-        blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
-        lastAppliedBlur = blur
-        wallpaperController.setNotificationShadeZoom(zoomOut)
-        listeners.forEach {
-            it.onWallpaperZoomOutChanged(zoomOut)
-            it.onBlurRadiusChanged(blur)
-        }
-        notificationShadeWindowController.setBackgroundBlurRadius(blur)
-    }
-
-    /**
-     * Animate blurs when unlocking.
-     */
-    private val keyguardStateCallback = object : KeyguardStateController.Callback {
-        override fun onKeyguardFadingAwayChanged() {
-            if (!keyguardStateController.isKeyguardFadingAway ||
-                    biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK) {
-                return
+    val updateBlurCallback =
+        Choreographer.FrameCallback {
+            updateScheduled = false
+            val (blur, zoomOut) = computeBlurAndZoomOut()
+            val opaque = scrimsVisible && !blursDisabledForAppLaunch
+            Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
+            blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
+            lastAppliedBlur = blur
+            wallpaperController.setNotificationShadeZoom(zoomOut)
+            listeners.forEach {
+                it.onWallpaperZoomOutChanged(zoomOut)
+                it.onBlurRadiusChanged(blur)
             }
+            notificationShadeWindowController.setBackgroundBlurRadius(blur)
+        }
 
-            keyguardAnimator?.cancel()
-            keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
-                // keyguardStateController.keyguardFadingAwayDuration might be zero when unlock by
-                // fingerprint due to there is no window container, see AppTransition#goodToGo.
-                // We use DozeParameters.wallpaperFadeOutDuration as an alternative.
-                duration = dozeParameters.wallpaperFadeOutDuration
-                startDelay = keyguardStateController.keyguardFadingAwayDelay
-                interpolator = Interpolators.FAST_OUT_SLOW_IN
-                addUpdateListener { animation: ValueAnimator ->
-                    wakeAndUnlockBlurRadius =
-                            blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)
+    /** Animate blurs when unlocking. */
+    private val keyguardStateCallback =
+        object : KeyguardStateController.Callback {
+            override fun onKeyguardFadingAwayChanged() {
+                if (
+                    !keyguardStateController.isKeyguardFadingAway ||
+                        biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK
+                ) {
+                    return
                 }
-                addListener(object : AnimatorListenerAdapter() {
-                    override fun onAnimationEnd(animation: Animator) {
-                        keyguardAnimator = null
-                        wakeAndUnlockBlurRadius = 0f
-                    }
-                })
-                start()
-            }
-        }
 
-        override fun onKeyguardShowingChanged() {
-            if (keyguardStateController.isShowing) {
                 keyguardAnimator?.cancel()
-                notificationAnimator?.cancel()
+                keyguardAnimator =
+                    ValueAnimator.ofFloat(1f, 0f).apply {
+                        // keyguardStateController.keyguardFadingAwayDuration might be zero when
+                        // unlock by fingerprint due to there is no window container, see
+                        // AppTransition#goodToGo. We use DozeParameters.wallpaperFadeOutDuration as
+                        // an alternative.
+                        duration = dozeParameters.wallpaperFadeOutDuration
+                        startDelay = keyguardStateController.keyguardFadingAwayDelay
+                        interpolator = Interpolators.FAST_OUT_SLOW_IN
+                        addUpdateListener { animation: ValueAnimator ->
+                            wakeAndUnlockBlurRadius =
+                                blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)
+                        }
+                        addListener(
+                            object : AnimatorListenerAdapter() {
+                                override fun onAnimationEnd(animation: Animator) {
+                                    keyguardAnimator = null
+                                    wakeAndUnlockBlurRadius = 0f
+                                }
+                            }
+                        )
+                        start()
+                    }
             }
-        }
-    }
 
-    private val statusBarStateCallback = object : StatusBarStateController.StateListener {
-        override fun onStateChanged(newState: Int) {
-            updateShadeAnimationBlur(
-                    shadeExpansion, prevTracking, prevShadeVelocity, prevShadeDirection)
-            scheduleUpdate()
-        }
-
-        override fun onDozingChanged(isDozing: Boolean) {
-            if (isDozing) {
-                shadeAnimation.finishIfRunning()
-                brightnessMirrorSpring.finishIfRunning()
+            override fun onKeyguardShowingChanged() {
+                if (keyguardStateController.isShowing) {
+                    keyguardAnimator?.cancel()
+                    notificationAnimator?.cancel()
+                }
             }
         }
 
-        override fun onDozeAmountChanged(linear: Float, eased: Float) {
-            wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
+    private val statusBarStateCallback =
+        object : StatusBarStateController.StateListener {
+            override fun onStateChanged(newState: Int) {
+                updateShadeAnimationBlur(
+                    shadeExpansion,
+                    prevTracking,
+                    prevShadeVelocity,
+                    prevShadeDirection,
+                )
+                scheduleUpdate()
+            }
+
+            override fun onDozingChanged(isDozing: Boolean) {
+                if (isDozing) {
+                    shadeAnimation.finishIfRunning()
+                    brightnessMirrorSpring.finishIfRunning()
+                }
+            }
+
+            override fun onDozeAmountChanged(linear: Float, eased: Float) {
+                wakeAndUnlockBlurRadius =
+                    if (ambientAod()) {
+                        0f
+                    } else {
+                        blurUtils.blurRadiusOfRatio(eased)
+                    }
+            }
         }
-    }
 
     init {
         dumpManager.registerCriticalDumpable(javaClass.name, this)
@@ -317,16 +334,19 @@
         statusBarStateController.addCallback(statusBarStateCallback)
         notificationShadeWindowController.setScrimsVisibilityListener {
             // Stop blur effect when scrims is opaque to avoid unnecessary GPU composition.
-            visibility -> scrimsVisible = visibility == ScrimController.OPAQUE
+            visibility ->
+            scrimsVisible = visibility == ScrimController.OPAQUE
         }
         shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
         shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
         updateResources()
-        configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
-            override fun onConfigChanged(newConfig: Configuration?) {
-                updateResources()
+        configurationController.addCallback(
+            object : ConfigurationController.ConfigurationListener {
+                override fun onConfigChanged(newConfig: Configuration?) {
+                    updateResources()
+                }
             }
-        })
+        )
     }
 
     private fun updateResources() {
@@ -341,15 +361,15 @@
         listeners.remove(listener)
     }
 
-    /**
-     * Update blurs when pulling down the shade
-     */
+    /** Update blurs when pulling down the shade */
     override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) {
         val rawFraction = event.fraction
         val tracking = event.tracking
         val timestamp = SystemClock.elapsedRealtimeNanos()
-        val expansion = MathUtils.saturate(
-                (rawFraction - panelPullDownMinFraction) / (1f - panelPullDownMinFraction))
+        val expansion =
+            MathUtils.saturate(
+                (rawFraction - panelPullDownMinFraction) / (1f - panelPullDownMinFraction)
+            )
 
         if (shadeExpansion == expansion && prevTracking == tracking) {
             prevTimestamp = timestamp
@@ -360,14 +380,14 @@
         if (prevTimestamp < 0) {
             prevTimestamp = timestamp
         } else {
-            deltaTime = MathUtils.constrain(
-                    ((timestamp - prevTimestamp) / 1E9).toFloat(), 0.00001f, 1f)
+            deltaTime =
+                MathUtils.constrain(((timestamp - prevTimestamp) / 1E9).toFloat(), 0.00001f, 1f)
         }
 
         val diff = expansion - shadeExpansion
         val shadeDirection = sign(diff).toInt()
-        val shadeVelocity = MathUtils.constrain(
-            VELOCITY_SCALE * diff / deltaTime, MIN_VELOCITY, MAX_VELOCITY)
+        val shadeVelocity =
+            MathUtils.constrain(VELOCITY_SCALE * diff / deltaTime, MIN_VELOCITY, MAX_VELOCITY)
         updateShadeAnimationBlur(expansion, tracking, shadeVelocity, shadeDirection)
 
         prevShadeDirection = shadeDirection
@@ -383,7 +403,7 @@
         expansion: Float,
         tracking: Boolean,
         velocity: Float,
-        direction: Int
+        direction: Int,
     ) {
         if (shouldApplyShadeBlur()) {
             if (expansion > 0f) {
@@ -432,11 +452,12 @@
     private fun animateBlur(blur: Boolean, velocity: Float) {
         isBlurred = blur
 
-        val targetBlurNormalized = if (blur && shouldApplyShadeBlur()) {
-            1f
-        } else {
-            0f
-        }
+        val targetBlurNormalized =
+            if (blur && shouldApplyShadeBlur()) {
+                1f
+            } else {
+                0f
+            }
 
         shadeAnimation.setStartVelocity(velocity)
         shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized).toInt())
@@ -453,13 +474,13 @@
     }
 
     /**
-     * Should blur be applied to the shade currently. This is mainly used to make sure that
-     * on the lockscreen, the wallpaper isn't blurred.
+     * Should blur be applied to the shade currently. This is mainly used to make sure that on the
+     * lockscreen, the wallpaper isn't blurred.
      */
     private fun shouldApplyShadeBlur(): Boolean {
         val state = statusBarStateController.state
         return (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) &&
-                !keyguardStateController.isKeyguardFadingAway
+            !keyguardStateController.isKeyguardFadingAway
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -483,33 +504,30 @@
      * invalidation.
      */
     inner class DepthAnimation() {
-        /**
-         * Blur radius visible on the UI, in pixels.
-         */
+        /** Blur radius visible on the UI, in pixels. */
         var radius = 0f
 
-        /**
-         * Depth ratio of the current blur radius.
-         */
+        /** Depth ratio of the current blur radius. */
         val ratio
             get() = blurUtils.ratioOfBlurRadius(radius)
 
-        /**
-         * Radius that we're animating to.
-         */
+        /** Radius that we're animating to. */
         private var pendingRadius = -1
 
-        private var springAnimation = SpringAnimation(this, object :
-                FloatPropertyCompat<DepthAnimation>("blurRadius") {
-            override fun setValue(rect: DepthAnimation?, value: Float) {
-                radius = value
-                scheduleUpdate()
-            }
+        private var springAnimation =
+            SpringAnimation(
+                this,
+                object : FloatPropertyCompat<DepthAnimation>("blurRadius") {
+                    override fun setValue(rect: DepthAnimation?, value: Float) {
+                        radius = value
+                        scheduleUpdate()
+                    }
 
-            override fun getValue(rect: DepthAnimation?): Float {
-                return radius
-            }
-        })
+                    override fun getValue(rect: DepthAnimation?): Float {
+                        return radius
+                    }
+                },
+            )
 
         init {
             springAnimation.spring = SpringForce(0.0f)
@@ -545,13 +563,9 @@
         }
     }
 
-    /**
-     * Invoked when changes are needed in z-space
-     */
+    /** Invoked when changes are needed in z-space */
     interface DepthListener {
-        /**
-         * Current wallpaper zoom out, where 0 is the closest, and 1 the farthest
-         */
+        /** Current wallpaper zoom out, where 0 is the closest, and 1 the farthest */
         fun onWallpaperZoomOutChanged(zoomOut: Float)
 
         fun onBlurRadiusChanged(blurRadius: Int) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 32d37ae..c019f30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -22,3 +22,7 @@
 per-file *RemoteInput* = file:notification/OWNERS
 per-file *EmptyShadeView* = set noparent
 per-file *EmptyShadeView* = file:notification/OWNERS
+per-file *Lockscreen* = set noparent
+per-file *Lockscreen* = file:../keyguard/OWNERS
+per-file *Scrim* = set noparent
+per-file *Scrim* = file:../keyguard/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 73ad0e5..da04f6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -46,6 +46,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardClockSwitch;
 import com.android.systemui.DejankUtils;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
 import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus;
@@ -123,6 +124,7 @@
     private final Lazy<SceneContainerOcclusionInteractor> mSceneContainerOcclusionInteractorLazy;
     private final Lazy<KeyguardClockInteractor> mKeyguardClockInteractorLazy;
     private final Lazy<SceneBackInteractor> mSceneBackInteractorLazy;
+    private final Lazy<AlternateBouncerInteractor> mAlternateBouncerInteractorLazy;
     private int mState;
     private int mLastState;
     private int mUpcomingState;
@@ -193,7 +195,8 @@
             Lazy<SceneInteractor> sceneInteractorLazy,
             Lazy<SceneContainerOcclusionInteractor> sceneContainerOcclusionInteractor,
             Lazy<KeyguardClockInteractor> keyguardClockInteractorLazy,
-            Lazy<SceneBackInteractor> sceneBackInteractorLazy) {
+            Lazy<SceneBackInteractor> sceneBackInteractorLazy,
+            Lazy<AlternateBouncerInteractor> alternateBouncerInteractorLazy) {
         mUiEventLogger = uiEventLogger;
         mInteractionJankMonitorLazy = interactionJankMonitorLazy;
         mJavaAdapter = javaAdapter;
@@ -205,6 +208,7 @@
         mSceneContainerOcclusionInteractorLazy = sceneContainerOcclusionInteractor;
         mKeyguardClockInteractorLazy = keyguardClockInteractorLazy;
         mSceneBackInteractorLazy = sceneBackInteractorLazy;
+        mAlternateBouncerInteractorLazy = alternateBouncerInteractorLazy;
         for (int i = 0; i < HISTORY_SIZE; i++) {
             mHistoricalRecords[i] = new HistoricalState();
         }
@@ -233,6 +237,7 @@
                         mSceneInteractorLazy.get().getCurrentOverlays(),
                         mSceneBackInteractorLazy.get().getBackStack(),
                         mSceneContainerOcclusionInteractorLazy.get().getInvisibleDueToOcclusion(),
+                        mAlternateBouncerInteractorLazy.get().isVisible(),
                         this::calculateStateFromSceneFramework),
                     this::onStatusBarStateChanged);
 
@@ -693,7 +698,8 @@
             SceneKey currentScene,
             Set<OverlayKey> currentOverlays,
             SceneStack backStack,
-            boolean isOccluded) {
+            boolean isOccluded,
+            boolean alternateBouncerIsVisible) {
         SceneContainerFlag.isUnexpectedlyInLegacyMode();
 
         final boolean onBouncer = currentScene.equals(Scenes.Bouncer);
@@ -714,7 +720,8 @@
 
         final String inputLogString = "currentScene=" + currentScene.getTestTag()
                 + " currentOverlays=" + currentOverlays + " backStack=" + backStack
-                + " isUnlocked=" + isUnlocked + " isOccluded=" + isOccluded;
+                + " isUnlocked=" + isUnlocked + " isOccluded=" + isOccluded
+                + " alternateBouncerIsVisible=" + alternateBouncerIsVisible;
 
         int newState;
 
@@ -722,6 +729,7 @@
         // 1. deviceUnlockStatus.isUnlocked changes from false to true.
         // 2. Lockscreen changes to Gone, either in currentScene or in backStack.
         // 3. Bouncer is removed from currentScene or backStack, if it was present.
+        // 4. the alternate bouncer is hidden, if it was visible.
         //
         // From this function's perspective, though, deviceUnlockStatus, currentScene, and backStack
         // each update separately, and the relative order of those updates is not well-defined. This
@@ -733,6 +741,7 @@
         // 1. deviceUnlockStatus.isUnlocked is false.
         // 2. currentScene is a keyguardish scene (Lockscreen, Bouncer, or Communal).
         // 3. backStack contains a keyguardish scene (Lockscreen or Communal).
+        // 4. the alternate bouncer is visible.
 
         final boolean onKeyguardish = onLockscreen || onBouncer || onCommunal;
         final boolean overKeyguardish = overLockscreen || overCommunal;
@@ -741,7 +750,7 @@
             // Occlusion is special; even though the device is still technically on the lockscreen,
             // the UI behaves as if it is unlocked.
             newState = StatusBarState.SHADE;
-        } else if (onKeyguardish || overKeyguardish) {
+        } else if (onKeyguardish || overKeyguardish || alternateBouncerIsVisible) {
             // We get here if we are on or over a keyguardish scene, even if isUnlocked is true; we
             // want to return SHADE_LOCKED or KEYGUARD until we are also neither on nor over a
             // keyguardish scene.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
index be733d4..8ce0dbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -31,8 +31,8 @@
 abstract class StatusBarChipsModule {
     @Binds
     @IntoMap
-    @ClassKey(DemoRonChipViewModel::class)
-    abstract fun binds(impl: DemoRonChipViewModel): CoreStartable
+    @ClassKey(DemoNotifChipViewModel::class)
+    abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable
 
     companion object {
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
index cce9a16..5fa19dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
 
 import android.content.pm.PackageManager
 import android.content.pm.PackageManager.NameNotFoundException
@@ -22,7 +22,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
@@ -37,25 +37,25 @@
 import kotlinx.coroutines.flow.asStateFlow
 
 /**
- * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on
- * adb commands sent by the user.
+ * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb
+ * commands sent by the user.
  *
  * Example adb commands:
  *
  * To show a chip with the SysUI icon and custom text and color:
  * ```
- * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343"
+ * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343"
  * ```
  *
  * To hide the chip:
  * ```
- * adb shell cmd statusbar demo-ron --hide
+ * adb shell cmd statusbar demo-notif --hide
  * ```
  *
- * See [DemoRonCommand] for more information on the adb command spec.
+ * See [DemoNotifCommand] for more information on the adb command spec.
  */
 @SysUISingleton
-class DemoRonChipViewModel
+class DemoNotifChipViewModel
 @Inject
 constructor(
     private val commandRegistry: CommandRegistry,
@@ -63,19 +63,19 @@
     private val systemClock: SystemClock,
 ) : OngoingActivityChipViewModel, CoreStartable {
     override fun start() {
-        commandRegistry.registerCommand("demo-ron") { DemoRonCommand() }
+        commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() }
     }
 
     private val _chip =
         MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden())
     override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow()
 
-    private inner class DemoRonCommand : ParseableCommand("demo-ron") {
+    private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) {
         private val packageName: String? by
             param(
                 longName = "packageName",
                 shortName = "p",
-                description = "The package name for the demo RON app",
+                description = "The package name for app \"posting\" the demo notification",
                 valueParser = Type.String,
             )
 
@@ -99,15 +99,12 @@
             )
 
         private val hide by
-            flag(
-                longName = "hide",
-                description = "Hides any existing demo RON chip",
-            )
+            flag(longName = "hide", description = "Hides any existing demo notification chip")
 
         override fun execute(pw: PrintWriter) {
-            if (!StatusBarRonChips.isEnabled) {
+            if (!StatusBarNotifChips.isEnabled) {
                 pw.println(
-                    "Error: com.android.systemui.status_bar_ron_chips must be enabled " +
+                    "Error: com.android.systemui.status_bar_notification_chips must be enabled " +
                         "before using this demo feature"
                 )
                 return
@@ -167,8 +164,12 @@
                 return null
             }
             return OngoingActivityChipModel.ChipIcon.FullColorAppIcon(
-                Icon.Loaded(drawable = iconDrawable, contentDescription = null),
+                Icon.Loaded(drawable = iconDrawable, contentDescription = null)
             )
         }
     }
+
+    companion object {
+        private const val DEMO_COMMAND_NAME = "demo-notif"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
index 4ef1909..47ffbaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.shared
+package com.android.systemui.statusbar.chips.notification.shared
 
 import com.android.systemui.Flags
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
-/** Helper for reading or using the status bar RON chips flag state. */
+/** Helper for reading or using the status bar promoted notification chips flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object StatusBarRonChips {
+object StatusBarNotifChips {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS
 
     /** A token used for dependency declaration */
     val token: FlagToken
@@ -33,7 +33,7 @@
     /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.statusBarRonChips()
+        get() = Flags.statusBarNotificationChips()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
new file mode 100644
index 0000000..6ae9263
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.notification.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** A view model for status bar chips for promoted ongoing notifications. */
+@SysUISingleton
+class NotifChipsViewModel
+@Inject
+constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
+    /**
+     * A flow modeling the notification chips that should be shown. Emits an empty list if there are
+     * no notifications that should show a status bar chip.
+     */
+    val chips: Flow<List<OngoingActivityChipModel.Shown>> =
+        activeNotificationsInteractor.promotedOngoingNotifications.map { notifications ->
+            notifications.mapNotNull { it.toChipModel() }
+        }
+
+    /**
+     * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the
+     * notification has invalid data such that it can't be displayed as a chip.
+     */
+    private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? {
+        // TODO(b/364653005): Log error if there's no icon view.
+        val rawIcon = this.statusBarChipIconView ?: return null
+        val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon)
+        // TODO(b/364653005): Use the notification color if applicable.
+        val colors = ColorsModel.Themed
+        // TODO(b/364653005): When the chip is clicked, show the HUN.
+        val onClickListener = null
+        return OngoingActivityChipModel.Shown.ShortTimeDelta(
+            icon,
+            colors,
+            time = this.whenTime,
+            onClickListener,
+        )
+        // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta.
+        // TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text.
+        // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
+        // TODO(b/364653005): If the app that posted the notification is in the foreground, don't
+        // show that app's chip.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index 3b1e565..f4462a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -21,13 +21,14 @@
 import android.graphics.drawable.GradientDrawable
 import android.view.View
 import android.view.ViewGroup
+import android.widget.DateTimeView
 import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.TextView
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
@@ -42,6 +43,8 @@
         val chipTimeView: ChipChronometer =
             chipView.requireViewById(R.id.ongoing_activity_chip_time)
         val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text)
+        val chipShortTimeDeltaView: DateTimeView =
+            chipView.requireViewById(R.id.ongoing_activity_chip_short_time_delta)
         val chipBackgroundView: ChipBackgroundContainer =
             chipView.requireViewById(R.id.ongoing_activity_chip_background)
 
@@ -49,13 +52,14 @@
             is OngoingActivityChipModel.Shown -> {
                 // Data
                 setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
-                setChipMainContent(chipModel, chipTextView, chipTimeView)
+                setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView)
                 chipView.setOnClickListener(chipModel.onClickListener)
                 updateChipPadding(
                     chipModel,
                     chipBackgroundView,
                     chipTextView,
                     chipTimeView,
+                    chipShortTimeDeltaView,
                 )
 
                 // Accessibility
@@ -65,6 +69,7 @@
                 val textColor = chipModel.colors.text(chipContext)
                 chipTimeView.setTextColor(textColor)
                 chipTextView.setTextColor(textColor)
+                chipShortTimeDeltaView.setTextColor(textColor)
                 (chipBackgroundView.background as GradientDrawable).color =
                     chipModel.colors.background(chipContext)
             }
@@ -97,7 +102,7 @@
                 defaultIconView.tintView(iconTint)
             }
             is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
-                StatusBarRonChips.assertInNewMode()
+                StatusBarNotifChips.assertInNewMode()
                 IconViewBinder.bind(icon.impl, defaultIconView)
                 defaultIconView.visibility = View.VISIBLE
                 defaultIconView.untintView()
@@ -161,6 +166,7 @@
         chipModel: OngoingActivityChipModel.Shown,
         chipTextView: TextView,
         chipTimeView: ChipChronometer,
+        chipShortTimeDeltaView: DateTimeView,
     ) {
         when (chipModel) {
             is OngoingActivityChipModel.Shown.Countdown -> {
@@ -168,21 +174,35 @@
                 chipTextView.visibility = View.VISIBLE
 
                 chipTimeView.hide()
+                chipShortTimeDeltaView.visibility = View.GONE
             }
             is OngoingActivityChipModel.Shown.Text -> {
                 chipTextView.text = chipModel.text
                 chipTextView.visibility = View.VISIBLE
 
                 chipTimeView.hide()
+                chipShortTimeDeltaView.visibility = View.GONE
             }
             is OngoingActivityChipModel.Shown.Timer -> {
                 ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
                 chipTimeView.visibility = View.VISIBLE
 
                 chipTextView.visibility = View.GONE
+                chipShortTimeDeltaView.visibility = View.GONE
+            }
+            is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
+                chipShortTimeDeltaView.setTime(chipModel.time)
+                // TODO(b/364653005): DateTimeView's relative time doesn't quite match the format we
+                //  want in the status bar chips.
+                chipShortTimeDeltaView.isShowRelativeTime = true
+                chipShortTimeDeltaView.visibility = View.VISIBLE
+
+                chipTextView.visibility = View.GONE
+                chipTimeView.hide()
             }
             is OngoingActivityChipModel.Shown.IconOnly -> {
                 chipTextView.visibility = View.GONE
+                chipShortTimeDeltaView.visibility = View.GONE
                 chipTimeView.hide()
             }
         }
@@ -200,6 +220,7 @@
         backgroundView: View,
         chipTextView: TextView,
         chipTimeView: ChipChronometer,
+        chipShortTimeDeltaView: DateTimeView,
     ) {
         if (chipModel.icon != null) {
             if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
@@ -209,15 +230,18 @@
                 backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
                 chipTextView.setTextPaddingForEmbeddedPaddingIcon()
                 chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
+                chipShortTimeDeltaView.setTextPaddingForEmbeddedPaddingIcon()
             } else {
                 backgroundView.setBackgroundPaddingForNormalIcon()
                 chipTextView.setTextPaddingForNormalIcon()
                 chipTimeView.setTextPaddingForNormalIcon()
+                chipShortTimeDeltaView.setTextPaddingForNormalIcon()
             }
         } else {
             backgroundView.setBackgroundPaddingForNoIcon()
             chipTextView.setTextPaddingForNoIcon()
             chipTimeView.setTextPaddingForNoIcon()
+            chipShortTimeDeltaView.setTextPaddingForNoIcon()
         }
     }
 
@@ -258,23 +282,13 @@
             context.resources.getDimensionPixelSize(
                 R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
             )
-        setPaddingRelative(
-            sidePadding,
-            paddingTop,
-            sidePadding,
-            paddingBottom,
-        )
+        setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
     }
 
     private fun View.setBackgroundPaddingForNormalIcon() {
         val sidePadding =
             context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding)
-        setPaddingRelative(
-            sidePadding,
-            paddingTop,
-            sidePadding,
-            paddingBottom,
-        )
+        setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
     }
 
     private fun View.setBackgroundPaddingForNoIcon() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 62622a8..cf07af1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.Flags
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 
 /** Model representing the display of an ongoing activity as a chip in the status bar. */
 sealed class OngoingActivityChipModel {
@@ -79,6 +80,24 @@
         }
 
         /**
+         * The chip shows the time delta between now and [time] in a short format, e.g. "15min" or
+         * "1hr ago".
+         */
+        data class ShortTimeDelta(
+            override val icon: ChipIcon,
+            override val colors: ColorsModel,
+            /** The time of the event that this chip represents. */
+            val time: Long,
+            override val onClickListener: View.OnClickListener?,
+        ) : Shown(icon, colors, onClickListener) {
+            init {
+                StatusBarNotifChips.assertInNewMode()
+            }
+
+            override val logName = "Shown.ShortTimeDelta"
+        }
+
+        /**
          * This chip shows a countdown using [secondsUntilStarted]. Used to inform users that an
          * event is about to start. Typically, a [Countdown] chip will turn into a [Timer] chip.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 24c1b87..ed32597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
-import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
@@ -24,19 +23,20 @@
 import com.android.systemui.statusbar.chips.StatusBarChipsLog
 import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
 import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel
 import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -55,7 +55,8 @@
     shareToAppChipViewModel: ShareToAppChipViewModel,
     castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
     callChipViewModel: CallChipViewModel,
-    demoRonChipViewModel: DemoRonChipViewModel,
+    notifChipsViewModel: NotifChipsViewModel,
+    demoNotifChipViewModel: DemoNotifChipViewModel,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) {
     private enum class ChipType {
@@ -63,8 +64,9 @@
         ShareToApp,
         CastToOtherDevice,
         Call,
-        /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */
-        DemoRon,
+        Notification,
+        /** A demo of a notification chip, used just for testing. */
+        DemoNotification,
     }
 
     /** Model that helps us internally track the various chip states from each of the types. */
@@ -85,7 +87,8 @@
             val shareToApp: OngoingActivityChipModel.Hidden,
             val castToOtherDevice: OngoingActivityChipModel.Hidden,
             val call: OngoingActivityChipModel.Hidden,
-            val demoRon: OngoingActivityChipModel.Hidden,
+            val notifs: OngoingActivityChipModel.Hidden,
+            val demoNotif: OngoingActivityChipModel.Hidden,
         ) : InternalChipModel
     }
 
@@ -94,7 +97,8 @@
         val shareToApp: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
         val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
         val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
-        val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+        val notifs: List<OngoingActivityChipModel.Shown> = emptyList(),
+        val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
     )
 
     /** Bundles all the incoming chips into one object to easily pass to various flows. */
@@ -104,8 +108,9 @@
                 shareToAppChipViewModel.chip,
                 castToOtherDeviceChipViewModel.chip,
                 callChipViewModel.chip,
-                demoRonChipViewModel.chip,
-            ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon ->
+                notifChipsViewModel.chips,
+                demoNotifChipViewModel.chip,
+            ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif ->
                 logger.log(
                     TAG,
                     LogLevel.INFO,
@@ -121,16 +126,19 @@
                     LogLevel.INFO,
                     {
                         str1 = call.logName
-                        str2 = demoRon.logName
+                        // TODO(b/364653005): Log other information for notification chips.
+                        str2 = notifs.map { it.logName }.toString()
+                        str3 = demoNotif.logName
                     },
-                    { "... > Call=$str1 > DemoRon=$str2" }
+                    { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" },
                 )
                 ChipBundle(
                     screenRecord = screenRecord,
                     shareToApp = shareToApp,
                     castToOtherDevice = castToOtherDevice,
                     call = call,
-                    demoRon = demoRon,
+                    notifs = notifs,
+                    demoNotif = demoNotif,
                 )
             }
             // Some of the chips could have timers in them and we don't want the start time
@@ -189,9 +197,9 @@
      * actually displaying the chip.
      */
     val chips: StateFlow<MultipleOngoingActivityChipsModel> =
-        if (!Flags.statusBarRonChips()) {
-            // Multiple chips are only allowed with RONs. If the flag isn't on, use just the
-            // primary chip.
+        if (!StatusBarNotifChips.isEnabled) {
+            // Multiple chips are only allowed with notification chips. If the flag isn't on, use
+            // just the primary chip.
             primaryChip
                 .map {
                     MultipleOngoingActivityChipsModel(
@@ -199,11 +207,7 @@
                         secondary = OngoingActivityChipModel.Hidden(),
                     )
                 }
-                .stateIn(
-                    scope,
-                    SharingStarted.Lazily,
-                    MultipleOngoingActivityChipsModel(),
-                )
+                .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel())
         } else {
             internalChips
                 .pairwise(initialValue = DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL)
@@ -212,11 +216,7 @@
                     val correctSecondary = createOutputModel(old.secondary, new.secondary)
                     MultipleOngoingActivityChipsModel(correctPrimary, correctSecondary)
                 }
-                .stateIn(
-                    scope,
-                    SharingStarted.Lazily,
-                    MultipleOngoingActivityChipsModel(),
-                )
+                .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel())
         }
 
     /** A data class representing the return result of [pickMostImportantChip]. */
@@ -251,7 +251,7 @@
                             // suppress the share-to-app chip to make sure they don't both show.
                             // See b/296461748.
                             shareToApp = OngoingActivityChipModel.Hidden(),
-                        )
+                        ),
                 )
             bundle.shareToApp is OngoingActivityChipModel.Shown ->
                 MostImportantChipResult(
@@ -274,11 +274,19 @@
                     mostImportantChip = InternalChipModel.Shown(ChipType.Call, bundle.call),
                     remainingChips = bundle.copy(call = OngoingActivityChipModel.Hidden()),
                 )
-            bundle.demoRon is OngoingActivityChipModel.Shown -> {
-                StatusBarRonChips.assertInNewMode()
+            bundle.notifs.isNotEmpty() ->
                 MostImportantChipResult(
-                    mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon),
-                    remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()),
+                    mostImportantChip =
+                        InternalChipModel.Shown(ChipType.Notification, bundle.notifs.first()),
+                    remainingChips =
+                        bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)),
+                )
+            bundle.demoNotif is OngoingActivityChipModel.Shown -> {
+                StatusBarNotifChips.assertInNewMode()
+                MostImportantChipResult(
+                    mostImportantChip =
+                        InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif),
+                    remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()),
                 )
             }
             else -> {
@@ -287,7 +295,8 @@
                 check(bundle.shareToApp is OngoingActivityChipModel.Hidden)
                 check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden)
                 check(bundle.call is OngoingActivityChipModel.Hidden)
-                check(bundle.demoRon is OngoingActivityChipModel.Hidden)
+                check(bundle.notifs.isEmpty())
+                check(bundle.demoNotif is OngoingActivityChipModel.Hidden)
                 MostImportantChipResult(
                     mostImportantChip =
                         InternalChipModel.Hidden(
@@ -295,7 +304,8 @@
                             shareToApp = bundle.shareToApp,
                             castToOtherDevice = bundle.castToOtherDevice,
                             call = bundle.call,
-                            demoRon = bundle.demoRon,
+                            notifs = OngoingActivityChipModel.Hidden(),
+                            demoNotif = bundle.demoNotif,
                         ),
                     // All the chips are already hidden, so no need to filter anything out of the
                     // bundle.
@@ -323,7 +333,8 @@
                 ChipType.ShareToApp -> new.shareToApp
                 ChipType.CastToOtherDevice -> new.castToOtherDevice
                 ChipType.Call -> new.call
-                ChipType.DemoRon -> new.demoRon
+                ChipType.Notification -> new.notifs
+                ChipType.DemoNotification -> new.demoNotif
             }
         } else if (new is InternalChipModel.Shown) {
             // If we have a chip to show, always show it.
@@ -344,7 +355,8 @@
                 shareToApp = OngoingActivityChipModel.Hidden(),
                 castToOtherDevice = OngoingActivityChipModel.Hidden(),
                 call = OngoingActivityChipModel.Hidden(),
-                demoRon = OngoingActivityChipModel.Hidden(),
+                notifs = OngoingActivityChipModel.Hidden(),
+                demoNotif = OngoingActivityChipModel.Hidden(),
             )
 
         private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index dac0102..10090283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.connectivity
 
 import android.os.UserManager
+import com.android.systemui.bluetooth.qsdialog.dagger.AudioSharingModule
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION
 import com.android.systemui.qs.QsEventLogger
@@ -56,7 +57,7 @@
 import dagger.multibindings.IntoMap
 import dagger.multibindings.StringKey
 
-@Module
+@Module(includes = [AudioSharingModule::class])
 interface ConnectivityModule {
 
     /** Inject BluetoothTile into tileMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
new file mode 100644
index 0000000..b64a577
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.util.kotlin.pairwiseBy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+
+/**
+ * Responsible for creating and starting the status bar components for each display. Also does it
+ * for newly added displays.
+ */
+@SysUISingleton
+class MultiDisplayStatusBarStarter
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val displayScopeRepository: DisplayScopeRepository,
+    private val statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+    private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+    private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+    private val displayRepository: DisplayRepository,
+    private val initializerStore: StatusBarInitializerStore,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+    private val statusBarInitializerStore: StatusBarInitializerStore,
+) : CoreStartable {
+
+    init {
+        StatusBarConnectedDisplays.assertInNewMode()
+    }
+
+    override fun start() {
+        applicationScope.launch {
+            displayRepository.displays
+                .pairwiseBy { previousDisplays, currentDisplays ->
+                    currentDisplays - previousDisplays
+                }
+                .onStart { emit(displayRepository.displays.value) }
+                .collect { newDisplays ->
+                    newDisplays.forEach { createAndStartComponentsForDisplay(it) }
+                }
+        }
+    }
+
+    private fun createAndStartComponentsForDisplay(display: Display) {
+        val displayId = display.displayId
+        createAndStartOrchestratorForDisplay(displayId)
+        createAndStartInitializerForDisplay(displayId)
+    }
+
+    private fun createAndStartOrchestratorForDisplay(displayId: Int) {
+        statusBarOrchestratorFactory
+            .create(
+                displayId,
+                displayScopeRepository.scopeForDisplay(displayId),
+                statusBarWindowStateRepositoryStore.forDisplay(displayId),
+                statusBarModeRepositoryStore.forDisplay(displayId),
+                initializerStore.forDisplay(displayId),
+                statusBarWindowControllerStore.forDisplay(displayId),
+            )
+            .start()
+    }
+
+    private fun createAndStartInitializerForDisplay(displayId: Int) {
+        statusBarInitializerStore.forDisplay(displayId).start()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 3ad76b7..6201ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -16,27 +16,31 @@
 package com.android.systemui.statusbar.core
 
 import android.app.Fragment
+import android.view.ViewGroup
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.PhoneStatusBarView
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import java.lang.IllegalStateException
-import javax.inject.Inject
 import javax.inject.Provider
 
 /**
  * Responsible for creating the status bar window and initializing the root components of that
  * window (see [CollapsedStatusBarFragment])
  */
-interface StatusBarInitializer {
+interface StatusBarInitializer : CoreStartable {
 
     var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener?
 
@@ -56,7 +60,7 @@
          *   Can be used to retrieve dependencies from that scope, including the status bar root
          *   view.
          */
-        fun onStatusBarViewInitialized(component: StatusBarFragmentComponent)
+        fun onStatusBarViewInitialized(component: HomeStatusBarComponent)
     }
 
     interface OnStatusBarViewUpdatedListener {
@@ -65,17 +69,22 @@
             statusBarTransitions: PhoneStatusBarTransitions,
         )
     }
+
+    interface Factory {
+        fun create(statusBarWindowController: StatusBarWindowController): StatusBarInitializer
+    }
 }
 
-@SysUISingleton
 class StatusBarInitializerImpl
-@Inject
+@AssistedInject
 constructor(
-    private val windowController: StatusBarWindowController,
+    @Assisted private val statusBarWindowController: StatusBarWindowController,
     private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
+    private val statusBarRootFactory: StatusBarRootFactory,
+    private val componentFactory: HomeStatusBarComponent.Factory,
     private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
-) : CoreStartable, StatusBarInitializer {
-    private var component: StatusBarFragmentComponent? = null
+) : StatusBarInitializer {
+    private var component: HomeStatusBarComponent? = null
 
     @get:VisibleForTesting
     var initialized = false
@@ -105,21 +114,57 @@
     }
 
     private fun doStart() {
+        if (StatusBarSimpleFragment.isEnabled) doComposeStart() else doLegacyStart()
+    }
+
+    /**
+     * Stand up the [PhoneStatusBarView] in a compose root. There will be no
+     * [CollapsedStatusBarFragment] in this mode
+     */
+    private fun doComposeStart() {
         initialized = true
-        windowController.fragmentHostManager
+        val statusBarRoot =
+            statusBarRootFactory.create(statusBarWindowController.backgroundView as ViewGroup) { cv
+                ->
+                val phoneStatusBarView = cv.findViewById<PhoneStatusBarView>(R.id.status_bar)
+                component =
+                    componentFactory.create(phoneStatusBarView).also { component ->
+                        // CollapsedStatusBarFragment used to be responsible initializting
+                        component.init()
+
+                        statusBarViewUpdatedListener?.onStatusBarViewUpdated(
+                            component.phoneStatusBarViewController,
+                            component.phoneStatusBarTransitions,
+                        )
+
+                        creationListeners.forEach { listener ->
+                            listener.onStatusBarViewInitialized(component)
+                        }
+                    }
+            }
+
+        // Add the new compose view to the hierarchy because we don't use fragment transactions
+        // anymore
+        val windowBackgroundView = statusBarWindowController.backgroundView as ViewGroup
+        windowBackgroundView.addView(statusBarRoot)
+    }
+
+    private fun doLegacyStart() {
+        initialized = true
+        statusBarWindowController.fragmentHostManager
             .addTagListener(
                 CollapsedStatusBarFragment.TAG,
                 object : FragmentHostManager.FragmentListener {
                     override fun onFragmentViewCreated(tag: String, fragment: Fragment) {
-                        val statusBarFragmentComponent =
-                            (fragment as CollapsedStatusBarFragment).statusBarFragmentComponent
+                        component =
+                            (fragment as CollapsedStatusBarFragment).homeStatusBarComponent
                                 ?: throw IllegalStateException()
                         statusBarViewUpdatedListener?.onStatusBarViewUpdated(
-                            statusBarFragmentComponent.phoneStatusBarViewController,
-                            statusBarFragmentComponent.phoneStatusBarTransitions,
+                            component!!.phoneStatusBarViewController,
+                            component!!.phoneStatusBarTransitions,
                         )
                         creationListeners.forEach { listener ->
-                            listener.onStatusBarViewInitialized(statusBarFragmentComponent)
+                            listener.onStatusBarViewInitialized(component!!)
                         }
                     }
 
@@ -137,4 +182,11 @@
             )
             .commit()
     }
+
+    @AssistedFactory
+    interface Factory : StatusBarInitializer.Factory {
+        override fun create(
+            statusBarWindowController: StatusBarWindowController
+        ): StatusBarInitializerImpl
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
new file mode 100644
index 0000000..041f0b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Provides per display instances of [StatusBarInitializer]. */
+interface StatusBarInitializerStore : PerDisplayStore<StatusBarInitializer>
+
+@SysUISingleton
+class MultiDisplayStatusBarInitializerStore
+@Inject
+constructor(
+    @Background backgroundApplicationScope: CoroutineScope,
+    displayRepository: DisplayRepository,
+    private val factory: StatusBarInitializer.Factory,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+) :
+    StatusBarInitializerStore,
+    PerDisplayStoreImpl<StatusBarInitializer>(backgroundApplicationScope, displayRepository) {
+
+    init {
+        StatusBarConnectedDisplays.assertInNewMode()
+    }
+
+    override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer {
+        return factory.create(
+            statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId)
+        )
+    }
+
+    override val instanceClass = StatusBarInitializer::class.java
+}
+
+@SysUISingleton
+class SingleDisplayStatusBarInitializerStore
+@Inject
+constructor(defaultInitializer: StatusBarInitializer) :
+    StatusBarInitializerStore,
+    PerDisplayStore<StatusBarInitializer> by SingleDisplayStore(defaultInitializer) {
+
+    init {
+        StatusBarConnectedDisplays.assertInLegacyMode()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index 8bd990b..5e59745 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -16,12 +16,12 @@
 
 package com.android.systemui.statusbar.core
 
+import android.view.Display
 import android.view.View
 import com.android.systemui.CoreStartable
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.plugins.PluginDependencyProvider
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -31,19 +31,21 @@
 import com.android.systemui.statusbar.AutoHideUiElement
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
 import com.android.systemui.statusbar.phone.AutoHideController
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
-import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
 import com.android.wm.shell.bubbles.Bubbles
 import dagger.Lazy
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import java.io.PrintWriter
 import java.util.Optional
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
@@ -57,27 +59,35 @@
  * It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The
  * plan is break it out into individual classes.
  */
-@SysUISingleton
 class StatusBarOrchestrator
-@Inject
+@AssistedInject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
-    private val statusBarInitializer: StatusBarInitializer,
-    private val statusBarWindowController: StatusBarWindowController,
-    private val statusBarModeRepository: StatusBarModeRepositoryStore,
+    @Assisted private val displayId: Int,
+    @Assisted private val coroutineScope: CoroutineScope,
+    @Assisted private val statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+    @Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository,
+    @Assisted private val statusBarInitializer: StatusBarInitializer,
+    @Assisted private val statusBarWindowController: StatusBarWindowController,
     private val demoModeController: DemoModeController,
     private val pluginDependencyProvider: PluginDependencyProvider,
     private val autoHideController: AutoHideController,
     private val remoteInputManager: NotificationRemoteInputManager,
     private val notificationShadeWindowViewControllerLazy:
-    Lazy<NotificationShadeWindowViewController>,
+        Lazy<NotificationShadeWindowViewController>,
     private val shadeSurface: ShadeSurface,
     private val bubblesOptional: Optional<Bubbles>,
-    private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+    private val dumpManager: DumpManager,
     powerInteractor: PowerInteractor,
     primaryBouncerInteractor: PrimaryBouncerInteractor,
 ) : CoreStartable {
 
+    private val dumpableName: String =
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            javaClass.simpleName
+        } else {
+            "${javaClass.simpleName}$displayId"
+        }
+
     private val phoneStatusBarViewController =
         MutableStateFlow<PhoneStatusBarViewController?>(value = null)
 
@@ -86,9 +96,9 @@
 
     private val shouldAnimateNextBarModeChange =
         combine(
-            statusBarModeRepository.defaultDisplay.isTransientShown,
+            statusBarModeRepository.isTransientShown,
             powerInteractor.isAwake,
-            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+            statusBarWindowStateRepository.windowState,
         ) { isTransientShown, isDeviceAwake, statusBarWindowState ->
             !isTransientShown &&
                 isDeviceAwake &&
@@ -107,8 +117,8 @@
 
     private val statusBarVisible =
         combine(
-            statusBarModeRepository.defaultDisplay.statusBarMode,
-            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+            statusBarModeRepository.statusBarMode,
+            statusBarWindowStateRepository.windowState,
         ) { mode, statusBarWindowState ->
             mode != StatusBarMode.LIGHTS_OUT &&
                 mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT &&
@@ -119,7 +129,7 @@
         combine(
                 shouldAnimateNextBarModeChange,
                 phoneStatusBarTransitions.filterNotNull(),
-                statusBarModeRepository.defaultDisplay.statusBarMode,
+                statusBarModeRepository.statusBarMode,
                 ::Triple,
             )
             .distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
@@ -130,37 +140,40 @@
 
     override fun start() {
         StatusBarSimpleFragment.assertInNewMode()
-        applicationScope.launch {
-            launch {
-                controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
-                    setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+        coroutineScope
+            .launch {
+                dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator)
+                launch {
+                    controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
+                        setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+                    }
                 }
-            }
-            launch {
-                barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
-                    if (deviceAsleep) {
-                        barTransitions.finishAnimations()
+                launch {
+                    barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
+                        if (deviceAsleep) {
+                            barTransitions.finishAnimations()
+                        }
+                    }
+                }
+                launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
+                launch {
+                    barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
+                        updateBarMode(animate, barTransitions, statusBarMode)
                     }
                 }
             }
-            launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
-            launch {
-                barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
-                    updateBarMode(animate, barTransitions, statusBarMode)
-                }
-            }
-        }
+            .invokeOnCompletion { dumpManager.unregisterDumpable(dumpableName) }
         createAndAddWindow()
         setupPluginDependencies()
         setUpAutoHide()
     }
 
     private fun createAndAddWindow() {
-        initializeStatusBarFragment()
+        initializeStatusBarRootView()
         statusBarWindowController.attach()
     }
 
-    private fun initializeStatusBarFragment() {
+    private fun initializeStatusBarRootView() {
         statusBarInitializer.statusBarViewUpdatedListener =
             object : StatusBarInitializer.OnStatusBarViewUpdatedListener {
                 override fun onStatusBarViewUpdated(
@@ -170,6 +183,10 @@
                     phoneStatusBarViewController.value = statusBarViewController
                     phoneStatusBarTransitions.value = statusBarTransitions
 
+                    if (displayId != Display.DEFAULT_DISPLAY) {
+                        return
+                    }
+                    // TODO(b/373310629): shade should be display id aware
                     notificationShadeWindowViewControllerLazy
                         .get()
                         .setStatusBarViewController(statusBarViewController)
@@ -189,6 +206,10 @@
     }
 
     private fun setUpAutoHide() {
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            return
+        }
+        // TODO(b/373309973): per display implementation of auto hide controller
         autoHideController.setStatusBar(
             object : AutoHideUiElement {
                 override fun synchronizeState() {}
@@ -198,13 +219,14 @@
                 }
 
                 override fun isVisible(): Boolean {
-                    return statusBarModeRepository.defaultDisplay.isTransientShown.value
+                    return statusBarModeRepository.isTransientShown.value
                 }
 
                 override fun hide() {
-                    statusBarModeRepository.defaultDisplay.clearTransient()
+                    statusBarModeRepository.clearTransient()
                 }
-            })
+            }
+        )
     }
 
     private fun updateBarMode(
@@ -215,11 +237,18 @@
         if (!demoModeController.isInDemoMode) {
             barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
         }
-        autoHideController.touchAutoHide()
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            // TODO(b/373309973): per display implementation of auto hide controller
+            autoHideController.touchAutoHide()
+        }
     }
 
     private fun updateBubblesVisibility(statusBarVisible: Boolean) {
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            return
+        }
         bubblesOptional.ifPresent { bubbles: Bubbles ->
+            // TODO(b/373311537): per display implementation of Bubbles
             bubbles.onStatusBarVisibilityChanged(statusBarVisible)
         }
     }
@@ -238,11 +267,23 @@
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value)
+        pw.println(statusBarWindowStateRepository.windowState.value)
         CentralSurfaces.dumpBarTransitions(
             pw,
             "PhoneStatusBarTransitions",
             phoneStatusBarTransitions.value,
         )
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            displayId: Int,
+            displayScope: CoroutineScope,
+            statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+            statusBarModeRepository: StatusBarModePerDisplayRepository,
+            statusBarInitializer: StatusBarInitializer,
+            statusBarWindowController: StatusBarWindowController,
+        ): StatusBarOrchestrator
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index cf238d5..f6f4503 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -17,20 +17,28 @@
 package com.android.systemui.statusbar.dagger
 
 import android.content.Context
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.systemui.CoreStartable
+import com.android.systemui.SysUICutoutProvider
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.StatusBarDataLayerModule
 import com.android.systemui.statusbar.phone.LightBarController
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
+import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.SingleDisplayStatusBarWindowControllerStore
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerImpl
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import dagger.Binds
+import dagger.Lazy
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ClassKey
@@ -62,16 +70,39 @@
     @ClassKey(StatusBarSignalPolicy::class)
     abstract fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable
 
+    @Binds
+    @SysUISingleton
+    abstract fun statusBarWindowControllerFactory(
+        implFactory: StatusBarWindowControllerImpl.Factory
+    ): StatusBarWindowController.Factory
+
     companion object {
 
         @Provides
         @SysUISingleton
-        fun statusBarWindowController(
-            context: Context?,
-            viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager?,
-            factory: StatusBarWindowControllerImpl.Factory,
-        ): StatusBarWindowController {
-            return factory.create(context, viewCaptureAwareWindowManager)
+        fun windowControllerStore(
+            multiDisplayImplLazy: Lazy<MultiDisplayStatusBarWindowControllerStore>,
+            singleDisplayImplLazy: Lazy<SingleDisplayStatusBarWindowControllerStore>,
+        ): StatusBarWindowControllerStore {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                multiDisplayImplLazy.get()
+            } else {
+                singleDisplayImplLazy.get()
+            }
+        }
+
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(MultiDisplayStatusBarWindowControllerStore::class)
+        fun multiDisplayControllerStoreAsCoreStartable(
+            storeLazy: Lazy<MultiDisplayStatusBarWindowControllerStore>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                storeLazy.get()
+            } else {
+                CoreStartable.NOP
+            }
         }
 
         @Provides
@@ -80,5 +111,16 @@
         fun provideOngoingCallLogBuffer(factory: LogBufferFactory): LogBuffer {
             return factory.create("OngoingCall", 75)
         }
+
+        @Provides
+        @SysUISingleton
+        fun contentInsetsProvider(
+            factory: StatusBarContentInsetsProviderImpl.Factory,
+            context: Context,
+            configurationController: ConfigurationController,
+            sysUICutoutProvider: SysUICutoutProvider,
+        ): StatusBarContentInsetsProvider {
+            return factory.create(context, configurationController, sysUICutoutProvider)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index 9f878b2..e6270b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -17,6 +17,7 @@
 
 import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule
 import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule
 import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule
 import dagger.Module
@@ -26,8 +27,9 @@
         [
             KeyguardStatusBarRepositoryModule::class,
             RemoteInputRepositoryModule::class,
+            StatusBarConfigurationControllerModule::class,
             StatusBarModeRepositoryModule::class,
-            StatusBarPhoneDataLayerModule::class
+            StatusBarPhoneDataLayerModule::class,
         ]
 )
 object StatusBarDataLayerModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
new file mode 100644
index 0000000..280d66b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.data.repository
+
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.systemui.CoreStartable
+import com.android.systemui.common.ui.GlobalConfig
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Status bar specific interface to disambiguate from the global [ConfigurationController]. */
+interface StatusBarConfigurationController : ConfigurationController
+
+/** Provides per display instances of [ConfigurationController], specifically for the Status Bar. */
+interface StatusBarConfigurationControllerStore : PerDisplayStore<StatusBarConfigurationController>
+
+@SysUISingleton
+class MultiDisplayStatusBarConfigurationControllerStore
+@Inject
+constructor(
+    @Background backgroundApplicationScope: CoroutineScope,
+    displayRepository: DisplayRepository,
+    private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+    private val configurationControllerFactory: ConfigurationControllerImpl.Factory,
+) :
+    StatusBarConfigurationControllerStore,
+    PerDisplayStoreImpl<StatusBarConfigurationController>(
+        backgroundApplicationScope,
+        displayRepository,
+    ) {
+
+    init {
+        StatusBarConnectedDisplays.assertInNewMode()
+    }
+
+    override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController {
+        val displayWindowProperties =
+            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR)
+        return configurationControllerFactory.create(displayWindowProperties.context)
+    }
+
+    override val instanceClass = StatusBarConfigurationController::class.java
+}
+
+@SysUISingleton
+class SingleDisplayStatusBarConfigurationControllerStore
+@Inject
+constructor(@GlobalConfig globalConfigurationController: ConfigurationController) :
+    StatusBarConfigurationControllerStore,
+    PerDisplayStore<StatusBarConfigurationController> by SingleDisplayStore(
+        globalConfigurationController as StatusBarConfigurationController
+    ) {
+
+    init {
+        StatusBarConnectedDisplays.assertInLegacyMode()
+    }
+}
+
+@Module
+object StatusBarConfigurationControllerModule {
+
+    @Provides
+    @SysUISingleton
+    fun store(
+        singleDisplayLazy: Lazy<SingleDisplayStatusBarConfigurationControllerStore>,
+        multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationControllerStore>,
+    ): StatusBarConfigurationControllerStore {
+        return if (StatusBarConnectedDisplays.isEnabled) {
+            multiDisplayLazy.get()
+        } else {
+            singleDisplayLazy.get()
+        }
+    }
+
+    @Provides
+    @SysUISingleton
+    @IntoMap
+    @ClassKey(StatusBarConfigurationControllerStore::class)
+    fun storeAsCoreStartable(
+        multiDisplayLazy: Lazy<MultiDisplayStatusBarConfigurationControllerStore>
+    ): CoreStartable {
+        return if (StatusBarConnectedDisplays.isEnabled) {
+            multiDisplayLazy.get()
+        } else {
+            CoreStartable.NOP
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 088c86d..44bee1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.phone.BoundsPair
 import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import dagger.assisted.Assisted
@@ -174,7 +174,7 @@
 
     private val _statusBarBounds = MutableStateFlow(BoundsPair(Rect(), Rect()))
 
-    override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
+    override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) {
         val statusBarBoundsProvider = component.boundsProvider
         val listener =
             object : StatusBarBoundsProvider.BoundsChangeListener {
@@ -196,10 +196,9 @@
 
     /** Modifies the raw [StatusBarAttributes] if letterboxing is needed. */
     private val modifiedStatusBarAttributes: StateFlow<ModifiedStatusBarAttributes?> =
-        combine(
-                _originalStatusBarAttributes,
-                _statusBarBounds,
-            ) { originalAttributes, statusBarBounds ->
+        combine(_originalStatusBarAttributes, _statusBarBounds) {
+                originalAttributes,
+                statusBarBounds ->
                 if (originalAttributes == null) {
                     null
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
index 962cb095..2c9fa25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.DisplayId
 import com.android.systemui.statusbar.core.StatusBarInitializer
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -31,6 +31,7 @@
 
 interface StatusBarModeRepositoryStore {
     val defaultDisplay: StatusBarModePerDisplayRepository
+
     fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository
 }
 
@@ -39,7 +40,7 @@
 @Inject
 constructor(
     @DisplayId private val displayId: Int,
-    factory: StatusBarModePerDisplayRepositoryFactory
+    factory: StatusBarModePerDisplayRepositoryFactory,
 ) :
     StatusBarModeRepositoryStore,
     CoreStartable,
@@ -47,17 +48,15 @@
     override val defaultDisplay = factory.create(displayId)
 
     override fun forDisplay(displayId: Int) =
-        if (this.displayId == displayId) {
-            defaultDisplay
-        } else {
-            TODO("b/127878649 implement multi-display state management")
-        }
+        // TODO(b/369337087): implement per display status bar modes.
+        //  For now just use default display instance.
+        defaultDisplay
 
     override fun start() {
         defaultDisplay.start()
     }
 
-    override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
+    override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) {
         defaultDisplay.onStatusBarViewInitialized(component)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 118f5f0..bf7e879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.animation.AnimationUtil.Companion.frames
 import javax.inject.Inject
 import kotlin.math.roundToInt
@@ -44,8 +44,8 @@
  */
 class SystemEventChipAnimationController @Inject constructor(
     private val context: Context,
-    private val statusBarWindowController: StatusBarWindowController,
-    private val contentInsetsProvider: StatusBarContentInsetsProvider
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+    private val contentInsetsProvider: StatusBarContentInsetsProvider,
 ) : SystemStatusAnimationCallback {
 
     private lateinit var animationWindowView: FrameLayout
@@ -244,7 +244,7 @@
         val height = themedContext.resources.getDimensionPixelSize(R.dimen.status_bar_height)
         val lp = FrameLayout.LayoutParams(MATCH_PARENT, height)
         lp.gravity = Gravity.END or Gravity.TOP
-        statusBarWindowController.addViewToWindow(animationWindowView, lp)
+        statusBarWindowControllerStore.defaultDisplay.addViewToWindow(animationWindowView, lp)
         animationWindowView.clipToPadding = false
         animationWindowView.clipChildren = false
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index f0e60dd..e34f61d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -23,7 +23,7 @@
 import androidx.core.animation.AnimatorSet
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.Assert
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
@@ -65,7 +65,7 @@
 constructor(
     private val coordinator: SystemEventCoordinator,
     private val chipAnimationController: SystemEventChipAnimationController,
-    private val statusBarWindowController: StatusBarWindowController,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
     dumpManager: DumpManager,
     private val systemClock: SystemClock,
     @Application private val coroutineScope: CoroutineScope,
@@ -277,7 +277,7 @@
     private fun runChipAppearAnimation() {
         Assert.isMainThread()
         if (hasPersistentDot) {
-            statusBarWindowController.setForceStatusBarVisible(true)
+            statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(true)
         }
         animationState.value = ANIMATING_IN
 
@@ -311,7 +311,7 @@
                             scheduledEvent.value != null -> ANIMATION_QUEUED
                             else -> IDLE
                         }
-                    statusBarWindowController.setForceStatusBarVisible(false)
+                    statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(false)
                 }
             }
         )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 693ae66..ebaa3c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -20,7 +20,7 @@
 import android.view.MotionEvent
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import javax.inject.Inject
 
 /** A class to detect when a user swipes away the status bar. */
@@ -31,10 +31,11 @@
     context: Context,
     displayTracker: DisplayTracker,
     logger: SwipeUpGestureLogger,
-    private val statusBarWindowController: StatusBarWindowController,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
 ) : SwipeUpGestureHandler(context, displayTracker, logger, loggerTag = LOGGER_TAG) {
     override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean {
         // Gesture starts just below the status bar
+        val statusBarWindowController = statusBarWindowControllerStore.defaultDisplay
         return ev.y >= statusBarWindowController.statusBarHeight &&
             ev.y <= 3 * statusBarWindowController.statusBarHeight
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
index 0927a72..a614638 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
@@ -115,23 +115,23 @@
                 final float h = y - mInitialTouchY;
                 if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
                         && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
-                    setTrackingHeadsUp(true);
-                    mCollapseSnoozes = h < 0;
-                    mInitialTouchX = x;
-                    mInitialTouchY = y;
-                    int startHeight = (int) (mPickedChild.getActualHeight()
-                                                + mPickedChild.getTranslationY());
-                    mPanel.setHeadsUpDraggingStartingHeight(startHeight);
-                    mPanel.startExpand(x, y, true /* startTracking */, startHeight);
-
                     if (!SceneContainerFlag.isEnabled()) {
+                        setTrackingHeadsUp(true);
+                        mCollapseSnoozes = h < 0;
+                        mInitialTouchX = x;
+                        mInitialTouchY = y;
+                        int startHeight = (int) (mPickedChild.getActualHeight()
+                                + mPickedChild.getTranslationY());
+                        mPanel.setHeadsUpDraggingStartingHeight(startHeight);
+                        mPanel.startExpand(x, y, true /* startTracking */, startHeight);
+
                         // This call needs to be after the expansion start otherwise we will get a
                         // flicker of one frame as it's not expanded yet.
                         mHeadsUpManager.unpinAll(true);
-                    }
 
-                    clearNotificationEffects();
-                    endMotion();
+                        clearNotificationEffects();
+                        endMotion();
+                    }
                     return true;
                 }
                 break;
@@ -167,17 +167,70 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (!mTrackingHeadsUp) {
+        if (SceneContainerFlag.isEnabled()) {
+            int pointerIndex = event.findPointerIndex(mTrackingPointer);
+            if (pointerIndex < 0) {
+                pointerIndex = 0;
+                mTrackingPointer = event.getPointerId(pointerIndex);
+            }
+            final float x = event.getX(pointerIndex);
+            final float y = event.getY(pointerIndex);
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_POINTER_UP:
+                    final int upPointer = event.getPointerId(event.getActionIndex());
+                    if (mTrackingPointer == upPointer) {
+                        // gesture is ongoing, find a new pointer to track
+                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                        mTrackingPointer = event.getPointerId(newIndex);
+                        mInitialTouchX = event.getX(newIndex);
+                        mInitialTouchY = event.getY(newIndex);
+                    }
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    final float h = y - mInitialTouchY;
+                    if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
+                            && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+                        setTrackingHeadsUp(true);
+                        mCollapseSnoozes = h < 0;
+                        mInitialTouchX = x;
+                        mInitialTouchY = y;
+                        int startHeight = (int) (mPickedChild.getActualHeight()
+                                + mPickedChild.getTranslationY());
+                        mPanel.setHeadsUpDraggingStartingHeight(startHeight);
+                        mPanel.startExpand(x, y, true /* startTracking */, startHeight);
+
+                        clearNotificationEffects();
+                        endMotion();
+                        return true;
+                    }
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP:
+                    if (mPickedChild != null && mTouchingHeadsUpView) {
+                        // We may swallow this click if the heads up just came in.
+                        if (mHeadsUpManager.shouldSwallowClick(
+                                mPickedChild.getEntry().getSbn().getKey())) {
+                            endMotion();
+                            return true;
+                        }
+                    }
+                    endMotion();
+                    return false;
+            }
             return false;
+        } else {
+            if (!mTrackingHeadsUp) {
+                return false;
+            }
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    endMotion();
+                    setTrackingHeadsUp(false);
+                    break;
+            }
+            return true;
         }
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                endMotion();
-                setTrackingHeadsUp(false);
-                break;
-        }
-        return true;
     }
 
     private void endMotion() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinator.kt
deleted file mode 100644
index d268e35..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinator.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.coordinator
-
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-
-/**
- * Filter out notifications on the lockscreen if the lockscreen hosted dream is active. If the user
- * stops dreaming, pulls the shade down or unlocks the device, then the notifications are unhidden.
- */
-@CoordinatorScope
-class DreamCoordinator
-@Inject
-constructor(
-    private val statusBarStateController: SysuiStatusBarStateController,
-    @Application private val scope: CoroutineScope,
-    private val keyguardRepository: KeyguardRepository,
-) : Coordinator {
-    private var isOnKeyguard = false
-    private var isLockscreenHostedDream = false
-
-    override fun attach(pipeline: NotifPipeline) {
-        pipeline.addPreGroupFilter(filter)
-        statusBarStateController.addCallback(statusBarStateListener)
-        scope.launch { attachFilterOnDreamingStateChange() }
-        recordStatusBarState(statusBarStateController.state)
-    }
-
-    private val filter =
-        object : NotifFilter("LockscreenHostedDreamFilter") {
-            var isFiltering = false
-            override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean {
-                return isFiltering
-            }
-            inline fun update(msg: () -> String) {
-                val wasFiltering = isFiltering
-                isFiltering = isLockscreenHostedDream && isOnKeyguard
-                if (wasFiltering != isFiltering) {
-                    invalidateList(msg())
-                }
-            }
-        }
-
-    private val statusBarStateListener =
-        object : StatusBarStateController.StateListener {
-            override fun onStateChanged(newState: Int) {
-                recordStatusBarState(newState)
-            }
-        }
-
-    private suspend fun attachFilterOnDreamingStateChange() {
-        keyguardRepository.isActiveDreamLockscreenHosted.collect { isDreaming ->
-            recordDreamingState(isDreaming)
-        }
-    }
-
-    private fun recordStatusBarState(newState: Int) {
-        isOnKeyguard = newState == StatusBarState.KEYGUARD
-        filter.update { "recordStatusBarState: " + StatusBarState.toString(newState) }
-    }
-
-    private fun recordDreamingState(isDreaming: Boolean) {
-        isLockscreenHostedDream = isDreaming
-        filter.update { "recordLockscreenHostedDreamState: $isDreaming" }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 96c260b..46d4560f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.statusbar.notification.collection.coordinator
 
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
 import com.android.systemui.statusbar.notification.collection.PipelineDumpable
@@ -41,7 +39,6 @@
 @Inject
 constructor(
     sectionStyleProvider: SectionStyleProvider,
-    featureFlags: FeatureFlags,
     dataStoreCoordinator: DataStoreCoordinator,
     hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
     hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
@@ -70,7 +67,6 @@
     visualStabilityCoordinator: VisualStabilityCoordinator,
     sensitiveContentCoordinator: SensitiveContentCoordinator,
     dismissibilityCoordinator: DismissibilityCoordinator,
-    dreamCoordinator: DreamCoordinator,
     statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
     bundleCoordinator: BundleCoordinator,
 ) : NotifCoordinators {
@@ -115,10 +111,6 @@
         mCoordinators.add(remoteInputCoordinator)
         mCoordinators.add(dismissibilityCoordinator)
 
-        if (featureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
-            mCoordinators.add(dreamCoordinator)
-        }
-
         if (NotificationsLiveDataStoreRefactor.isEnabled) {
             mCoordinators.add(statsLoggerCoordinator)
         }
@@ -152,10 +144,7 @@
         sectionStyleProvider.setMinimizedSections(setOf(rankingCoordinator.minimizedSectioner))
         if (SortBySectionTimeFlag.isEnabled) {
             sectionStyleProvider.setSilentSections(
-                listOf(
-                    rankingCoordinator.silentSectioner,
-                    rankingCoordinator.minimizedSectioner,
-                )
+                listOf(rankingCoordinator.silentSectioner, rankingCoordinator.minimizedSectioner)
             )
         } else {
             sectionStyleProvider.setSilentSections(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index c52632e..9d5d7a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -275,7 +275,8 @@
             }
         }
 
-        if (LockscreenOtpRedaction.isEnabled()) {
+        if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
+
             if (inflaterParams.isChildInGroup() && needsRedaction) {
                 params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index ca5f49d..684ce48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -88,6 +88,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModesCleanupStartable;
 
 import dagger.Binds;
 import dagger.Module;
@@ -299,4 +300,10 @@
             ZenModeRepository repository) {
         return new NotificationsSoundPolicyInteractor(repository);
     }
+
+    /** Binds {@link ZenModesCleanupStartable} as a {@link CoreStartable}. */
+    @Binds
+    @IntoMap
+    @ClassKey(ZenModesCleanupStartable.class)
+    CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup);
 }
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 9b382e6..697a6ce 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
@@ -17,6 +17,7 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 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
@@ -26,6 +27,7 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
@@ -74,6 +76,17 @@
     val allNotificationsCountValue: Int
         get() = repository.activeNotifications.value.individuals.size
 
+    /** The notifications that are promoted and ongoing. Sorted by priority order. */
+    val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> =
+        if (StatusBarNotifChips.isEnabled) {
+            // TODO(b/364653005): Filter all the notifications down to just the promoted ones.
+            // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow
+            // instead of being separate.
+            topLevelRepresentativeNotifications
+        } else {
+            flowOf(emptyList())
+        }
+
     /**
      * The priority ongoing call notification, or null if there is no ongoing call.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
index 695e088..fbec640 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.icu.text.MessageFormat
-import com.android.app.tracing.coroutines.flow.flowOn
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.modes.shared.ModesUi
@@ -40,6 +39,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 7b6a2cb..d538f52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -444,10 +444,12 @@
                 if (onFinishedRunnable != null) {
                     onFinishedRunnable.run();
                 }
+                if (mRunWithoutInterruptions) {
+                    enableAppearDrawing(false);
+                }
 
                 // We need to reset the View state, even if the animation was cancelled
-                enableAppearDrawing(false);
-                onAppearAnimationFinished(isAppearing);
+                onAppearAnimationFinished(isAppearing, /* cancelled = */ !mRunWithoutInterruptions);
 
                 if (mRunWithoutInterruptions) {
                     InteractionJankMonitor.getInstance().end(getCujType(isAppearing));
@@ -461,6 +463,7 @@
                 if (onStartedRunnable != null) {
                     onStartedRunnable.run();
                 }
+                onAppearAnimationStarted(isAppearing);
                 mRunWithoutInterruptions = true;
                 Configuration.Builder builder = Configuration.Builder
                         .withView(getCujType(isAppearing), ActivatableNotificationView.this);
@@ -484,6 +487,8 @@
                 frameTimeNanos -> {
                     if (mAppearAnimator == cachedAnimator) {
                         mAppearAnimator.start();
+                    } else {
+                        onAppearAnimationSkipped(isAppearing);
                     }
                 }, delay);
     }
@@ -500,7 +505,13 @@
         }
     }
 
-    protected void onAppearAnimationFinished(boolean wasAppearing) {
+    protected void onAppearAnimationStarted(boolean isAppear) {
+    }
+
+    protected void onAppearAnimationSkipped(boolean isAppear) {
+    }
+
+    protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) {
     }
 
     private void cancelAppearAnimation() {
@@ -828,4 +839,13 @@
             });
         }
     }
+
+    protected void dumpAppearAnimationProperties(IndentingPrintWriter pw, String[] args) {
+        pw.print("AppearAnimation: ");
+        pw.print("mDrawingAppearAnimation", mDrawingAppearAnimation);
+        pw.print("mAppearAnimationFraction", mAppearAnimationFraction);
+        pw.print("mIsHeadsUpAnimation", mIsHeadsUpAnimation);
+        pw.print("mTargetPoint", mTargetPoint);
+        pw.println();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index adb3352..38e6609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1015,7 +1015,7 @@
         }
         mNotificationParent = isChildInGroup ? parent : null;
         mPrivateLayout.setIsChildInGroup(isChildInGroup);
-        if (LockscreenOtpRedaction.isEnabled()) {
+        if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
             mPublicLayout.setIsChildInGroup(isChildInGroup);
         }
 
@@ -1835,6 +1835,25 @@
         void logSkipResetAllContentAlphas(
                 NotificationEntry entry
         );
+
+        /** Called when we start an appear animation. */
+        void logStartAppearAnimation(NotificationEntry entry, boolean isAppear);
+
+        /** Called when we cancel the running appear animation. */
+        void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing);
+
+        /** Called when the animator of the appear animation is started. */
+        void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear);
+
+        /** Called when we prepared an appear animation, but the animator was never started. */
+        void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear);
+
+        /** Called when the animator of the appear animation is finished. */
+        void logAppearAnimationFinished(
+                NotificationEntry entry,
+                boolean isAppear,
+                boolean cancelled
+        );
     }
 
     /**
@@ -3165,6 +3184,13 @@
     }
 
     @Override
+    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
+            Runnable onFinishRunnable) {
+        mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ true);
+        super.performAddAnimation(delay, duration, isHeadsUpAppear, onFinishRunnable);
+    }
+
+    @Override
     public long performRemoveAnimation(
             long duration,
             long delay,
@@ -3173,6 +3199,7 @@
             Runnable onStartedRunnable,
             Runnable onFinishedRunnable,
             AnimatorListenerAdapter animationListener, ClipSide clipSide) {
+        mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ false);
         if (mMenuRow != null && mMenuRow.isMenuVisible()) {
             Animator anim = getTranslateViewAnimator(0f, null /* listener */);
             if (anim != null) {
@@ -3201,8 +3228,25 @@
     }
 
     @Override
-    protected void onAppearAnimationFinished(boolean wasAppearing) {
-        super.onAppearAnimationFinished(wasAppearing);
+    protected void onAppearAnimationStarted(boolean isAppear) {
+        mLogger.logAppearAnimationStarted(getEntry(), /* isAppear = */ isAppear);
+        super.onAppearAnimationStarted(isAppear);
+    }
+
+    @Override
+    protected void onAppearAnimationSkipped(boolean isAppear) {
+        mLogger.logAppearAnimationSkipped(getEntry(),  /* isAppear = */ isAppear);
+        super.onAppearAnimationSkipped(isAppear);
+    }
+
+    @Override
+    protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) {
+        mLogger.logAppearAnimationFinished(
+                /* entry = */ getEntry(),
+                /* isAppear = */ wasAppearing,
+                /* cancelled = */ cancelled
+        );
+        super.onAppearAnimationFinished(wasAppearing, cancelled);
         if (wasAppearing) {
             // During the animation the visible view might have changed, so let's make sure all
             // alphas are reset
@@ -3218,6 +3262,12 @@
     }
 
     @Override
+    public void cancelAppearDrawing() {
+        mLogger.logCancelAppearDrawing(getEntry(), isDrawingAppearAnimation());
+        super.cancelAppearDrawing();
+    }
+
+    @Override
     public void resetAllContentAlphas() {
         mLogger.logResetAllContentAlphas(getEntry());
         mPrivateLayout.setAlpha(1f);
@@ -3883,6 +3933,7 @@
                 dumpHeights(pw);
             }
             showingLayout.dump(pw, args);
+            dumpAppearAnimationProperties(pw, args);
             dumpCustomOutline(pw, args);
             dumpClipping(pw, args);
             if (getViewState() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c31a2cb..23a2fac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -209,6 +209,32 @@
                 ) {
                     mLogBufferLogger.logSkipResetAllContentAlphas(entry);
                 }
+
+                @Override
+                public void logStartAppearAnimation(NotificationEntry entry, boolean isAppear) {
+                    mLogBufferLogger.logStartAppearAnimation(entry, isAppear);
+                }
+
+                @Override
+                public void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing) {
+                    mLogBufferLogger.logCancelAppearDrawing(entry, wasDrawing);
+                }
+
+                @Override
+                public void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear) {
+                    mLogBufferLogger.logAppearAnimationStarted(entry, isAppear);
+                }
+
+                @Override
+                public void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear) {
+                    mLogBufferLogger.logAppearAnimationSkipped(entry, isAppear);
+                }
+
+                @Override
+                public void logAppearAnimationFinished(NotificationEntry entry, boolean isAppear,
+                        boolean cancelled) {
+                    mLogBufferLogger.logAppearAnimationFinished(entry, isAppear, cancelled);
+                }
             };
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
index b90aa10..71f9c07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
@@ -19,6 +19,7 @@
 import android.widget.flags.Flags.notifLinearlayoutOptimized
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
 import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing
 import javax.inject.Inject
 import javax.inject.Provider
@@ -35,6 +36,7 @@
     bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory,
     optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory,
     notificationViewFlipperFactory: Provider<NotificationViewFlipperFactory>,
+    notificationRowIconViewInflaterFactory: NotificationRowIconViewInflaterFactory,
 ) : NotifRemoteViewsFactoryContainer {
     override val factories: Set<NotifRemoteViewsFactory> = buildSet {
         add(precomputedTextViewFactory)
@@ -47,5 +49,8 @@
         if (NotificationViewFlipperPausing.isEnabled) {
             add(notificationViewFlipperFactory.get())
         }
+        if (android.app.Flags.notificationsRedesignAppIcons()) {
+            add(notificationRowIconViewInflaterFactory)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 69f45db..41abac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -223,7 +223,7 @@
                     );
         }
 
-        if (LockscreenOtpRedaction.isEnabled()) {
+        if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
             result.mPublicInflatedSingleLineViewModel =
                     SingleLineViewInflater.inflateRedactedSingleLineViewModel(row.getContext(),
                             isConversation);
@@ -309,7 +309,7 @@
                 });
                 break;
             case FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE:
-                if (LockscreenOtpRedaction.isEnabled()) {
+                if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
                     row.getPublicLayout()
                             .performWhenContentInactive(VISIBLE_TYPE_SINGLELINE, () -> {
                                 row.getPublicLayout().setSingleLineView(null);
@@ -342,7 +342,7 @@
      * Cancel any pending content view frees from {@link #freeNotificationView} for the provided
      * content views.
      *
-     * @param row top level notification row containing the content views
+     * @param row          top level notification row containing the content views
      * @param contentViews content views to cancel pending frees on
      */
     private void cancelContentViewFrees(
@@ -360,7 +360,7 @@
         if ((contentViews & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
             row.getPublicLayout().removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED);
         }
-        if (LockscreenOtpRedaction.isEnabled()
+        if (LockscreenOtpRedaction.isSingleLineViewEnabled()
                 && (contentViews & FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE) != 0) {
             row.getPublicLayout().removeContentInactiveRunnable(VISIBLE_TYPE_SINGLELINE);
         }
@@ -478,6 +478,13 @@
                 notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP));
         setRemoteViewsInflaterFactory(result.newPublicView,
                 notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC));
+        if (android.app.Flags.notificationsRedesignAppIcons()) {
+            setRemoteViewsInflaterFactory(result.mNewGroupHeaderView,
+                    notifLayoutInflaterFactoryProvider.provide(row, FLAG_GROUP_SUMMARY_HEADER));
+            setRemoteViewsInflaterFactory(result.mNewMinimizedGroupHeaderView,
+                    notifLayoutInflaterFactoryProvider.provide(row,
+                            FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER));
+        }
     }
 
     private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews,
@@ -516,6 +523,7 @@
                     logger.logAsyncTaskProgress(entry, "contracted view applied");
                     result.inflatedContentView = v;
                 }
+
                 @Override
                 public RemoteViews getRemoteView() {
                     return result.newContentView;
@@ -974,7 +982,7 @@
             }
         }
 
-        if (LockscreenOtpRedaction.isEnabled()
+        if (LockscreenOtpRedaction.isSingleLineViewEnabled()
                 && (reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE) != 0) {
             HybridNotificationView view = result.mPublicInflatedSingleLineView;
             SingleLineViewModel viewModel = result.mPublicInflatedSingleLineViewModel;
@@ -1254,7 +1262,7 @@
                         );
             }
 
-            if (LockscreenOtpRedaction.isEnabled()) {
+            if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
                 result.mPublicInflatedSingleLineViewModel =
                         SingleLineViewInflater.inflateRedactedSingleLineViewModel(mContext,
                                 isConversation);
@@ -1406,6 +1414,7 @@
     @VisibleForTesting
     abstract static class ApplyCallback {
         public abstract void setResultView(View v);
+
         public abstract RemoteViews getRemoteView();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index b1e9032..6e78f56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -30,7 +30,7 @@
 @Inject
 constructor(
     @NotificationLog private val buffer: LogBuffer,
-    @NotificationRenderLog private val notificationRenderBuffer: LogBuffer
+    @NotificationRenderLog private val notificationRenderBuffer: LogBuffer,
 ) {
     fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) {
         buffer.log(
@@ -40,7 +40,7 @@
                 str1 = child.logKey
                 str2 = oldParent.logKey
             },
-            { "Detach child $str1 kept in parent $str2" }
+            { "Detach child $str1 kept in parent $str2" },
         )
     }
 
@@ -52,13 +52,13 @@
                 str1 = child.logKey
                 str2 = newParent.logKey
             },
-            { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }
+            { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" },
         )
     }
 
     fun logRemoveTransientFromContainer(
         childEntry: NotificationEntry,
-        containerEntry: NotificationEntry
+        containerEntry: NotificationEntry,
     ) {
         notificationRenderBuffer.log(
             TAG,
@@ -67,25 +67,20 @@
                 str1 = childEntry.logKey
                 str2 = containerEntry.logKey
             },
-            { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" }
+            { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" },
         )
     }
 
-    fun logRemoveTransientFromNssl(
-        childEntry: NotificationEntry,
-    ) {
+    fun logRemoveTransientFromNssl(childEntry: NotificationEntry) {
         notificationRenderBuffer.log(
             TAG,
             LogLevel.INFO,
             { str1 = childEntry.logKey },
-            { "RemoveTransientRow from Nssl: childKey: $str1" }
+            { "RemoveTransientRow from Nssl: childKey: $str1" },
         )
     }
 
-    fun logRemoveTransientFromViewGroup(
-        childEntry: NotificationEntry,
-        containerView: ViewGroup,
-    ) {
+    fun logRemoveTransientFromViewGroup(childEntry: NotificationEntry, containerView: ViewGroup) {
         notificationRenderBuffer.log(
             TAG,
             LogLevel.WARNING,
@@ -93,14 +88,14 @@
                 str1 = childEntry.logKey
                 str2 = containerView.toString()
             },
-            { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" }
+            { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" },
         )
     }
 
     fun logAddTransientRow(
         childEntry: NotificationEntry,
         containerEntry: NotificationEntry,
-        index: Int
+        index: Int,
     ) {
         notificationRenderBuffer.log(
             TAG,
@@ -110,14 +105,11 @@
                 str2 = containerEntry.logKey
                 int1 = index
             },
-            { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" }
+            { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" },
         )
     }
 
-    fun logRemoveTransientRow(
-        childEntry: NotificationEntry,
-        containerEntry: NotificationEntry,
-    ) {
+    fun logRemoveTransientRow(childEntry: NotificationEntry, containerEntry: NotificationEntry) {
         notificationRenderBuffer.log(
             TAG,
             LogLevel.ERROR,
@@ -125,7 +117,7 @@
                 str1 = childEntry.logKey
                 str2 = containerEntry.logKey
             },
-            { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }
+            { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" },
         )
     }
 
@@ -134,7 +126,7 @@
             TAG,
             LogLevel.INFO,
             { str1 = entry.logKey },
-            { "resetAllContentAlphas: $str1" }
+            { "resetAllContentAlphas: $str1" },
         )
     }
 
@@ -143,7 +135,72 @@
             TAG,
             LogLevel.INFO,
             { str1 = entry.logKey },
-            { "Skip resetAllContentAlphas: $str1" }
+            { "Skip resetAllContentAlphas: $str1" },
+        )
+    }
+
+    fun logStartAppearAnimation(entry: NotificationEntry, isAppear: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+            },
+            { "startAppearAnimation childKey: $str1 isAppear:$bool1" },
+        )
+    }
+
+    fun logCancelAppearDrawing(entry: NotificationEntry, wasDrawing: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.WARNING,
+            {
+                str1 = entry.logKey
+                bool1 = wasDrawing
+            },
+            { "cancelAppearDrawing childKey: $str1 wasDrawing:$bool1" },
+        )
+    }
+
+    fun logAppearAnimationStarted(entry: NotificationEntry, isAppear: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+            },
+            { "onAppearAnimationStarted childKey: $str1 isAppear:$bool1" },
+        )
+    }
+
+    fun logAppearAnimationSkipped(entry: NotificationEntry, isAppear: Boolean) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.WARNING,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+            },
+            { "Skipped an appear animation childKey: $str1 isAppear:$bool1" },
+        )
+    }
+
+    fun logAppearAnimationFinished(
+        entry: NotificationEntry,
+        isAppear: Boolean,
+        cancelled: Boolean,
+    ) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = entry.logKey
+                bool1 = isAppear
+                bool2 = cancelled
+            },
+            { "onAppearAnimationFinished childKey: $str1 isAppear:$bool1 cancelled:$bool2" },
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 84f2f66..4feb78a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.notification.row;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.notification.row.icon.AppIconProviderModule;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderModule;
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 
 import dagger.Binds;
@@ -28,7 +30,7 @@
 /**
  * Dagger Module containing notification row and view inflation implementations.
  */
-@Module
+@Module(includes = {AppIconProviderModule.class, NotificationIconStyleProviderModule.class})
 public abstract class NotificationRowModule {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index d67947d..4e26ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -90,7 +90,7 @@
                 notification = notification,
                 isGroupConversation = isGroupConversation,
                 builder = builder,
-                systemUiContext = systemUiContext
+                systemUiContext = systemUiContext,
             )
 
         val conversationData =
@@ -98,7 +98,7 @@
                 // We don't show the sender's name for one-to-one conversation
                 conversationSenderName =
                     if (isGroupConversation) conversationTextData?.senderName else null,
-                avatar = conversationAvatar
+                avatar = conversationAvatar,
             )
 
         return SingleLineViewModel(
@@ -111,7 +111,7 @@
     @JvmStatic
     fun inflateRedactedSingleLineViewModel(
         context: Context,
-        isConversation: Boolean = false
+        isConversation: Boolean = false,
     ): SingleLineViewModel {
         val conversationData =
             if (isConversation) {
@@ -122,7 +122,7 @@
                             com.android.systemui.res.R.drawable
                                 .ic_redacted_notification_single_line_icon
                         )
-                    )
+                    ),
                 )
             } else {
                 null
@@ -134,7 +134,7 @@
             context.getString(
                 com.android.systemui.res.R.string.redacted_notification_single_line_text
             ),
-            conversationData
+            conversationData,
         )
     }
 
@@ -159,11 +159,13 @@
         }
 
         // load the sender's name to display
-        val name = lastMessage.senderPerson?.name
+        // null senderPerson means the current user.
+        val name = lastMessage.senderPerson?.name ?: user.name
+
         val senderName =
             systemUiContext.resources.getString(
                 R.string.conversation_single_line_name_display,
-                if (Flags.cleanUpSpansAndNewLines()) name?.toString() else name
+                if (Flags.cleanUpSpansAndNewLines()) name?.toString() else name,
             )
 
         // We need to find back-up values for those texts if they are needed and empty
@@ -333,7 +335,7 @@
                         sender.icon
                             ?: builder.getDefaultAvatar(
                                 name = sender.name,
-                                uniqueNames = uniqueNames
+                                uniqueNames = uniqueNames,
                             )
                     lastKey = senderKey
                 } else {
@@ -341,7 +343,7 @@
                         sender.icon
                             ?: builder.getDefaultAvatar(
                                 name = sender.name,
-                                uniqueNames = uniqueNames
+                                uniqueNames = uniqueNames,
                             )
                     break
                 }
@@ -424,7 +426,7 @@
 
     private fun Notification.Builder.getDefaultAvatar(
         name: CharSequence?,
-        uniqueNames: PeopleHelper.NameToPrefixMap? = null
+        uniqueNames: PeopleHelper.NameToPrefixMap? = null,
     ): Icon {
         val layoutColor = getSmallIconColor(/* isHeader= */ false)
         if (!name.isNullOrEmpty()) {
@@ -432,7 +434,7 @@
             return peopleHelper.createAvatarSymbol(
                 /* name = */ name,
                 /* symbol = */ symbol,
-                /* layoutColor = */ layoutColor
+                /* layoutColor = */ layoutColor,
             )
         }
         // If name is null, create default avatar with background color
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
new file mode 100644
index 0000000..24b5cf1a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.icon
+
+import android.app.ActivityManager
+import android.app.Flags
+import android.content.Context
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.util.Log
+import com.android.internal.R
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import javax.inject.Provider
+
+/** A provider used to cache and fetch app icons used by notifications. */
+interface AppIconProvider {
+    @Throws(NameNotFoundException::class)
+    fun getOrFetchAppIcon(packageName: String, context: Context): Drawable
+}
+
+@SysUISingleton
+class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider {
+    private val iconFactory: BaseIconFactory
+        get() {
+            val isLowRam = ActivityManager.isLowRamDeviceStatic()
+            val res = sysuiContext.resources
+            val iconSize: Int =
+                res.getDimensionPixelSize(
+                    if (isLowRam) R.dimen.notification_small_icon_size_low_ram
+                    else R.dimen.notification_small_icon_size
+                )
+            return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize)
+        }
+
+    override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable {
+        val icon = context.packageManager.getApplicationIcon(packageName)
+        return BitmapDrawable(
+            context.resources,
+            iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE),
+        )
+    }
+}
+
+class NoOpIconProvider : AppIconProvider {
+    companion object {
+        const val TAG = "NoOpIconProvider"
+    }
+
+    override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable {
+        Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.")
+        return ColorDrawable(Color.WHITE)
+    }
+}
+
+@Module
+class AppIconProviderModule {
+    @Provides
+    @SysUISingleton
+    fun provideImpl(realImpl: Provider<AppIconProviderImpl>): AppIconProvider =
+        if (Flags.notificationsRedesignAppIcons()) {
+            realImpl.get()
+        } else {
+            NoOpIconProvider()
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
new file mode 100644
index 0000000..165c1a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.icon
+
+import android.annotation.WorkerThread
+import android.app.Flags
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import javax.inject.Provider
+
+/**
+ * A provider used to cache and fetch information about which icon should be displayed by
+ * notifications.
+ */
+interface NotificationIconStyleProvider {
+    @WorkerThread
+    fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean
+}
+
+@SysUISingleton
+class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIconStyleProvider {
+    override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
+        val packageContext = notification.getPackageContext(context)
+        return !belongsToHeadlessSystemApp(packageContext)
+    }
+
+    @WorkerThread
+    private fun belongsToHeadlessSystemApp(context: Context): Boolean {
+        val info = context.applicationInfo
+        if (info != null) {
+            if ((info.flags and ApplicationInfo.FLAG_SYSTEM) == 0) {
+                // It's not a system app at all.
+                return false
+            } else {
+                // If there's no launch intent, it's probably a headless app.
+                val pm = context.packageManager
+                return (pm.getLaunchIntentForPackage(info.packageName) == null)
+            }
+        } else {
+            // If for some reason we don't have the app info, we don't know; best assume it's
+            // not a system app.
+            return false
+        }
+    }
+}
+
+class NoOpIconStyleProvider : NotificationIconStyleProvider {
+    companion object {
+        const val TAG = "NoOpIconStyleProvider"
+    }
+
+    override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
+        Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.")
+        return true
+    }
+}
+
+@Module
+class NotificationIconStyleProviderModule {
+    @Provides
+    @SysUISingleton
+    fun provideImpl(
+        realImpl: Provider<NotificationIconStyleProviderImpl>
+    ): NotificationIconStyleProvider =
+        if (Flags.notificationsRedesignAppIcons()) {
+            realImpl.get()
+        } else {
+            NoOpIconStyleProvider()
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
new file mode 100644
index 0000000..79defd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.icon
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.NotificationRowIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder
+import javax.inject.Inject
+
+/**
+ * A factory which owns the construction of any NotificationRowIconView inside of Notifications in
+ * SystemUI. This allows overriding the small icon with the app icon in notifications.
+ */
+class NotificationRowIconViewInflaterFactory
+@Inject
+constructor(
+    private val appIconProvider: AppIconProvider,
+    private val iconStyleProvider: NotificationIconStyleProvider,
+) : NotifRemoteViewsFactory {
+    override fun instantiate(
+        row: ExpandableNotificationRow,
+        @NotificationRowContentBinder.InflationFlag layoutType: Int,
+        parent: View?,
+        name: String,
+        context: Context,
+        attrs: AttributeSet,
+    ): View? {
+        return when (name) {
+            NotificationRowIconView::class.java.name ->
+                NotificationRowIconView(context, attrs).also { view ->
+                    val sbn = row.entry.sbn
+                    view.setIconProvider(
+                        object : NotificationRowIconView.NotificationIconProvider {
+                            override fun shouldShowAppIcon(): Boolean {
+                                return iconStyleProvider.shouldShowAppIcon(row.entry.sbn, context)
+                            }
+
+                            override fun getAppIcon(): Drawable {
+                                return appIconProvider.getOrFetchAppIcon(sbn.packageName, context)
+                            }
+                        }
+                    )
+                }
+            else -> null
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt
index 078deb9..da4a126 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt
@@ -30,6 +30,9 @@
 
     @JvmStatic
     inline val isEnabled
-        get() =
-            redactSensitiveContentNotificationsOnLockscreen() && AsyncHybridViewInflation.isEnabled
+        get() = redactSensitiveContentNotificationsOnLockscreen()
+
+    @JvmStatic
+    inline val isSingleLineViewEnabled
+        get() = isEnabled && AsyncHybridViewInflation.isEnabled
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationProgressTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationProgressTemplateViewWrapper.kt
new file mode 100644
index 0000000..a693fd3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationProgressTemplateViewWrapper.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper
+
+import android.content.Context
+import android.view.View
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/** Wraps a notification containing a progress template */
+class NotificationProgressTemplateViewWrapper(
+    ctx: Context,
+    view: View,
+    row: ExpandableNotificationRow,
+) : NotificationTemplateViewWrapper(ctx, view, row)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 22b95ef..182fba3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -76,6 +76,8 @@
                 return new NotificationCompactHeadsUpTemplateViewWrapper(ctx, v, row);
             } else if ("compactMessagingHUN".equals((v.getTag()))) {
                 return new NotificationCompactMessagingTemplateViewWrapper(ctx, v, row);
+            } else if ("progress".equals(v.getTag())) {
+                return new NotificationProgressTemplateViewWrapper(ctx, v, row);
             }
 
             if (row.getEntry().getSbn().getNotification().isStyle(
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 d828a67..ec1dc0a7 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
@@ -25,6 +25,8 @@
 import static com.android.systemui.Flags.notificationOverExpansionClippingFix;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
+import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_DOWN;
+import static com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent.SCROLL_UP;
 import static com.android.systemui.util.DumpUtilsKt.println;
 import static com.android.systemui.util.DumpUtilsKt.visibilityString;
 
@@ -118,8 +120,10 @@
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
 import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent;
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState;
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -609,7 +613,7 @@
         @Override
         public boolean isScrolledToTop() {
             if (SceneContainerFlag.isEnabled()) {
-                return mScrollViewFields.isScrolledToTop();
+                return mScrollViewFields.getScrollState().isScrolledToTop();
             } else {
                 return mOwnScrollY == 0;
             }
@@ -1247,9 +1251,25 @@
     }
 
     @Override
-    public void setScrolledToTop(boolean scrolledToTop) {
-        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
-        mScrollViewFields.setScrolledToTop(scrolledToTop);
+    public void setScrollState(@NonNull ShadeScrollState scrollState) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+
+        boolean forwardScrollable =
+                scrollState.getScrollPosition() < scrollState.getMaxScrollPosition();
+        boolean backwardScrollable = scrollState.getScrollPosition() > 0;
+        mScrollable = forwardScrollable || backwardScrollable;
+        mForwardScrollable = forwardScrollable;
+        mBackwardScrollable = backwardScrollable;
+
+        boolean scrollPositionChanged = mScrollViewFields.getScrollState().getScrollPosition()
+                != scrollState.getScrollPosition();
+        mScrollViewFields.setScrollState(scrollState);
+
+        if (scrollPositionChanged) {
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+        }
     }
 
     @Override
@@ -1295,6 +1315,12 @@
     }
 
     @Override
+    public void setAccessibilityScrollEventConsumer(
+            @Nullable Consumer<AccessibilityScrollEvent> consumer) {
+        mScrollViewFields.setAccessibilityScrollEventConsumer(consumer);
+    }
+
+    @Override
     public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mScrollViewFields.setCurrentGestureOverscrollConsumer(consumer);
@@ -2645,6 +2671,11 @@
         return mHeadsUpInset;
     }
 
+    @Override
+    public int getStackBottomInset() {
+        return mPaddingBetweenElements + mShelf.getIntrinsicHeight();
+    }
+
     /**
      * Calculate the gap height between two different views
      *
@@ -4243,17 +4274,27 @@
      */
     @Override
     public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled.
-        if (SceneContainerFlag.isEnabled()) {
-            return super.performAccessibilityActionInternal(action, arguments);
-        }
-
         if (super.performAccessibilityActionInternal(action, arguments)) {
             return true;
         }
         if (!isEnabled()) {
             return false;
         }
+
+        if (SceneContainerFlag.isEnabled()) {
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                case android.R.id.accessibilityActionScrollDown:
+                    mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_DOWN);
+                    return true;
+                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                case android.R.id.accessibilityActionScrollUp:
+                    mScrollViewFields.sendAccessibilityScrollEvent(SCROLL_UP);
+                    return true;
+            }
+            return false;
+        }
+
         int direction = -1;
         switch (action) {
             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
@@ -5029,25 +5070,21 @@
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
-        // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled.
-        if (SceneContainerFlag.isEnabled()) {
-            return;
-        }
-
         event.setScrollable(mScrollable);
         event.setMaxScrollX(mScrollX);
-        event.setScrollY(mOwnScrollY);
-        event.setMaxScrollY(getScrollRange());
+
+        if (SceneContainerFlag.isEnabled()) {
+            event.setScrollY(mScrollViewFields.getScrollState().getScrollPosition());
+            event.setMaxScrollY(mScrollViewFields.getScrollState().getMaxScrollPosition());
+        } else {
+            event.setScrollY(mOwnScrollY);
+            event.setMaxScrollY(getScrollRange());
+        }
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        // Don't handle scroll accessibility events from the NSSL, when SceneContainer enabled.
-        if (SceneContainerFlag.isEnabled()) {
-            return;
-        }
-
         if (mScrollable) {
             info.setScrollable(true);
             if (mBackwardScrollable) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 00c5c40..04974b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -24,6 +24,7 @@
 import static com.android.server.notification.Flags.screenshareNotificationHiding;
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.Flags.confineNotificationTouchToViewWidth;
+import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
@@ -607,6 +608,16 @@
                             true /* requireMinHeight */,
                             false /* ignoreDecors */,
                             !confineNotificationTouchToViewWidth() /* ignoreWidth */);
+
+                    // Verify the MotionEvent x,y are actually inside the touch area of the shelf,
+                    // since the shelf may be animated down to a collapsed size on keyguard.
+                    if (ignoreTouchesNextToNotificationShelf()) {
+                        if (child instanceof NotificationShelf shelf) {
+                            if (!NotificationSwipeHelper.isTouchInView(ev, shelf)) {
+                                return null;
+                            }
+                        }
+                    }
                     if (child instanceof ExpandableNotificationRow row) {
                         ExpandableNotificationRow parent = row.getNotificationParent();
                         if (parent != null && parent.areChildrenExpanded()
@@ -2122,9 +2133,6 @@
             boolean hunWantsIt = false;
             if (shouldHeadsUpHandleTouch()) {
                 hunWantsIt = mHeadsUpTouchHelper.onInterceptTouchEvent(ev);
-                if (hunWantsIt) {
-                    mView.startDraggingOnHun();
-                }
             }
             boolean swipeWantsIt = false;
             if (mLongPressedView == null && !mView.isBeingDragged()
@@ -2210,6 +2218,9 @@
             boolean hunWantsIt = false;
             if (shouldHeadsUpHandleTouch()) {
                 hunWantsIt = mHeadsUpTouchHelper.onTouchEvent(ev);
+                if (hunWantsIt) {
+                    mView.startDraggingOnHun();
+                }
             }
 
             // Check if we need to clear any snooze leavebehinds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 7e327e6..0e94ca35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -18,6 +18,7 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+import static com.android.systemui.Flags.ignoreTouchesNextToNotificationShelf;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -39,6 +40,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -503,13 +505,21 @@
         final int height = (view instanceof ExpandableView)
                 ? ((ExpandableView) view).getActualHeight()
                 : view.getHeight();
+        final int width;
+        if (ignoreTouchesNextToNotificationShelf()) {
+            width = (view instanceof NotificationShelf)
+                ? ((NotificationShelf) view).getActualWidth()
+                : view.getWidth();
+        } else {
+            width = view.getWidth();
+        }
         final int rx = (int) ev.getRawX();
         final int ry = (int) ev.getRawY();
         int[] temp = new int[2];
         view.getLocationOnScreen(temp);
         final int x = temp[0];
         final int y = temp[1];
-        Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
+        Rect rect = new Rect(x, y, x + width, y + height);
         boolean ret = rect.contains(rx, ry);
         return ret;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index f6e8b8f..fa20e43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.notification.stack
 
 import android.util.IndentingPrintWriter
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState
 import com.android.systemui.util.printSection
 import com.android.systemui.util.println
 import java.util.function.Consumer
@@ -32,8 +34,9 @@
 class ScrollViewFields {
     /** Used to produce the clipping path */
     var scrimClippingShape: ShadeScrimShape? = null
-    /** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */
-    var isScrolledToTop: Boolean = true
+
+    /** Scroll state of the notification shade. */
+    var scrollState: ShadeScrollState = ShadeScrollState()
 
     /**
      * Height in view pixels at which the Notification Stack would like to be laid out, including
@@ -47,6 +50,13 @@
      * placeholder
      */
     var syntheticScrollConsumer: Consumer<Float>? = null
+
+    /**
+     * When the NSSL navigates through the notifications with TalkBack, it can send scroll events
+     * here, to be able to browse through the whole list of notifications in the shade.
+     */
+    var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null
+
     /**
      * When a gesture is consumed internally by NSSL but needs to be handled by other elements (such
      * as the notif scrim) as overscroll, we can notify the placeholder through here.
@@ -64,12 +74,6 @@
      */
     var remoteInputRowBottomBoundConsumer: Consumer<Float?>? = null
 
-    /**
-     * Any time the heads up height is recalculated, it should be updated here to be used by the
-     * placeholder
-     */
-    var headsUpHeightConsumer: Consumer<Float>? = null
-
     /** send the [syntheticScroll] to the [syntheticScrollConsumer], if present. */
     fun sendSyntheticScroll(syntheticScroll: Float) =
         syntheticScrollConsumer?.accept(syntheticScroll)
@@ -86,10 +90,15 @@
     fun sendRemoteInputRowBottomBound(bottomY: Float?) =
         remoteInputRowBottomBoundConsumer?.accept(bottomY)
 
+    /** send an [AccessibilityScrollEvent] to the [accessibilityScrollEventConsumer] if present */
+    fun sendAccessibilityScrollEvent(event: AccessibilityScrollEvent) {
+        accessibilityScrollEventConsumer?.accept(event)
+    }
+
     fun dump(pw: IndentingPrintWriter) {
         pw.printSection("StackViewStates") {
             pw.println("scrimClippingShape", scrimClippingShape)
-            pw.println("isScrolledToTop", isScrolledToTop)
+            pw.println("scrollState", scrollState)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index c0f1a56..5ec4c89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -17,7 +17,10 @@
 package com.android.systemui.statusbar.notification.stack.data.repository
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState
+import java.util.function.Consumer
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 
@@ -44,9 +47,9 @@
     /** height made available to the notifications in the size-constrained mode of lock screen. */
     val constrainedAvailableSpace = MutableStateFlow(0)
 
-    /**
-     * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
-     * further.
-     */
-    val scrolledToTop = MutableStateFlow(true)
+    /** Scroll state of the notification shade. */
+    val shadeScrollState = MutableStateFlow(ShadeScrollState())
+
+    /** A consumer of [AccessibilityScrollEvent]s. */
+    var accessibilityScrollEventConsumer: Consumer<AccessibilityScrollEvent>? = null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 32e092b..d4dd1d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -23,8 +23,11 @@
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.data.repository.NotificationPlaceholderRepository
 import com.android.systemui.statusbar.notification.stack.data.repository.NotificationViewHeightRepository
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState
+import java.util.function.Consumer
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
@@ -78,11 +81,9 @@
     val constrainedAvailableSpace: StateFlow<Int> =
         placeholderRepository.constrainedAvailableSpace.asStateFlow()
 
-    /**
-     * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
-     * further.
-     */
-    val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow()
+    /** Scroll state of the notification shade. */
+    val shadeScrollState: StateFlow<ShadeScrollState> =
+        placeholderRepository.shadeScrollState.asStateFlow()
 
     /**
      * The amount in px that the notification stack should scroll due to internal expansion. This
@@ -123,9 +124,9 @@
         placeholderRepository.shadeScrimBounds.value = bounds
     }
 
-    /** Sets whether the notification stack is scrolled to the top. */
-    fun setScrolledToTop(scrolledToTop: Boolean) {
-        placeholderRepository.scrolledToTop.value = scrolledToTop
+    /** Updates the current scroll state of the notification shade. */
+    fun setScrollState(shadeScrollState: ShadeScrollState) {
+        placeholderRepository.shadeScrollState.value = shadeScrollState
     }
 
     /** Sets the amount (px) that the notification stack should scroll due to internal expansion. */
@@ -133,6 +134,16 @@
         viewHeightRepository.syntheticScroll.value = delta
     }
 
+    /** Sends an [AccessibilityScrollEvent] to scroll the stack up or down. */
+    fun sendAccessibilityScrollEvent(accessibilityScrollEvent: AccessibilityScrollEvent) {
+        placeholderRepository.accessibilityScrollEventConsumer?.accept(accessibilityScrollEvent)
+    }
+
+    /** Set a consumer for the [AccessibilityScrollEvent]s to be handled by the placeholder. */
+    fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?) {
+        placeholderRepository.accessibilityScrollEventConsumer = consumer
+    }
+
     /** Sets whether the current touch gesture is overscroll. */
     fun setCurrentGestureOverscroll(isOverscroll: Boolean) {
         viewHeightRepository.isCurrentGestureOverscroll.value = isOverscroll
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/AccessibilityScrollEvent.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/AccessibilityScrollEvent.kt
index f4d281d..01341e1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/AccessibilityScrollEvent.kt
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.notification.stack.shared.model
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
-
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+/**
+ * An event to be sent by the NotificationStackScrollLayout to the NotificationsPlaceholder, when
+ * TalkBack runs out of visible notifications, and wants to scroll the shade to access more.
+ */
+enum class AccessibilityScrollEvent {
+    SCROLL_UP,
+    SCROLL_DOWN,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrollState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrollState.kt
new file mode 100644
index 0000000..3963286
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/model/ShadeScrollState.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.shared.model
+
+data class ShadeScrollState(
+    /**
+     * Whether the notification stack is scrolled to the top (i.e. when freshly opened). It also
+     * returns true, when scrolling is not possible because all the content fits in the current
+     * viewport.
+     */
+    val isScrolledToTop: Boolean = true,
+
+    /**
+     * Current scroll position of the shade. 0 when scrolled to the top, [maxScrollPosition] when
+     * scrolled all the way to the bottom.
+     */
+    val scrollPosition: Int = 0,
+
+    /**
+     * Max scroll position of the shade. 0, when no scrolling is possible e.g. all the content fits
+     * in the current viewport.
+     */
+    val maxScrollPosition: Int = 0,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 6ad9f01..5249a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.statusbar.notification.stack.ui.view
 
 import android.view.View
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState
 import java.util.function.Consumer
 
 /**
@@ -35,6 +37,9 @@
     /** Height in pixels required to display the top HeadsUp Notification. */
     val topHeadsUpHeight: Int
 
+    /** Bottom inset of the Notification Stack that us used to display the Shelf. */
+    val stackBottomInset: Int
+
     /**
      * Since this is an interface rather than a literal View, this provides cast-like access to the
      * underlying view.
@@ -62,12 +67,15 @@
     /** set the bottom-most y position in px, where we can draw HUNs in this view's coordinates */
     fun setHeadsUpBottom(headsUpBottom: Float)
 
-    /** set whether the view has been scrolled all the way to the top */
-    fun setScrolledToTop(scrolledToTop: Boolean)
+    /** Updates the current scroll state of the notification shade. */
+    fun setScrollState(scrollState: ShadeScrollState)
 
     /** Set a consumer for synthetic scroll events */
     fun setSyntheticScrollConsumer(consumer: Consumer<Float>?)
 
+    /** Set a consumer for accessibility actions to be handled by the placeholder. */
+    fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?)
+
     /** Set a consumer for current gesture overscroll events */
     fun setCurrentGestureOverscrollConsumer(consumer: Consumer<Boolean>?)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 87d70ba..4a76871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -17,7 +17,8 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewbinder
 
 import android.util.Log
-import com.android.app.tracing.coroutines.flow.filter
+import com.android.app.tracing.coroutines.flow.collectTraced
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.dagger.SysUISingleton
@@ -37,7 +38,6 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.launch
 
 /** Binds the [NotificationScrollView]. */
 @SysUISingleton
@@ -79,39 +79,47 @@
             launch {
                 viewModel
                     .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
-                    .collect { view.setScrimClippingShape(it) }
+                    .collectTraced { view.setScrimClippingShape(it) }
             }
 
-            launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
-            launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+            launch { viewModel.maxAlpha.collectTraced { view.setMaxAlpha(it) } }
+            launch { viewModel.shadeScrollState.collect { view.setScrollState(it) } }
             launch {
-                viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) }
+                viewModel.expandFraction.collectTraced {
+                    view.setExpandFraction(it.coerceIn(0f, 1f))
+                }
             }
-            launch { viewModel.qsExpandFraction.collect { view.setQsExpandFraction(it) } }
+            launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } }
             launch {
-                viewModel.isShowingStackOnLockscreen.collect {
+                viewModel.isShowingStackOnLockscreen.collectTraced {
                     view.setShowingStackOnLockscreen(it)
                 }
             }
             launch {
-                viewModel.alphaForLockscreenFadeIn.collect { view.setAlphaForLockscreenFadeIn(it) }
+                viewModel.alphaForLockscreenFadeIn.collectTraced {
+                    view.setAlphaForLockscreenFadeIn(it)
+                }
             }
-            launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
-            launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
+            launch { viewModel.isScrollable.collectTraced { view.setScrollingEnabled(it) } }
+            launch { viewModel.isDozing.collectTraced { isDozing -> view.setDozing(isDozing) } }
             launch {
-                viewModel.isPulsing.collect { isPulsing ->
+                viewModel.isPulsing.collectTraced { isPulsing ->
                     view.setPulsing(isPulsing, viewModel.shouldAnimatePulse.value)
                 }
             }
             launch {
                 viewModel.shouldResetStackTop
                     .filter { it }
-                    .collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
+                    .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
             }
             launch {
-                viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() }
+                viewModel.shouldCloseGuts
+                    .filter { it }
+                    .collectTraced { view.closeGutsOnSceneTouch() }
             }
-            launch { viewModel.suppressHeightUpdates.collect { view.suppressHeightUpdates(it) } }
+            launch {
+                viewModel.suppressHeightUpdates.collectTraced { view.suppressHeightUpdates(it) }
+            }
 
             launchAndDispose {
                 view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
@@ -120,11 +128,13 @@
                 view.setRemoteInputRowBottomBoundConsumer(
                     viewModel.remoteInputRowBottomBoundConsumer
                 )
+                view.setAccessibilityScrollEventConsumer(viewModel.accessibilityScrollEventConsumer)
                 DisposableHandle {
                     view.setSyntheticScrollConsumer(null)
                     view.setCurrentGestureOverscrollConsumer(null)
                     view.setCurrentGestureInGutsConsumer(null)
                     view.setRemoteInputRowBottomBoundConsumer(null)
+                    view.setAccessibilityScrollEventConsumer(null)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index aec81b0..574ca3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -34,8 +34,10 @@
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimClipping
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
 import com.android.systemui.util.kotlin.ActivatableFlowDumper
@@ -255,16 +257,16 @@
     val maxAlpha: Flow<Float> =
         stackAppearanceInteractor.alphaForBrightnessMirror.dumpValue("maxAlpha")
 
-    /**
-     * Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
-     * further.
-     */
-    val scrolledToTop: Flow<Boolean> =
-        stackAppearanceInteractor.scrolledToTop.dumpValue("scrolledToTop")
+    /** Scroll state of the notification shade. */
+    val shadeScrollState: Flow<ShadeScrollState> = stackAppearanceInteractor.shadeScrollState
 
     /** Receives the amount (px) that the stack should scroll due to internal expansion. */
     val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll
 
+    /** Receives an event to scroll the stack up or down. */
+    val accessibilityScrollEventConsumer: (AccessibilityScrollEvent) -> Unit =
+        stackAppearanceInteractor::sendAccessibilityScrollEvent
+
     /**
      * Receives whether the current touch gesture is overscroll as it has already been consumed by
      * the stack.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index c8e8358..a8ce47c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -27,12 +27,15 @@
 import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState
 import com.android.systemui.util.kotlin.ActivatableFlowDumper
 import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import java.util.function.Consumer
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
@@ -140,9 +143,9 @@
     /** The bottom bound of the currently focused remote input notification row. */
     val remoteInputRowBottomBound = remoteInputInteractor.remoteInputRowBottomBound
 
-    /** Sets whether the notification stack is scrolled to the top. */
-    fun setScrolledToTop(scrolledToTop: Boolean) {
-        interactor.setScrolledToTop(scrolledToTop)
+    /** Updates the current scroll state of the notification shade. */
+    fun setScrollState(scrollState: ShadeScrollState) {
+        interactor.setScrollState(scrollState)
     }
 
     /** Sets whether the heads up notification is animating away. */
@@ -155,6 +158,11 @@
         headsUpNotificationInteractor.snooze()
     }
 
+    /** Set a consumer for accessibility events to be handled by the placeholder. */
+    fun setAccessibilityScrollEventConsumer(consumer: Consumer<AccessibilityScrollEvent>?) {
+        interactor.setAccessibilityScrollEventConsumer(consumer)
+    }
+
     @AssistedFactory
     interface Factory {
         fun create(): NotificationsPlaceholderViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 0ad22e0..878ae91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -20,6 +20,7 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.flow.flowName
 import com.android.systemui.common.shared.model.NotificationContainerBounds
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -68,6 +69,8 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
@@ -75,6 +78,7 @@
 import com.android.systemui.util.kotlin.FlowDumperImpl
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -137,8 +141,10 @@
     private val primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
     private val primaryBouncerToLockscreenTransitionViewModel:
         PrimaryBouncerToLockscreenTransitionViewModel,
-    private val aodBurnInViewModel: AodBurnInViewModel,
+    aodBurnInViewModel: AodBurnInViewModel,
     private val communalSceneInteractor: CommunalSceneInteractor,
+    // Lazy because it's only used in the SceneContainer + Dual Shade configuration.
+    headsUpNotificationInteractor: Lazy<HeadsUpNotificationInteractor>,
     unfoldTransitionInteractor: UnfoldTransitionInteractor,
 ) : FlowDumperImpl(dumpManager) {
 
@@ -153,6 +159,7 @@
             ) { shadeExpansion, qsExpansion ->
                 shadeExpansion || qsExpansion
             }
+            .flowName("isAnyExpanded")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -170,6 +177,7 @@
                 isAnyExpanded ->
                 isShadeLocked && isAnyExpanded
             }
+            .flowName("isShadeLocked")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -222,6 +230,7 @@
                 ),
                 keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
             )
+            .flowName("isOnLockscreen")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -234,6 +243,7 @@
         combine(isOnLockscreen, isAnyExpanded) { isKeyguard, isAnyExpanded ->
                 isKeyguard && !isAnyExpanded
             }
+            .flowName("isOnLockscreenWithoutShade")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -269,6 +279,7 @@
         combine(isOnGlanceableHub, isAnyExpanded) { isGlanceableHub, isAnyExpanded ->
                 isGlanceableHub && !isAnyExpanded
             }
+            .flowName("isOnGlanceableHubWithoutShade")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -283,6 +294,7 @@
                 isAnyExpanded ->
                 isDreaming && !isAnyExpanded
             }
+            .flowName("isDreamingWithoutShade")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -340,6 +352,7 @@
                     }
                 }
             }
+            .flowName("shadeCollapseFadeIn")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
@@ -377,6 +390,7 @@
                     bounds.copy(top = top, isAnimated = animate)
                 }
             }
+            .flowName("bounds")
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Lazily,
@@ -390,20 +404,36 @@
      * notifications unless in splitshade.
      */
     private val alphaForShadeAndQsExpansion: Flow<Float> =
-        interactor.configurationBasedDimensions
-            .flatMapLatest { configurationBasedDimensions ->
-                combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
-                    shadeExpansion,
-                    qsExpansion ->
-                    if (shadeExpansion > 0f || qsExpansion > 0f) {
-                        if (configurationBasedDimensions.useSplitShade) {
-                            emit(1f)
-                        } else if (qsExpansion == 1f) {
-                            // Ensure HUNs will be visible in QS shade (at least while unlocked)
-                            emit(1f)
-                        } else {
-                            // Fade as QS shade expands
-                            emit(1f - qsExpansion)
+        if (DualShade.isEnabled) {
+                combineTransform(
+                    headsUpNotificationInteractor.get().isHeadsUpOrAnimatingAway,
+                    shadeInteractor.shadeExpansion,
+                    shadeInteractor.qsExpansion,
+                ) { isHeadsUpOrAnimatingAway, shadeExpansion, qsExpansion ->
+                    if (isHeadsUpOrAnimatingAway) {
+                        // Ensure HUNs will be visible in QS shade (at least while unlocked)
+                        emit(1f)
+                    } else if (shadeExpansion > 0f || qsExpansion > 0f) {
+                        // Fade out as QS shade expands
+                        emit(1f - qsExpansion)
+                    }
+                }
+            } else {
+                interactor.configurationBasedDimensions.flatMapLatest { configurationBasedDimensions
+                    ->
+                    combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
+                        shadeExpansion,
+                        qsExpansion ->
+                        if (shadeExpansion > 0f || qsExpansion > 0f) {
+                            if (configurationBasedDimensions.useSplitShade) {
+                                emit(1f)
+                            } else if (qsExpansion == 1f) {
+                                // Ensure HUNs will be visible in QS shade (at least while unlocked)
+                                emit(1f)
+                            } else {
+                                // Fade as QS shade expands
+                                emit(1f - qsExpansion)
+                            }
                         }
                     }
                 }
@@ -427,7 +457,7 @@
     private fun alphaForTransitions(viewState: ViewStateAccessor): Flow<Float> {
         return merge(
             keyguardInteractor.dismissAlpha.dumpWhileCollecting("keyguardInteractor.dismissAlpha"),
-            // All transition view models are mututally exclusive, and safe to merge
+            // All transition view models are mutually exclusive, and safe to merge
             bouncerToGoneNotificationAlpha(viewState),
             aodToGoneTransitionViewModel.notificationAlpha(viewState),
             aodToLockscreenTransitionViewModel.notificationAlpha,
@@ -474,6 +504,7 @@
         // flatMapLatest below, the last value gets emitted, to avoid the randomness of `merge`.
         val alphaForTransitionsAndShade =
             merge(alphaForTransitions(viewState), alphaForShadeAndQsExpansion)
+                .flowName("alphaForTransitionsAndShade")
                 .stateIn(
                     // Use view-level scope instead of ApplicationScope, to prevent collection that
                     // never stops
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index 5b37468..d1338ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -58,7 +58,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.kotlin.getOrNull
@@ -93,7 +93,7 @@
     @Main private val mainExecutor: DelayableExecutor,
     private val shadeControllerLazy: Lazy<ShadeController>,
     private val communalSceneInteractor: CommunalSceneInteractor,
-    private val statusBarWindowController: StatusBarWindowController,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
     private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
     private val shadeAnimationInteractor: ShadeAnimationInteractor,
     private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
@@ -562,7 +562,7 @@
         }
         val rootView = animationController.transitionContainer.rootView
         val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
-            statusBarWindowController.wrapAnimationControllerIfInStatusBar(
+            statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar(
                 rootView,
                 animationController
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 0474344..afa5a78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -33,7 +33,6 @@
 import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
 import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 
 import android.annotation.Nullable;
@@ -41,7 +40,6 @@
 import android.app.IWallpaperManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.TaskInfo;
@@ -130,7 +128,6 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.emergency.EmergencyGesture;
 import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
 import com.android.systemui.flags.FeatureFlags;
@@ -143,8 +140,6 @@
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
-import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.views.NavigationBarView;
 import com.android.systemui.notetask.NoteTaskController;
@@ -228,7 +223,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
 import com.android.systemui.util.DumpUtilsKt;
@@ -275,11 +270,6 @@
 @SysUISingleton
 public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
 
-    private static final String BANNER_ACTION_CANCEL =
-            "com.android.systemui.statusbar.banner_action_cancel";
-    private static final String BANNER_ACTION_SETUP =
-            "com.android.systemui.statusbar.banner_action_setup";
-
     private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003;
     // 1020-1040 reserved for BaseStatusBar
 
@@ -379,7 +369,7 @@
     @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final StatusBarInitializer mStatusBarInitializer;
-    private final StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
     private final StatusBarModeRepositoryStore mStatusBarModeRepository;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @VisibleForTesting
@@ -433,7 +423,6 @@
     private final NotificationsController mNotificationsController;
     private final StatusBarSignalPolicy mStatusBarSignalPolicy;
     private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
-    private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
 
     /** Controller for the Shade. */
     private final ShadeSurface mShadeSurface;
@@ -617,7 +606,7 @@
             LightBarController lightBarController,
             AutoHideController autoHideController,
             StatusBarInitializer statusBarInitializer,
-            StatusBarWindowController statusBarWindowController,
+            StatusBarWindowControllerStore statusBarWindowControllerStore,
             StatusBarWindowStateController statusBarWindowStateController,
             StatusBarModeRepositoryStore statusBarModeRepository,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -706,7 +695,6 @@
             WiredChargingRippleController wiredChargingRippleController,
             IDreamManager dreamManager,
             Lazy<CameraLauncher> cameraLauncherLazy,
-            Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy,
             LightRevealScrim lightRevealScrim,
             AlternateBouncerInteractor alternateBouncerInteractor,
             UserTracker userTracker,
@@ -723,7 +711,7 @@
         mLightBarController = lightBarController;
         mAutoHideController = autoHideController;
         mStatusBarInitializer = statusBarInitializer;
-        mStatusBarWindowController = statusBarWindowController;
+        mStatusBarWindowControllerStore = statusBarWindowControllerStore;
         mStatusBarModeRepository = statusBarModeRepository;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mPulseExpansionHandler = pulseExpansionHandler;
@@ -851,7 +839,6 @@
         mDeviceStateManager = deviceStateManager;
         wiredChargingRippleController.registerCallbacks();
 
-        mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
         mLightRevealScrim = lightRevealScrim;
 
         mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
@@ -963,12 +950,6 @@
             }
         }
 
-        IntentFilter internalFilter = new IntentFilter();
-        internalFilter.addAction(BANNER_ACTION_CANCEL);
-        internalFilter.addAction(BANNER_ACTION_SETUP);
-        mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
-                null, Context.RECEIVER_EXPORTED_UNAUDITED);
-
         if (mWallpaperSupported) {
             IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
                     ServiceManager.getService(Context.WALLPAPER_SERVICE));
@@ -1021,7 +1002,6 @@
                 mStatusBarKeyguardViewManager,
                 getNotificationShadeWindowViewController(),
                 mAmbientIndicationContainer);
-        updateLightRevealScrimVisibility();
 
         mConfigurationController.addCallback(mConfigurationListener);
 
@@ -1293,11 +1273,6 @@
         });
         mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
 
-        if (lightRevealMigration()) {
-            LightRevealScrimViewBinder.bind(
-                    mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
-        }
-
         mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
             Runnable updateOpaqueness = () -> {
                 mNotificationShadeWindowController.setLightRevealScrimOpaque(
@@ -1315,7 +1290,6 @@
         });
 
         mScreenOffAnimationController.initialize(this, mShadeSurface, mLightRevealScrim);
-        updateLightRevealScrimVisibility();
 
         if (!SceneContainerFlag.isEnabled()) {
             mShadeSurface.initDependencies(
@@ -1907,7 +1881,7 @@
         // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
         // StatusBarOrchestrator
         if (!StatusBarSimpleFragment.isEnabled()) {
-            mStatusBarWindowController.attach();
+            mStatusBarWindowControllerStore.getDefaultDisplay().attach();
         }
     }
 
@@ -2838,23 +2812,13 @@
         mScrimController.setExpansionAffectsAlpha(!unlocking);
 
         if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
-                        && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
-                        || mTransitionToFullShadeProgress > 0f)) {
-                    // Assume scrim state for shade is already correct and do nothing
-                } else {
-                    // Safeguard which prevents the scrim from being stuck in the wrong state
-                    mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
-                }
+            if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
+                    && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                    || mTransitionToFullShadeProgress > 0f)) {
+                // Assume scrim state for shade is already correct and do nothing
             } else {
-                if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
-                        && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
-                        || mTransitionToFullShadeProgress > 0f)) {
-                    mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
-                } else {
-                    mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED);
-                }
+                // Safeguard which prevents the scrim from being stuck in the wrong state
+                mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
             }
             // This will cancel the keyguardFadingAway animation if it is running. We need to do
             // this as otherwise it can remain pending and leave keyguard in a weird state.
@@ -2900,7 +2864,6 @@
         } else {
             mScrimController.legacyTransitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
-        updateLightRevealScrimVisibility();
 
         Trace.endSection();
     }
@@ -2948,29 +2911,6 @@
         return mDeviceInteractive;
     }
 
-    private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
-                NotificationManager noMan = (NotificationManager)
-                        mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-                noMan.cancel(com.android.internal.messages.nano.SystemMessageProto.SystemMessage.
-                        NOTE_HIDDEN_NOTIFICATIONS);
-
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
-                if (BANNER_ACTION_SETUP.equals(action)) {
-                    mShadeController.animateCollapseShadeForced();
-                    mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
-                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
-                    );
-                }
-            }
-        }
-    };
-
     @Override
     public void handleExternalShadeWindowTouch(MotionEvent event) {
         getNotificationShadeWindowViewController().handleExternalTouch(event);
@@ -3045,17 +2985,6 @@
         return mStatusBarModeRepository.getDefaultDisplay().isTransientShown().getValue();
     }
 
-    private void updateLightRevealScrimVisibility() {
-        if (mLightRevealScrim == null) {
-            // status bar may not be inflated yet
-            return;
-        }
-
-        if (!lightRevealMigration()) {
-            mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
-        }
-    }
-
     private final KeyguardUpdateMonitorCallback mUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
                 @Override
@@ -3204,12 +3133,8 @@
                 public void onDozeAmountChanged(float linear, float eased) {
                     if (!lightRevealMigration()
                             && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
-                        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                            // If wakeAndUnlocking, this is handled in AuthRippleInteractor
-                            if (!mBiometricUnlockController.isWakeAndUnlock()) {
-                                mLightRevealScrim.setRevealAmount(1f - linear);
-                            }
-                        } else {
+                        // If wakeAndUnlocking, this is handled in AuthRippleInteractor
+                        if (!mBiometricUnlockController.isWakeAndUnlock()) {
                             mLightRevealScrim.setRevealAmount(1f - linear);
                         }
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index bb5aa23..a8c823c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -20,6 +20,7 @@
 import android.graphics.Rect
 import android.os.LocaleList
 import android.view.View.LAYOUT_DIRECTION_RTL
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import dagger.assisted.Assisted
@@ -30,7 +31,7 @@
 @AssistedInject
 constructor(
     @Assisted private val context: Context,
-) : ConfigurationController {
+) : ConfigurationController, StatusBarConfigurationController {
 
     private val listeners: MutableList<ConfigurationListener> = ArrayList()
     private val lastConfig = Configuration()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
index db237e8..5273702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
@@ -17,7 +17,10 @@
 
 import android.content.Context
 import android.hardware.devicestate.DeviceState
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback
+import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags
 import com.android.internal.R
 
 /**
@@ -47,12 +50,21 @@
     private var wasFolded: Boolean? = null
 
     override fun onDeviceStateChanged(state: DeviceState) {
-        val isFolded = foldedDeviceStates.contains(state.identifier)
+        val isFolded: Boolean
+        val willGoToSleep: Boolean
+
+        if (DeviceStateManagerFlags.deviceStatePropertyMigration()) {
+            isFolded = state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+            willGoToSleep = state.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP)
+        } else {
+            isFolded = foldedDeviceStates.contains(state.identifier)
+            willGoToSleep = goToSleepDeviceStates.contains(state.identifier)
+        }
+
         if (wasFolded == isFolded) {
             return
         }
         wasFolded = isFolded
-        val willGoToSleep = goToSleepDeviceStates.contains(state.identifier)
         listener.onFoldStateChanged(isFolded, willGoToSleep)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 8f94c06..d0f4b6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_FRAME_VIEW;
+import static com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.OPERATOR_NAME_FRAME_VIEW;
 
 import android.graphics.Rect;
 import android.util.MathUtils;
@@ -44,7 +44,7 @@
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -63,7 +63,7 @@
  * Controls the appearance of heads up notifications in the icon area and the header itself.
  * It also controls the roundness of the heads up notifications and the pulsing notifications.
  */
-@StatusBarFragmentScope
+@HomeStatusBarScope
 public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
         implements OnHeadsUpChangedListener,
         DarkIconDispatcher.DarkReceiver,
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 316e1f1..a34ac2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -22,7 +22,7 @@
 import android.hardware.biometrics.BiometricSourceType
 import android.provider.Settings
 import com.android.app.tracing.ListenersTracing.forEachTraced
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 178c318..6a77988 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -182,14 +182,8 @@
         mCarrierLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                 getResources().getDimensionPixelSize(
                         com.android.internal.R.dimen.text_size_small_material));
-        lp = (MarginLayoutParams) mCarrierLabel.getLayoutParams();
+        updateCarrierLabelMargin();
 
-        int marginStart = calculateMargin(
-                getResources().getDimensionPixelSize(R.dimen.keyguard_carrier_text_margin),
-                mPadding.left);
-        lp.setMarginStart(marginStart);
-
-        mCarrierLabel.setLayoutParams(lp);
         updateKeyguardStatusBarHeight();
     }
 
@@ -203,6 +197,15 @@
         setLayoutParams(lp);
     }
 
+    private void updateCarrierLabelMargin() {
+        MarginLayoutParams lp = (MarginLayoutParams) mCarrierLabel.getLayoutParams();
+        int marginStart = calculateMargin(
+                getResources().getDimensionPixelSize(R.dimen.keyguard_carrier_text_margin),
+                mPadding.left);
+        lp.setMarginStart(marginStart);
+        mCarrierLabel.setLayoutParams(lp);
+    }
+
     void loadDimens() {
         Resources res = getResources();
         mSystemIconsSwitcherHiddenExpandedMargin = res.getDimensionPixelSize(
@@ -334,6 +337,7 @@
 
         RelativeLayout.LayoutParams lp = (LayoutParams) mCarrierLabel.getLayoutParams();
         lp.addRule(RelativeLayout.START_OF, R.id.status_icon_area);
+        updateCarrierLabelMargin();
 
         lp = (LayoutParams) mStatusIconArea.getLayoutParams();
         lp.removeRule(RelativeLayout.RIGHT_OF);
@@ -366,6 +370,7 @@
 
         lp = (LayoutParams) mCarrierLabel.getLayoutParams();
         lp.addRule(RelativeLayout.START_OF, R.id.cutout_space_view);
+        updateCarrierLabelMargin();
 
         lp = (LayoutParams) mStatusIconArea.getLayoutParams();
         lp.addRule(RelativeLayout.RIGHT_OF, R.id.cutout_space_view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index 4604233..1cca3ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -57,7 +57,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.kotlin.getOrNull
 import dagger.Lazy
@@ -85,7 +85,7 @@
     private val context: Context,
     @DisplayId private val displayId: Int,
     private val lockScreenUserManager: NotificationLockscreenUserManager,
-    private val statusBarWindowController: StatusBarWindowController,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
     private val wakefulnessLifecycle: WakefulnessLifecycle,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val deviceProvisionedController: DeviceProvisionedController,
@@ -525,7 +525,7 @@
         }
         val rootView = animationController.transitionContainer.rootView
         val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
-            statusBarWindowController.wrapAnimationControllerIfInStatusBar(
+            statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar(
                 rootView,
                 animationController
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java
index 7c871e1..5acc3a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java
@@ -18,7 +18,7 @@
 
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 
-import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.LIGHTS_OUT_NOTIF_VIEW;
+import static com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.LIGHTS_OUT_NOTIF_VIEW;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -37,7 +37,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope;
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
@@ -51,7 +51,7 @@
  * This controller shows and hides the notification dot in the status bar to indicate
  * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}.
  */
-@StatusBarFragmentScope
+@HomeStatusBarScope
 public class LegacyLightsOutNotifController extends ViewController<View> {
     private final CommandQueue mCommandQueue;
     private final NotifLiveDataStore mNotifDataStore;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index d6716a0..e7d9717 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -40,7 +40,7 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
 import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
 import com.android.systemui.util.leak.RotationUtils;
@@ -49,7 +49,7 @@
 
 public class PhoneStatusBarView extends FrameLayout {
     private static final String TAG = "PhoneStatusBarView";
-    private final StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
 
     private int mRotationOrientation = -1;
     @Nullable
@@ -75,7 +75,7 @@
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
+        mStatusBarWindowControllerStore = Dependency.get(StatusBarWindowControllerStore.class);
     }
 
     void setTouchEventHandler(Gefingerpoken handler) {
@@ -326,7 +326,7 @@
         if (Flags.statusBarStopUpdatingWindowHeight()) {
             return;
         }
-        mStatusBarWindowController.refreshStatusBarHeight();
+        mStatusBarWindowControllerStore.getDefaultDisplay().refreshStatusBarHeight();
     }
 
     interface HasCornerCutoutFetcher {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index bef552c..ff7c143 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -151,7 +151,11 @@
 
         startSideContainer = mView.requireViewById(R.id.status_bar_start_side_content)
         startSideContainer.setOnHoverListener(
-            statusOverlayHoverListenerFactory.createDarkAwareListener(startSideContainer)
+            statusOverlayHoverListenerFactory.createDarkAwareListener(
+                startSideContainer,
+                topHoverMargin = 6,
+                bottomHoverMargin = 6,
+            )
         )
         startSideContainer.setOnTouchListener(iconsOnTouchListener)
     }
@@ -210,7 +214,7 @@
                 event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL
             centralSurfaces.setInteracting(
                 WINDOW_STATUS_BAR,
-                !upOrCancel || shadeController.isExpandedVisible
+                !upOrCancel || shadeController.isExpandedVisible,
             )
         }
     }
@@ -247,7 +251,7 @@
                         String.format(
                             "onTouchForwardedFromStatusBar: panel disabled, " +
                                 "ignoring touch at (${event.x.toInt()},${event.y.toInt()})"
-                        )
+                        ),
                     )
                 }
                 return false
@@ -266,7 +270,7 @@
                 if (!shadeViewController.isViewEnabled) {
                     shadeLogger.logMotionEvent(
                         event,
-                        "onTouchForwardedFromStatusBar: panel view disabled"
+                        "onTouchForwardedFromStatusBar: panel view disabled",
                     )
                     return true
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 069c624..dc4d66d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,7 +29,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
-import android.app.AlarmManager;
 import android.graphics.Color;
 import android.os.Handler;
 import android.os.Trace;
@@ -53,8 +52,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.settingslib.Utils;
-import com.android.systemui.CoreStartable;
-import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
@@ -81,11 +78,9 @@
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
-import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
 
 import kotlinx.coroutines.CoroutineDispatcher;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
@@ -104,8 +99,7 @@
  */
 @SysUISingleton
 @ExperimentalCoroutinesApi
-public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable,
-        CoreStartable {
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable {
 
     static final String TAG = "ScrimController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -217,7 +211,6 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DozeParameters mDozeParameters;
     private final DockManager mDockManager;
-    private final AlarmTimeout mTimeTicker;
     private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
     private final Handler mHandler;
     private final Executor mMainExecutor;
@@ -232,7 +225,6 @@
 
     private float mAdditionalScrimBehindAlphaKeyguard = 0f;
     // Combined scrim behind keyguard alpha of default scrim + additional scrim
-    // (if wallpaper dimming is applied).
     private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
     private final float mDefaultScrimAlpha;
 
@@ -261,7 +253,6 @@
     private int mBehindTint;
     private int mNotificationsTint;
 
-    private boolean mWallpaperVisibilityTimedOut;
     private int mScrimsVisibility;
     private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
     private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
@@ -269,7 +260,6 @@
     private boolean mBlankScreen;
     private boolean mScreenBlankingCallbackCalled;
     private Callback mCallback;
-    private boolean mWallpaperSupportsAmbientMode;
     private boolean mScreenOn;
     private boolean mTransparentScrimBackground;
 
@@ -282,7 +272,6 @@
     private boolean mKeyguardOccluded;
 
     private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
-    private final WallpaperRepository mWallpaperRepository;
     private CoroutineDispatcher mMainDispatcher;
     private boolean mIsBouncerToGoneTransitionRunning = false;
     private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@@ -332,7 +321,6 @@
     public ScrimController(
             LightBarController lightBarController,
             DozeParameters dozeParameters,
-            AlarmManager alarmManager,
             KeyguardStateController keyguardStateController,
             DelayedWakeLock.Factory delayedWakeLockFactory,
             Handler handler,
@@ -348,7 +336,6 @@
             AlternateBouncerToGoneTransitionViewModel alternateBouncerToGoneTransitionViewModel,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
             KeyguardInteractor keyguardInteractor,
-            WallpaperRepository wallpaperRepository,
             @Main CoroutineDispatcher mainDispatcher,
             LargeScreenShadeInterpolator largeScreenShadeInterpolator) {
         mScrimStateListener = lightBarController::setScrimState;
@@ -363,8 +350,6 @@
         mMainExecutor = mainExecutor;
         mJavaAdapter = javaAdapter;
         mScreenOffAnimationController = screenOffAnimationController;
-        mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
-                "hide_aod_wallpaper", mHandler);
         mWakeLock = delayedWakeLockFactory.create("Scrims");
         // Scrim alpha is initially set to the value on the resource but might be changed
         // to make sure that text on top of it is legible.
@@ -395,17 +380,9 @@
         mAlternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mKeyguardInteractor = keyguardInteractor;
-        mWallpaperRepository = wallpaperRepository;
         mMainDispatcher = mainDispatcher;
     }
 
-    @Override
-    public void start() {
-        mJavaAdapter.alwaysCollectFlow(
-                mWallpaperRepository.getWallpaperSupportsAmbientMode(),
-                this::setWallpaperSupportsAmbientMode);
-    }
-
     /**
      * Attach the controller to the supplied views.
      */
@@ -627,18 +604,6 @@
             holdWakeLock();
         }
 
-        // AOD wallpapers should fade away after a while.
-        // Docking pulses may take a long time, wallpapers should also fade away after a while.
-        mWallpaperVisibilityTimedOut = false;
-        if (shouldFadeAwayWallpaper()) {
-            DejankUtils.postAfterTraversal(() -> {
-                mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
-                        AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
-            });
-        } else {
-            DejankUtils.postAfterTraversal(mTimeTicker::cancel);
-        }
-
         if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
             mAnimationDelay = CentralSurfaces.FADE_KEYGUARD_START_DELAY;
             scheduleUpdate();
@@ -657,19 +622,6 @@
         dispatchBackScrimState(mScrimBehind.getViewAlpha());
     }
 
-    private boolean shouldFadeAwayWallpaper() {
-        if (!mWallpaperSupportsAmbientMode) {
-            return false;
-        }
-
-        if (mState == ScrimState.AOD
-                && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) {
-            return true;
-        }
-
-        return false;
-    }
-
     public ScrimState getState() {
         return mState;
     }
@@ -728,19 +680,6 @@
         }
     }
 
-    @VisibleForTesting
-    protected void onHideWallpaperTimeout() {
-        if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
-            return;
-        }
-
-        holdWakeLock();
-        mWallpaperVisibilityTimedOut = true;
-        mAnimateChange = true;
-        mAnimationDuration = mDozeParameters.getWallpaperFadeOutDuration();
-        scheduleUpdate();
-    }
-
     private void holdWakeLock() {
         if (!mWakeLockHeld) {
             if (mWakeLock != null) {
@@ -1171,16 +1110,6 @@
         setOrAdaptCurrentAnimation(mNotificationsScrim);
         setOrAdaptCurrentAnimation(mScrimInFront);
         dispatchBackScrimState(mScrimBehind.getViewAlpha());
-
-        // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
-        // and docking.
-        if (mWallpaperVisibilityTimedOut) {
-            mWallpaperVisibilityTimedOut = false;
-            DejankUtils.postAfterTraversal(() -> {
-                mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
-                        AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
-            });
-        }
     }
 
     /**
@@ -1259,15 +1188,11 @@
             dispatchBackScrimState(mScrimBehind.getViewAlpha());
         }
 
-        // We want to override the back scrim opacity for the AOD state
-        // when it's time to fade the wallpaper away.
-        boolean aodWallpaperTimeout = (mState == ScrimState.AOD || mState == ScrimState.PULSING)
-                && mWallpaperVisibilityTimedOut;
         // We also want to hide FLAG_SHOW_WHEN_LOCKED activities under the scrim.
         boolean hideFlagShowWhenLockedActivities =
                 (mState == ScrimState.PULSING || mState == ScrimState.AOD)
                 && mKeyguardOccluded;
-        if (aodWallpaperTimeout || hideFlagShowWhenLockedActivities) {
+        if (hideFlagShowWhenLockedActivities) {
             mBehindAlpha = 1;
         }
         // Prevent notification scrim flicker when transitioning away from keyguard.
@@ -1668,17 +1593,6 @@
         pw.println(mPanelExpansionFraction);
         pw.print("  mExpansionAffectsAlpha=");
         pw.println(mExpansionAffectsAlpha);
-
-        pw.print("  mState.getMaxLightRevealScrimAlpha=");
-        pw.println(mState.getMaxLightRevealScrimAlpha());
-    }
-
-    private void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
-        mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
-        ScrimState[] states = ScrimState.values();
-        for (int i = 0; i < states.length; i++) {
-            states[i].setWallpaperSupportsAmbientMode(wallpaperSupportsAmbientMode);
-        }
     }
 
     /**
@@ -1715,26 +1629,6 @@
         updateScrims();
     }
 
-    public void setHasBackdrop(boolean hasBackdrop) {
-        for (ScrimState state : ScrimState.values()) {
-            state.setHasBackdrop(hasBackdrop);
-        }
-
-        // Backdrop event may arrive after state was already applied,
-        // in this case, back-scrim needs to be re-evaluated
-        if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
-            float newBehindAlpha = mState.getBehindAlpha();
-            if (isNaN(newBehindAlpha)) {
-                throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
-                        + ", back: " + mBehindAlpha);
-            }
-            if (mBehindAlpha != newBehindAlpha) {
-                mBehindAlpha = newBehindAlpha;
-                updateScrims();
-            }
-        }
-    }
-
     private void setKeyguardFadingAway(boolean fadingAway, long duration) {
         for (ScrimState state : ScrimState.values()) {
             state.setKeyguardFadingAway(fadingAway, duration);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index fbba3dc..198859a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -209,11 +209,6 @@
         }
 
         @Override
-        public float getMaxLightRevealScrimAlpha() {
-            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
-        }
-
-        @Override
         public boolean isLowPowerState() {
             return true;
         }
@@ -237,11 +232,6 @@
             mAnimationDuration = mWakeLockScreenSensorActive
                     ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION;
         }
-        @Override
-        public float getMaxLightRevealScrimAlpha() {
-            return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA
-                    : AOD.getMaxLightRevealScrimAlpha();
-        }
     },
 
     /**
@@ -368,8 +358,6 @@
     DozeParameters mDozeParameters;
     DockManager mDockManager;
     boolean mDisplayRequiresBlanking;
-    boolean mWallpaperSupportsAmbientMode;
-    boolean mHasBackdrop;
     boolean mLaunchingAffordanceWithPreview;
     boolean mOccludeAnimationPlaying;
     boolean mWakeLockScreenSensorActive;
@@ -408,10 +396,6 @@
         return mBehindAlpha;
     }
 
-    public float getMaxLightRevealScrimAlpha() {
-        return 1f;
-    }
-
     public float getNotifAlpha() {
         return mNotifAlpha;
     }
@@ -473,10 +457,6 @@
         mSurfaceColor = surfaceColor;
     }
 
-    public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
-        mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
-    }
-
     public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
         mLaunchingAffordanceWithPreview = launchingAffordanceWithPreview;
     }
@@ -489,10 +469,6 @@
         return false;
     }
 
-    public void setHasBackdrop(boolean hasBackdrop) {
-        mHasBackdrop = hasBackdrop;
-    }
-
     public void setWakeLockScreenSensorActive(boolean active) {
         mWakeLockScreenSensorActive = active;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
index 00b08f0..3ac0bac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
@@ -18,10 +18,10 @@
 
 import android.graphics.Rect
 import android.view.View
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.END_SIDE_CONTENT
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.START_SIDE_CONTENT
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.END_SIDE_CONTENT
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.START_SIDE_CONTENT
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope
 import com.android.systemui.util.ListenerSet
 import com.android.systemui.util.boundsOnScreen
 import javax.inject.Inject
@@ -33,13 +33,13 @@
  * This is distinct from [StatusBarContentInsetsProvider], which provides the bounds of full status
  * bar after accounting for system insets.
  */
-@StatusBarFragmentScope
+@HomeStatusBarScope
 class StatusBarBoundsProvider
 @Inject
 constructor(
     @Named(START_SIDE_CONTENT) private val startSideContent: View,
     @Named(END_SIDE_CONTENT) private val endSideContent: View,
-) : StatusBarFragmentComponent.Startable {
+) : HomeStatusBarComponent.Startable {
 
     interface BoundsChangeListener {
         fun onStatusBarBoundsChanged(bounds: BoundsPair)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 613efaa..c6f6bd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -34,10 +34,10 @@
 import com.android.systemui.StatusBarInsetsCommand
 import com.android.systemui.SysUICutoutInformation
 import com.android.systemui.SysUICutoutProvider
-import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl.CacheKey
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
@@ -47,9 +47,11 @@
 import com.android.systemui.util.leak.RotationUtils.Rotation
 import com.android.systemui.util.leak.RotationUtils.getExactRotation
 import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import java.io.PrintWriter
 import java.lang.Math.max
-import javax.inject.Inject
 
 /**
  * Encapsulates logic that can solve for the left/right insets required for the status bar contents.
@@ -64,19 +66,87 @@
  *
  * NOTE: This class is not threadsafe
  */
-@SysUISingleton
-class StatusBarContentInsetsProvider
-@Inject
+interface StatusBarContentInsetsProvider :
+    CallbackController<StatusBarContentInsetsChangedListener> {
+
+    /**
+     * Some views may need to care about whether or not the current top display cutout is located in
+     * the corner rather than somewhere in the center. In the case of a corner cutout, the status
+     * bar area is contiguous.
+     */
+    fun currentRotationHasCornerCutout(): Boolean
+
+    /**
+     * Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy
+     * dot in the coordinates relative to the given rotation.
+     *
+     * @param rotation the rotation for which the bounds are required. This is an absolute value
+     *   (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+     *   which this method is called)
+     */
+    fun getBoundingRectForPrivacyChipForRotation(
+        @Rotation rotation: Int,
+        displayCutout: DisplayCutout?,
+    ): Rect
+
+    /**
+     * Calculate the distance from the left, right and top edges of the screen to the status bar
+     * content area. This differs from the content area rects in that these values can be used
+     * directly as padding.
+     *
+     * @param rotation the target rotation for which to calculate insets
+     */
+    fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets
+
+    /**
+     * Calculate the insets for the status bar content in the device's current rotation
+     *
+     * @see getStatusBarContentAreaForRotation
+     */
+    fun getStatusBarContentInsetsForCurrentRotation(): Insets
+
+    /**
+     * Calculates the area of the status bar contents invariant of the current device rotation, in
+     * the target rotation's coordinates
+     *
+     * @param rotation the rotation for which the bounds are required. This is an absolute value
+     *   (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+     *   which this method is called)
+     */
+    fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect
+
+    /** Get the status bar content area for the given rotation, in absolute bounds */
+    fun getStatusBarContentAreaForCurrentRotation(): Rect
+
+    fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int
+
+    interface Factory {
+        fun create(
+            context: Context,
+            configurationController: ConfigurationController,
+            sysUICutoutProvider: SysUICutoutProvider,
+        ): StatusBarContentInsetsProvider
+    }
+}
+
+class StatusBarContentInsetsProviderImpl
+@AssistedInject
 constructor(
-    val context: Context,
-    val configurationController: ConfigurationController,
+    @Assisted val context: Context,
+    @Assisted val configurationController: ConfigurationController,
     val dumpManager: DumpManager,
     val commandRegistry: CommandRegistry,
-    val sysUICutoutProvider: SysUICutoutProvider,
-) :
-    CallbackController<StatusBarContentInsetsChangedListener>,
-    ConfigurationController.ConfigurationListener,
-    Dumpable {
+    @Assisted val sysUICutoutProvider: SysUICutoutProvider,
+) : StatusBarContentInsetsProvider, ConfigurationController.ConfigurationListener, Dumpable {
+
+    @AssistedFactory
+    interface Factory : StatusBarContentInsetsProvider.Factory {
+        override fun create(
+            context: Context,
+            configurationController: ConfigurationController,
+            sysUICutoutProvider: SysUICutoutProvider,
+        ): StatusBarContentInsetsProviderImpl
+    }
 
     // Limit cache size as potentially we may connect large number of displays
     // (e.g. network displays)
@@ -95,7 +165,7 @@
                 object : StatusBarInsetsCommand.Callback {
                     override fun onExecute(
                         command: StatusBarInsetsCommand,
-                        printWriter: PrintWriter
+                        printWriter: PrintWriter,
                     ) {
                         executeCommand(command, printWriter)
                     }
@@ -133,12 +203,7 @@
         listeners.forEach { it.onStatusBarContentInsetsChanged() }
     }
 
-    /**
-     * Some views may need to care about whether or not the current top display cutout is located in
-     * the corner rather than somewhere in the center. In the case of a corner cutout, the status
-     * bar area is contiguous.
-     */
-    fun currentRotationHasCornerCutout(): Boolean {
+    override fun currentRotationHasCornerCutout(): Boolean {
         val cutout = checkNotNull(context.display).cutout ?: return false
         val topBounds = cutout.boundingRectTop
 
@@ -148,17 +213,9 @@
         return topBounds.left <= 0 || topBounds.right >= point.x
     }
 
-    /**
-     * Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy
-     * dot in the coordinates relative to the given rotation.
-     *
-     * @param rotation the rotation for which the bounds are required. This is an absolute value
-     *   (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
-     *   which this method is called)
-     */
-    fun getBoundingRectForPrivacyChipForRotation(
+    override fun getBoundingRectForPrivacyChipForRotation(
         @Rotation rotation: Int,
-        displayCutout: DisplayCutout?
+        displayCutout: DisplayCutout?,
     ): Rect {
         val key = getCacheKey(rotation, displayCutout)
         var insets = insetsCache[key]
@@ -176,14 +233,7 @@
         return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
     }
 
-    /**
-     * Calculate the distance from the left, right and top edges of the screen to the status bar
-     * content area. This differs from the content area rects in that these values can be used
-     * directly as padding.
-     *
-     * @param rotation the target rotation for which to calculate insets
-     */
-    fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
+    override fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
         traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") {
             val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
             val displayCutout = sysUICutout?.cutout
@@ -202,31 +252,17 @@
                         rotation,
                         sysUICutout,
                         getResourcesForRotation(rotation, context),
-                        key
+                        key,
                     )
 
             Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0)
         }
 
-    /**
-     * Calculate the insets for the status bar content in the device's current rotation
-     *
-     * @see getStatusBarContentAreaForRotation
-     */
-    fun getStatusBarContentInsetsForCurrentRotation(): Insets {
+    override fun getStatusBarContentInsetsForCurrentRotation(): Insets {
         return getStatusBarContentInsetsForRotation(getExactRotation(context))
     }
 
-    /**
-     * Calculates the area of the status bar contents invariant of the current device rotation, in
-     * the target rotation's coordinates
-     *
-     * @param rotation the rotation for which the bounds are required. This is an absolute value
-     *   (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
-     *   which this method is called)
-     */
-    @JvmOverloads
-    fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
+    override fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
         val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
         val displayCutout = sysUICutout?.cutout
         val key = getCacheKey(rotation, displayCutout)
@@ -235,12 +271,11 @@
                 rotation,
                 sysUICutout,
                 getResourcesForRotation(rotation, context),
-                key
+                key,
             )
     }
 
-    /** Get the status bar content area for the given rotation, in absolute bounds */
-    fun getStatusBarContentAreaForCurrentRotation(): Rect {
+    override fun getStatusBarContentAreaForCurrentRotation(): Rect {
         val rotation = getExactRotation(context)
         return getStatusBarContentAreaForRotation(rotation)
     }
@@ -249,7 +284,7 @@
         @Rotation targetRotation: Int,
         sysUICutout: SysUICutoutInformation?,
         rotatedResources: Resources,
-        key: CacheKey
+        key: CacheKey,
     ): Rect {
         return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources).also {
             insetsCache.put(key, it)
@@ -259,7 +294,7 @@
     private fun getCalculatedAreaForRotation(
         sysUICutout: SysUICutoutInformation?,
         @Rotation targetRotation: Int,
-        rotatedResources: Resources
+        rotatedResources: Resources,
     ): Rect {
         val currentRotation = getExactRotation(context)
 
@@ -299,7 +334,7 @@
             configurationController.isLayoutRtl,
             dotWidth,
             bottomAlignedMargin,
-            statusBarContentHeight
+            statusBarContentHeight,
         )
     }
 
@@ -349,7 +384,7 @@
         return resources.getDimensionPixelSize(dimenRes)
     }
 
-    fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int {
+    override fun getStatusBarPaddingTop(@Rotation rotation: Int?): Int {
         val res = rotation?.let { it -> getResourcesForRotation(it, context) } ?: context.resources
         return res.getDimensionPixelSize(R.dimen.status_bar_padding_top)
     }
@@ -364,13 +399,13 @@
         CacheKey(
             rotation = rotation,
             displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds),
-            displayCutout = displayCutout
+            displayCutout = displayCutout,
         )
 
     private data class CacheKey(
         @Rotation val rotation: Int,
         val displaySize: Rect,
-        val displayCutout: DisplayCutout?
+        val displayCutout: DisplayCutout?,
     )
 }
 
@@ -395,21 +430,21 @@
     contentRect: Rect,
     dotWidth: Int,
     chipWidth: Int,
-    isRtl: Boolean
+    isRtl: Boolean,
 ): Rect {
     return if (isRtl) {
         Rect(
             contentRect.left - dotWidth,
             contentRect.top,
             contentRect.left + chipWidth,
-            contentRect.bottom
+            contentRect.bottom,
         )
     } else {
         Rect(
             contentRect.right - chipWidth,
             contentRect.top,
             contentRect.right + dotWidth,
-            contentRect.bottom
+            contentRect.bottom,
         )
     }
 }
@@ -443,7 +478,7 @@
     isRtl: Boolean,
     dotWidth: Int,
     bottomAlignedMargin: Int,
-    statusBarContentHeight: Int
+    statusBarContentHeight: Int,
 ): Rect {
     /*
     TODO: Check if this is ever used for devices with no rounded corners
@@ -467,7 +502,7 @@
         targetRotation,
         currentRotation,
         bottomAlignedMargin,
-        statusBarContentHeight
+        statusBarContentHeight,
     )
 }
 
@@ -503,7 +538,7 @@
     @Rotation targetRotation: Int,
     @Rotation currentRotation: Int,
     bottomAlignedMargin: Int,
-    statusBarContentHeight: Int
+    statusBarContentHeight: Int,
 ): Rect {
     val insetTop = getInsetTop(bottomAlignedMargin, statusBarContentHeight, sbHeight)
 
@@ -597,7 +632,7 @@
 private fun getInsetTop(
     bottomAlignedMargin: Int,
     statusBarContentHeight: Int,
-    statusBarHeight: Int
+    statusBarHeight: Int,
 ): Int {
     val bottomAlignmentEnabled = bottomAlignedMargin >= 0
     if (!bottomAlignmentEnabled) {
@@ -610,7 +645,7 @@
 private fun sbRect(
     @Rotation relativeRotation: Int,
     sbHeight: Int,
-    displaySize: Pair<Int, Int>
+    displaySize: Pair<Int, Int>,
 ): Rect {
     val w = displaySize.first
     val h = displaySize.second
@@ -626,7 +661,7 @@
     sbRect: Rect,
     cutoutRect: Rect,
     currentWidth: Int,
-    currentHeight: Int
+    currentHeight: Int,
 ): Boolean {
     if (currentWidth < currentHeight) {
         // Check top/bottom edges by extending the width of the display cutout rect and checking
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
index 25b8bfe0..1afe416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
@@ -21,7 +21,7 @@
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_WARNING;
-import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_VIEW;
+import static com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.OPERATOR_NAME_VIEW;
 
 import android.annotation.NonNull;
 import android.os.Bundle;
@@ -32,7 +32,7 @@
 import com.android.systemui.demomode.DemoModeCommandReceiver;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.navigationbar.NavigationBarController;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.util.ViewController;
 
@@ -48,7 +48,7 @@
  * This class extends ViewController not because it controls a specific view, but because we want it
  * to get torn down and re-created in line with the view's lifecycle.
  */
-@StatusBarFragmentScope
+@HomeStatusBarScope
 public class StatusBarDemoMode extends ViewController<View> implements DemoMode {
     private final Clock mClockView;
     private final View mOperatorNameView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 8f2d4f9..7145ffe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -28,7 +28,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 
 import javax.inject.Inject;
 
@@ -38,7 +38,7 @@
 @SysUISingleton
 public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, CoreStartable {
     private final NotificationShadeWindowController mNotificationShadeWindowController;
-    private final StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
     private final ShadeViewController mShadeViewController;
     private final PanelExpansionInteractor mPanelExpansionInteractor;
     private final NotificationStackScrollLayoutController mNsslController;
@@ -50,7 +50,7 @@
     @Inject
     StatusBarHeadsUpChangeListener(
             NotificationShadeWindowController notificationShadeWindowController,
-            StatusBarWindowController statusBarWindowController,
+            StatusBarWindowControllerStore statusBarWindowControllerStore,
             ShadeViewController shadeViewController,
             PanelExpansionInteractor panelExpansionInteractor,
             NotificationStackScrollLayoutController nsslController,
@@ -59,7 +59,7 @@
             StatusBarStateController statusBarStateController,
             NotificationRemoteInputManager notificationRemoteInputManager) {
         mNotificationShadeWindowController = notificationShadeWindowController;
-        mStatusBarWindowController = statusBarWindowController;
+        mStatusBarWindowControllerStore = statusBarWindowControllerStore;
         mShadeViewController = shadeViewController;
         mPanelExpansionInteractor = panelExpansionInteractor;
         mNsslController = nsslController;
@@ -78,7 +78,7 @@
     public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
         if (inPinnedMode) {
             mNotificationShadeWindowController.setHeadsUpShowing(true);
-            mStatusBarWindowController.setForceStatusBarVisible(true);
+            mStatusBarWindowControllerStore.getDefaultDisplay().setForceStatusBarVisible(true);
             if (mPanelExpansionInteractor.isFullyCollapsed()) {
                 mShadeViewController.updateTouchableRegion();
             }
@@ -93,7 +93,9 @@
                 // open artificially.
                 mNotificationShadeWindowController.setHeadsUpShowing(false);
                 if (bypassKeyguard) {
-                    mStatusBarWindowController.setForceStatusBarVisible(false);
+                    mStatusBarWindowControllerStore
+                            .getDefaultDisplay()
+                            .setForceStatusBarVisible(false);
                 }
             } else {
                 // we need to keep the panel open artificially, let's wait until the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 479ffb7..74c6e72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -47,7 +47,6 @@
 
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.keyguard.KeyguardMessageAreaController;
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -68,7 +67,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -77,9 +75,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.model.DismissAction;
-import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.KeyguardDone;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.TaskbarDelegate;
@@ -165,7 +161,6 @@
     private final DreamOverlayStateController mDreamOverlayStateController;
     @Nullable
     private final FoldAodAnimationController mFoldAodAnimationController;
-    KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
     private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -463,11 +458,6 @@
             onPanelExpansionChanged(currentState);
         }
         mNotificationContainer = notificationContainer;
-        if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-            mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
-                    centralSurfaces.getKeyguardMessageArea());
-        }
-
         mCentralSurfacesRegistered = true;
 
         registerListeners();
@@ -518,23 +508,11 @@
             mListenForCanShowAlternateBouncer.cancel(null);
         }
         mListenForCanShowAlternateBouncer = null;
-        if (!DeviceEntryUdfpsRefactor.isEnabled()) {
-            mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow(
-                    mKeyguardTransitionInteractor
-                            .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)),
-                    this::consumeFromAlternateBouncerTransitionSteps
-            );
-
-            mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow(
-                    mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(),
-                    this::consumeKeyguardAuthenticatedBiometricsHandled
-            );
-        } else {
-            mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
-                    mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
-                    this::consumeCanShowAlternateBouncer
-            );
-        }
+        // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
+        mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
+                mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
+                this::consumeCanShowAlternateBouncer
+        );
 
         if (KeyguardWmStateRefactor.isEnabled()) {
             // Show the keyguard views whenever we've told WM that the lockscreen is visible.
@@ -578,8 +556,17 @@
     }
 
     private void consumeCanShowAlternateBouncer(boolean canShow) {
-        // do nothing, we only are registering for the flow to ensure that there's at least
-        // one subscriber that will update AlternateBouncerInteractor.canShowAlternateBouncer.value
+        // Hack: this is required to fix issues where
+        // KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never
+        // reset. This is caused by usages of show()/forceShow() that only read this flow to set the
+        // alternate bouncer visible state, if there is a race condition between when that flow
+        // changes to false and when the read happens, the flow will be set to an incorrect value
+        // and not reset on time.
+        if (!canShow) {
+            Log.d(TAG, "canShowAlternateBouncer turned false, maybe try hiding the alternate "
+                    + "bouncer if it is already visible");
+            mAlternateBouncerInteractor.maybeHide();
+        }
     }
 
     /** Register a callback, to be invoked by the Predictive Back system. */
@@ -782,21 +769,12 @@
             return;
         }
 
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
-                Log.d(TAG, "showBouncer:alternateBouncer.forceShow()");
-                mAlternateBouncerInteractor.forceShow();
-                updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
-            } else {
-                showPrimaryBouncer(scrimmed);
-            }
-            return;
-        }
-
-        if (!mAlternateBouncerInteractor.show()) {
-            showPrimaryBouncer(scrimmed);
-        } else {
+        if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
+            Log.d(TAG, "showBouncer:alternateBouncer.forceShow()");
+            mAlternateBouncerInteractor.forceShow();
             updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
+        } else {
+            showPrimaryBouncer(scrimmed);
         }
     }
 
@@ -911,13 +889,9 @@
                         mKeyguardGoneCancelAction = null;
                     }
 
-                    if (DeviceEntryUdfpsRefactor.isEnabled()) {
-                        Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()");
-                        mAlternateBouncerInteractor.forceShow();
-                        updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
-                    } else {
-                        updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
-                    }
+                    Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()");
+                    mAlternateBouncerInteractor.forceShow();
+                    updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
                     setKeyguardMessage(message, null, null);
                     return;
                 }
@@ -1023,11 +997,6 @@
         }
 
         final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState();
-        if (mKeyguardMessageAreaController != null) {
-            DeviceEntryUdfpsRefactor.assertInLegacyMode();
-            mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
-            mKeyguardMessageAreaController.setMessage("");
-        }
         if (!SceneContainerFlag.isEnabled()) {
             mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
         }
@@ -1636,12 +1605,7 @@
     /** Display security message to relevant KeyguardMessageArea. */
     public void setKeyguardMessage(String message, ColorStateList colorState,
             BiometricSourceType biometricSourceType) {
-        if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (mKeyguardMessageAreaController != null) {
-                DeviceEntryUdfpsRefactor.assertInLegacyMode();
-                mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
-            }
-        } else {
+        if (!mAlternateBouncerInteractor.isVisibleState()) {
             mPrimaryBouncerInteractor.showMessage(message, colorState);
         }
     }
@@ -1768,66 +1732,6 @@
         }
     }
 
-    /**
-     * An opportunity for the AlternateBouncer to handle the touch instead of sending
-     * the touch to NPVC child views.
-     * @return true if the alternate bouncer should consime the touch and prevent it from
-     * going to its child views
-     */
-    public boolean dispatchTouchEvent(MotionEvent event) {
-        if (shouldInterceptTouchEvent(event)
-                && !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) {
-            onTouch(event);
-        }
-        return shouldInterceptTouchEvent(event);
-    }
-
-    /**
-     * Whether the touch should be intercepted by the AlternateBouncer before going to the
-     * notification shade's child views.
-     */
-    public boolean shouldInterceptTouchEvent(MotionEvent event) {
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            return false;
-        }
-        return mAlternateBouncerInteractor.isVisibleState();
-    }
-
-    /**
-     * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently
-     * showing.
-     */
-    public boolean onTouch(MotionEvent event) {
-        if (DeviceEntryUdfpsRefactor.isEnabled()) {
-            return false;
-        }
-
-        boolean handleTouch = shouldInterceptTouchEvent(event);
-        if (handleTouch) {
-            final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN;
-            final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch()
-                    && event.getActionMasked() == MotionEvent.ACTION_UP;
-            final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade =
-                    mKeyguardUpdateManager.isUdfpsEnrolled();
-            final boolean actionOutsideShouldDismissAlternateBouncer =
-                    event.getActionMasked() == MotionEvent.ACTION_OUTSIDE
-                    && !udfpsOverlayWillForwardEventsOutsideNotificationShade;
-            if (actionDown) {
-                mAlternateBouncerInteractor.setReceivedDownTouch(true);
-            } else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer)
-                    && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) {
-                showPrimaryBouncer(true);
-            }
-        }
-
-        // Forward NPVC touches to callbacks in case they want to respond to touches
-        for (KeyguardViewManagerCallback callback: mCallbacks) {
-            callback.onTouch(event);
-        }
-
-        return handleTouch;
-    }
-
     /** Update keyguard position based on a tapped X coordinate. */
     public void updateKeyguardPosition(float x) {
         mPrimaryBouncerInteractor.setKeyguardPosition(x);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
index 640ec28..c40822d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
@@ -20,6 +20,7 @@
 import android.content.res.Resources
 import android.graphics.Color
 import android.graphics.drawable.PaintDrawable
+import android.util.TypedValue
 import android.view.MotionEvent
 import android.view.View
 import android.view.View.OnHoverListener
@@ -27,10 +28,10 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -65,6 +66,26 @@
         createDarkAwareListener(view, darkIconDispatcher.darkChangeFlow())
 
     /**
+     * Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay
+     * Also sets margins for hover background relative to view bounds
+     */
+    fun createDarkAwareListener(
+        view: View,
+        leftHoverMargin: Int = 0,
+        rightHoverMargin: Int = 0,
+        topHoverMargin: Int = 0,
+        bottomHoverMargin: Int = 0,
+    ) =
+        createDarkAwareListener(
+            view,
+            darkIconDispatcher.darkChangeFlow(),
+            leftHoverMargin,
+            rightHoverMargin,
+            topHoverMargin,
+            bottomHoverMargin,
+        )
+
+    /**
      * Creates listener using provided [DarkChange] producer to determine light or dark color of the
      * overlay
      */
@@ -76,6 +97,25 @@
             darkFlow.map { toHoverTheme(view, it) },
         )
 
+    private fun createDarkAwareListener(
+        view: View,
+        darkFlow: StateFlow<DarkChange>,
+        leftHoverMargin: Int = 0,
+        rightHoverMargin: Int = 0,
+        topHoverMargin: Int = 0,
+        bottomHoverMargin: Int = 0,
+    ) =
+        StatusOverlayHoverListener(
+            view,
+            configurationController,
+            resources,
+            darkFlow.map { toHoverTheme(view, it) },
+            leftHoverMargin,
+            rightHoverMargin,
+            topHoverMargin,
+            bottomHoverMargin,
+        )
+
     private fun toHoverTheme(view: View, darkChange: DarkChange): HoverTheme {
         val calculatedTint = DarkIconDispatcher.getTint(darkChange.areas, view, darkChange.tint)
         // currently calculated tint is either white or some shade of black.
@@ -91,7 +131,7 @@
  */
 enum class HoverTheme {
     LIGHT,
-    DARK
+    DARK,
 }
 
 /**
@@ -103,11 +143,19 @@
     configurationController: ConfigurationController,
     private val resources: Resources,
     private val themeFlow: Flow<HoverTheme>,
+    private val leftHoverMargin: Int = 0,
+    private val rightHoverMargin: Int = 0,
+    private val topHoverMargin: Int = 0,
+    private val bottomHoverMargin: Int = 0,
 ) : OnHoverListener {
 
     @ColorInt private var darkColor: Int = 0
     @ColorInt private var lightColor: Int = 0
     private var cornerRadius = 0f
+    private var leftHoverMarginInPx: Int = 0
+    private var rightHoverMarginInPx: Int = 0
+    private var topHoverMarginInPx: Int = 0
+    private var bottomHoverMarginInPx: Int = 0
 
     private var lastTheme = HoverTheme.LIGHT
 
@@ -138,7 +186,12 @@
             val drawable =
                 PaintDrawable(backgroundColor).apply {
                     setCornerRadius(cornerRadius)
-                    setBounds(0, 0, v.width, v.height)
+                    setBounds(
+                        /*left = */ 0 + leftHoverMarginInPx,
+                        /*top = */ 0 + topHoverMarginInPx,
+                        /*right = */ v.width - rightHoverMarginInPx,
+                        /*bottom = */ v.height - bottomHoverMarginInPx,
+                    )
                 }
             v.overlay.add(drawable)
         } else if (event.action == MotionEvent.ACTION_HOVER_EXIT) {
@@ -151,5 +204,18 @@
         lightColor = resources.getColor(R.color.status_bar_icons_hover_color_light)
         darkColor = resources.getColor(R.color.status_bar_icons_hover_color_dark)
         cornerRadius = resources.getDimension(R.dimen.status_icons_hover_state_background_radius)
+        leftHoverMarginInPx = leftHoverMargin.dpToPx(resources)
+        rightHoverMarginInPx = rightHoverMargin.dpToPx(resources)
+        topHoverMarginInPx = topHoverMargin.dpToPx(resources)
+        bottomHoverMarginInPx = bottomHoverMargin.dpToPx(resources)
+    }
+
+    private fun Int.dpToPx(resources: Resources): Int {
+        return TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP,
+                toFloat(),
+                resources.displayMetrics,
+            )
+            .toInt()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 5b03198..09e191d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -15,15 +15,25 @@
  */
 package com.android.systemui.statusbar.phone.dagger
 
+import android.view.Display
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Default
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.core.CommandQueueInitializer
+import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore
+import com.android.systemui.statusbar.core.MultiDisplayStatusBarStarter
+import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.core.StatusBarInitializer
 import com.android.systemui.statusbar.core.StatusBarInitializerImpl
+import com.android.systemui.statusbar.core.StatusBarInitializerStore
 import com.android.systemui.statusbar.core.StatusBarOrchestrator
 import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
 import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl
 import dagger.Binds
@@ -32,6 +42,7 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineScope
 
 /** Similar in purpose to [StatusBarModule], but scoped only to phones */
 @Module
@@ -47,25 +58,91 @@
         impl: CentralSurfacesCommandQueueCallbacks
     ): CommandQueue.Callbacks
 
-    /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
     @Binds
-    @IntoMap
-    @ClassKey(StatusBarInitializerImpl::class)
-    fun bindStatusBarInitializer(impl: StatusBarInitializerImpl): CoreStartable
+    fun initializerFactory(
+        implFactory: StatusBarInitializerImpl.Factory
+    ): StatusBarInitializer.Factory
 
-    @Binds fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer
+    @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer
 
     companion object {
+        /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(StatusBarInitializer::class)
+        fun bindStatusBarInitializer(
+            @Default defaultInitializerLazy: Lazy<StatusBarInitializerImpl>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                // Will be started through MultiDisplayStatusBarStarter
+                CoreStartable.NOP
+            } else {
+                defaultInitializerLazy.get()
+            }
+        }
+
+        // Dagger doesn't support providing AssistedInject types, without a qualifier. Using the
+        // Default qualifier for this reason.
+        @Default
+        @Provides
+        @SysUISingleton
+        fun statusBarInitializerImpl(
+            implFactory: StatusBarInitializerImpl.Factory,
+            statusBarWindowControllerStore: StatusBarWindowControllerStore,
+        ): StatusBarInitializerImpl {
+            return implFactory.create(statusBarWindowControllerStore.defaultDisplay)
+        }
+
+        @Provides
+        @SysUISingleton
+        @Default // Dagger does not support providing @AssistedInject types without a qualifier
+        fun orchestrator(
+            @Background backgroundApplicationScope: CoroutineScope,
+            statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+            statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+            initializerStore: StatusBarInitializerStore,
+            statusBarWindowControllerStore: StatusBarWindowControllerStore,
+            statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+        ): StatusBarOrchestrator {
+            return statusBarOrchestratorFactory.create(
+                Display.DEFAULT_DISPLAY,
+                backgroundApplicationScope,
+                statusBarWindowStateRepositoryStore.defaultDisplay,
+                statusBarModeRepositoryStore.defaultDisplay,
+                initializerStore.defaultDisplay,
+                statusBarWindowControllerStore.defaultDisplay,
+            )
+        }
+
         @Provides
         @SysUISingleton
         @IntoMap
         @ClassKey(StatusBarOrchestrator::class)
         fun orchestratorCoreStartable(
-            orchestratorLazy: Lazy<StatusBarOrchestrator>
+            @Default orchestratorLazy: Lazy<StatusBarOrchestrator>
         ): CoreStartable {
-            return if (StatusBarSimpleFragment.isEnabled) {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                // Will be started through MultiDisplayStatusBarStarter
+                CoreStartable.NOP
+            } else if (StatusBarSimpleFragment.isEnabled) {
                 orchestratorLazy.get()
             } else {
+                // Will be started through CentralSurfacesImpl
+                CoreStartable.NOP
+            }
+        }
+
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(MultiDisplayStatusBarStarter::class)
+        fun multiDisplayStarter(
+            multiDisplayStatusBarStarterLazy: Lazy<MultiDisplayStatusBarStarter>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                multiDisplayStatusBarStarterLazy.get()
+            } else {
                 CoreStartable.NOP
             }
         }
@@ -83,5 +160,32 @@
                 CoreStartable.NOP
             }
         }
+
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(StatusBarInitializerStore::class)
+        fun initializerStoreAsCoreStartable(
+            multiDisplayStoreLazy: Lazy<MultiDisplayStatusBarInitializerStore>
+        ): CoreStartable {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                multiDisplayStoreLazy.get()
+            } else {
+                CoreStartable.NOP
+            }
+        }
+
+        @Provides
+        @SysUISingleton
+        fun initializerStore(
+            singleDisplayStoreLazy: Lazy<SingleDisplayStatusBarInitializerStore>,
+            multiDisplayStoreLazy: Lazy<MultiDisplayStatusBarInitializerStore>,
+        ): StatusBarInitializerStore {
+            return if (StatusBarConnectedDisplays.isEnabled) {
+                multiDisplayStoreLazy.get()
+            } else {
+                singleDisplayStoreLazy.get()
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index c258095..37c8c63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -29,6 +29,7 @@
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -55,7 +56,7 @@
 import com.android.systemui.statusbar.OperatorNameView;
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
@@ -65,15 +66,15 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
 import com.android.systemui.statusbar.phone.StatusBarLocation;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable;
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent;
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent.Startable;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 import com.android.systemui.statusbar.phone.ui.DarkIconManager;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
-import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder;
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder;
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener;
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
@@ -114,7 +115,7 @@
     public static final int FADE_IN_DELAY = 50;
     private static final int SOURCE_SYSTEM_EVENT_ANIMATOR = 1;
     private static final int SOURCE_OTHER = 2;
-    private StatusBarFragmentComponent mStatusBarFragmentComponent;
+    private HomeStatusBarComponent mHomeStatusBarComponent;
     private PhoneStatusBarView mStatusBar;
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardStateController mKeyguardStateController;
@@ -133,7 +134,7 @@
     private StatusBarVisibilityModel mLastModifiedVisibility =
             StatusBarVisibilityModel.createDefaultModel();
     private DarkIconManager mDarkIconManager;
-    private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+    private final HomeStatusBarComponent.Factory mHomeStatusBarComponentFactory;
     private final CommandQueue mCommandQueue;
     private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
     private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
@@ -142,8 +143,8 @@
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
     private final StatusBarIconController mStatusBarIconController;
     private final CarrierConfigTracker mCarrierConfigTracker;
-    private final CollapsedStatusBarViewModel mCollapsedStatusBarViewModel;
-    private final CollapsedStatusBarViewBinder mCollapsedStatusBarViewBinder;
+    private final HomeStatusBarViewModel mHomeStatusBarViewModel;
+    private final HomeStatusBarViewBinder mHomeStatusBarViewBinder;
     private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
     private final DarkIconManager.Factory mDarkIconManagerFactory;
     private final SecureSettings mSecureSettings;
@@ -238,14 +239,14 @@
 
     @Inject
     public CollapsedStatusBarFragment(
-            StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
+            HomeStatusBarComponent.Factory homeStatusBarComponentFactory,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
             ShadeExpansionStateManager shadeExpansionStateManager,
             StatusBarIconController statusBarIconController,
             DarkIconManager.Factory darkIconManagerFactory,
-            CollapsedStatusBarViewModel collapsedStatusBarViewModel,
-            CollapsedStatusBarViewBinder collapsedStatusBarViewBinder,
+            HomeStatusBarViewModel homeStatusBarViewModel,
+            HomeStatusBarViewBinder homeStatusBarViewBinder,
             StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
             KeyguardStateController keyguardStateController,
             PanelExpansionInteractor panelExpansionInteractor,
@@ -261,13 +262,13 @@
             StatusBarWindowStateController statusBarWindowStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             DemoModeController demoModeController) {
-        mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
+        mHomeStatusBarComponentFactory = homeStatusBarComponentFactory;
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
         mShadeExpansionStateManager = shadeExpansionStateManager;
         mStatusBarIconController = statusBarIconController;
-        mCollapsedStatusBarViewModel = collapsedStatusBarViewModel;
-        mCollapsedStatusBarViewBinder = collapsedStatusBarViewBinder;
+        mHomeStatusBarViewModel = homeStatusBarViewModel;
+        mHomeStatusBarViewBinder = homeStatusBarViewBinder;
         mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
         mDarkIconManagerFactory = darkIconManagerFactory;
         mKeyguardStateController = keyguardStateController;
@@ -333,12 +334,12 @@
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        mDumpManager.registerDumpable(getClass().getSimpleName(), this);
-        mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(
+        mDumpManager.registerDumpable(getDumpableName(), this);
+        mHomeStatusBarComponent = mHomeStatusBarComponentFactory.create(
                 (PhoneStatusBarView) getView());
-        mStatusBarFragmentComponent.init();
+        mHomeStatusBarComponent.init();
         mStartableStates.clear();
-        for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
+        for (Startable startable : mHomeStatusBarComponent.getStartables()) {
             mStartableStates.put(startable, Startable.State.STARTING);
             startable.start();
             mStartableStates.put(startable, Startable.State.STARTED);
@@ -370,8 +371,16 @@
         mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
         mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
 
-        mCollapsedStatusBarViewBinder.bind(
-                mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener);
+        mHomeStatusBarViewBinder.bind(
+                mStatusBar, mHomeStatusBarViewModel, mStatusBarVisibilityChangeListener);
+    }
+
+    private String getDumpableName() {
+        if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+            return getClass().getSimpleName();
+        } else {
+            return getClass().getSimpleName() + getContext().getDisplayId();
+        }
     }
 
     @Override
@@ -465,12 +474,12 @@
         mCarrierConfigTracker.removeCallback(mCarrierConfigCallback);
         mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener);
 
-        for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
+        for (Startable startable : mHomeStatusBarComponent.getStartables()) {
             mStartableStates.put(startable, Startable.State.STOPPING);
             startable.stop();
             mStartableStates.put(startable, Startable.State.STOPPED);
         }
-        mDumpManager.unregisterDumpable(getClass().getSimpleName());
+        mDumpManager.unregisterDumpable(getDumpableName());
         if (mNicBindingDisposable != null) {
             mNicBindingDisposable.dispose();
             mNicBindingDisposable = null;
@@ -486,7 +495,12 @@
         NotificationIconContainer notificationIcons =
                 notificationIconArea.requireViewById(R.id.notificationIcons);
         mNotificationIconAreaInner = notificationIcons;
-        mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+        if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+            //TODO(b/369337701): implement notification icons for all displays.
+            // Currently if we try to bind for all displays, there is a crash, because the same
+            // notification icon view can't have multiple parents.
+            mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+        }
 
         if (!StatusBarSimpleFragment.isEnabled()) {
             updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
@@ -501,8 +515,8 @@
      *   fragment functionality and we won't need to expose it here anymore.
      */
     @Nullable
-    public StatusBarFragmentComponent getStatusBarFragmentComponent() {
-        return mStatusBarFragmentComponent;
+    public HomeStatusBarComponent getHomeStatusBarComponent() {
+        return mHomeStatusBarComponent;
     }
 
     private StatusBarVisibilityChangeListener mStatusBarVisibilityChangeListener =
@@ -608,7 +622,7 @@
 
         // TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead.
         boolean headsUpVisible =
-                mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
+                mHomeStatusBarComponent.getHeadsUpAppearanceController().shouldBeVisible();
 
         if (SceneContainerFlag.isEnabled()) {
             // With the scene container, only use the value calculated by the view model to
@@ -642,7 +656,7 @@
         }
         boolean showSecondaryOngoingActivityChip =
                 Flags.statusBarScreenSharingChips()
-                        && StatusBarRonChips.isEnabled()
+                        && StatusBarNotifChips.isEnabled()
                         && mHasSecondaryOngoingActivity;
 
         return new StatusBarVisibilityModel(
@@ -684,7 +698,7 @@
 
         boolean showSecondaryOngoingActivityChip =
                 // Secondary chips are only supported when RONs are enabled.
-                StatusBarRonChips.isEnabled()
+                StatusBarNotifChips.isEnabled()
                         && visibilityModel.getShowSecondaryOngoingActivityChip()
                         && !disableNotifications;
         if (showSecondaryOngoingActivityChip) {
@@ -743,7 +757,7 @@
         // transition to occluding to finish before allowing us to potentially show the status bar
         // again. (This status bar is always hidden on keyguard, so it's safe to continue hiding it
         // during this transition.) See b/273314977.
-        if (mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().getValue()) {
+        if (mHomeStatusBarViewModel.isTransitioningFromLockscreenToOccluded().getValue()) {
             return true;
         }
 
@@ -811,7 +825,7 @@
     }
 
     private void showSecondaryOngoingActivityChip(boolean animate) {
-        StatusBarRonChips.assertInNewMode();
+        StatusBarNotifChips.assertInNewMode();
         StatusBarSimpleFragment.assertInLegacyMode();
         animateShow(mSecondaryOngoingActivityChip, animate);
     }
@@ -983,7 +997,7 @@
         pw.println("mHasPrimaryOngoingActivity=" + mHasPrimaryOngoingActivity);
         pw.println("mHasSecondaryOngoingActivity=" + mHasSecondaryOngoingActivity);
         pw.println("mAnimationsEnabled=" + mAnimationsEnabled);
-        StatusBarFragmentComponent component = mStatusBarFragmentComponent;
+        HomeStatusBarComponent component = mHomeStatusBarComponent;
         if (component == null) {
             pw.println("StatusBarFragmentComponent is null");
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt
index 55af0e3..94006f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentStartable.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.fragments.FragmentService
 import com.android.systemui.qs.QSFragmentStartable
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -37,7 +37,7 @@
 @Inject
 constructor(
     private val fragmentService: FragmentService,
-    private val collapsedstatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>
+    private val collapsedstatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
 ) : CoreStartable {
     override fun start() {
         fragmentService.addFragmentInstantiationProvider(
@@ -47,7 +47,7 @@
     }
 }
 
-@Module(subcomponents = [StatusBarFragmentComponent::class])
+@Module(subcomponents = [HomeStatusBarComponent::class])
 interface CollapsedStatusBarFragmentStartableModule {
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
index 96faa35..d4cb625 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
@@ -42,27 +42,29 @@
  * fragment is recreated.
  *
  * Anything that depends on {@link CollapsedStatusBarFragment} or {@link PhoneStatusBarView}
- * should be included here or in {@link StatusBarFragmentModule}.
+ * should be included here or in {@link HomeStatusBarModule}.
  */
-
 @Subcomponent(modules = {
-        StatusBarFragmentModule.class,
+        HomeStatusBarModule.class,
         StatusBarStartablesModule.class
 })
-@StatusBarFragmentScope
-public interface StatusBarFragmentComponent {
+@HomeStatusBarScope
+public interface HomeStatusBarComponent {
     /** Simple factory. */
     @Subcomponent.Factory
     interface Factory {
-        StatusBarFragmentComponent create(
+        /** */
+        HomeStatusBarComponent create(
                 @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView);
     }
 
     /**
-     * Performs initialization logic after {@link StatusBarFragmentComponent} has been constructed.
+     * Performs initialization logic after {@link HomeStatusBarComponent} has been constructed.
      */
     interface Startable {
+        /** */
         void start();
+        /** */
         void stop();
 
         enum State {
@@ -86,32 +88,32 @@
     }
 
     /** */
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     BatteryMeterViewController getBatteryMeterViewController();
 
     /** */
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     @RootView
     PhoneStatusBarView getPhoneStatusBarView();
 
     /** */
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     PhoneStatusBarViewController getPhoneStatusBarViewController();
 
     /** */
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     HeadsUpAppearanceController getHeadsUpAppearanceController();
 
     /** */
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     LegacyLightsOutNotifController getLegacyLightsOutNotifController();
 
     /** */
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     StatusBarDemoMode getStatusBarDemoMode();
 
     /** */
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     PhoneStatusBarTransitions getPhoneStatusBarTransitions();
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
index f026b99..f6f8adb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
@@ -20,15 +20,18 @@
 import android.view.ViewStub;
 
 import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.dagger.qualifiers.DisplaySpecific;
 import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore;
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.StatusBarLocation;
 import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 
 import dagger.Module;
 import dagger.Provides;
@@ -37,9 +40,9 @@
 
 import javax.inject.Named;
 
-/** Dagger module for {@link StatusBarFragmentComponent}. */
+/** Dagger module for {@link HomeStatusBarComponent}. */
 @Module
-public interface StatusBarFragmentModule {
+public interface HomeStatusBarModule {
 
     String LIGHTS_OUT_NOTIF_VIEW = "lights_out_notif_view";
     String OPERATOR_NAME_VIEW = "operator_name_view";
@@ -49,21 +52,21 @@
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.battery);
     }
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     static StatusBarLocation getStatusBarLocation() {
         return StatusBarLocation.HOME;
     }
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     @Named(START_SIDE_CONTENT)
     static View startSideContent(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.status_bar_start_side_content);
@@ -71,7 +74,7 @@
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     @Named(END_SIDE_CONTENT)
     static View endSideContent(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.status_bar_end_side_content);
@@ -79,7 +82,7 @@
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     @Named(LIGHTS_OUT_NOTIF_VIEW)
     static View provideLightsOutNotifView(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.notification_lights_out);
@@ -87,7 +90,7 @@
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     @Named(OPERATOR_NAME_VIEW)
     static View provideOperatorNameView(@RootView PhoneStatusBarView view) {
         View operatorName = ((ViewStub) view.findViewById(R.id.operator_name_stub)).inflate();
@@ -97,7 +100,7 @@
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     @Named(OPERATOR_NAME_FRAME_VIEW)
     static Optional<View> provideOperatorFrameNameView(@RootView PhoneStatusBarView view) {
         return Optional.ofNullable(view.findViewById(R.id.operator_name_frame));
@@ -105,14 +108,14 @@
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     static Clock provideClock(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.clock);
     }
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     static PhoneStatusBarViewController providePhoneStatusBarViewController(
             PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
             @RootView PhoneStatusBarView phoneStatusBarView) {
@@ -122,18 +125,35 @@
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     static PhoneStatusBarTransitions providePhoneStatusBarTransitions(
             @RootView PhoneStatusBarView view,
-            StatusBarWindowController statusBarWindowController
-    ) {
-        return new PhoneStatusBarTransitions(view, statusBarWindowController.getBackgroundView());
+            StatusBarWindowControllerStore statusBarWindowControllerStore) {
+        return new PhoneStatusBarTransitions(
+                view, statusBarWindowControllerStore.getDefaultDisplay().getBackgroundView());
     }
 
     /** */
     @Provides
-    @StatusBarFragmentScope
+    @HomeStatusBarScope
     static HeadsUpStatusBarView providesHeasdUpStatusBarView(@RootView PhoneStatusBarView view) {
         return view.findViewById(R.id.heads_up_status_bar_view);
     }
+
+    /** */
+    @Provides
+    @HomeStatusBarScope
+    @DisplaySpecific
+    static int displayId(@RootView PhoneStatusBarView view) {
+        return view.getContext().getDisplayId();
+    }
+
+    /** */
+    @Provides
+    @HomeStatusBarScope
+    static StatusBarConfigurationController configurationController(
+            @DisplaySpecific int displayId, StatusBarConfigurationControllerStore store) {
+        return store.forDisplay(displayId);
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarScope.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarScope.java
index 96cff59..2b1eddd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarScope.java
@@ -24,9 +24,9 @@
 import javax.inject.Scope;
 
 /**
- * Scope annotation for singleton items within the {@link StatusBarFragmentComponent}.
+ * Scope annotation for singleton items within the {@link HomeStatusBarComponent}.
  */
 @Documented
 @Retention(RUNTIME)
 @Scope
-public @interface StatusBarFragmentScope {}
+public @interface HomeStatusBarScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
index 9003d13..ba91814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
@@ -28,5 +28,5 @@
     @IntoSet
     fun statusBarBoundsCalculator(
         statusBarBoundsProvider: StatusBarBoundsProvider
-    ): StatusBarFragmentComponent.Startable
+    ): HomeStatusBarComponent.Startable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index bd6a1c0..3cf8c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -52,7 +52,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.policy.CallbackController
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import java.util.concurrent.Executor
@@ -75,7 +75,7 @@
     @Main private val mainExecutor: Executor,
     private val iActivityManager: IActivityManager,
     private val dumpManager: DumpManager,
-    private val statusBarWindowController: StatusBarWindowController,
+    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
     private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
     private val statusBarModeRepository: StatusBarModeRepositoryStore,
     @OngoingCallLog private val logger: LogBuffer,
@@ -205,7 +205,9 @@
         this.chipView = chipView
         val backgroundView: ChipBackgroundContainer? =
             chipView.findViewById(R.id.ongoing_activity_chip_background)
-        backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight }
+        backgroundView?.maxHeightFetcher = {
+            statusBarWindowControllerStore.defaultDisplay.statusBarHeight
+        }
         if (hasOngoingCall()) {
             updateChip()
         }
@@ -339,7 +341,8 @@
             // But, this class still needs to do the non-display logic regardless of the flag.
             uidObserver.registerWithUid(currentCallNotificationInfo.uid)
             if (!currentCallNotificationInfo.statusBarSwipedAway) {
-                statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true)
+                statusBarWindowControllerStore.defaultDisplay
+                    .setOngoingProcessRequiresStatusBarVisible(true)
             }
             updateGestureListening()
             sendStateChangeEvent()
@@ -405,7 +408,9 @@
         if (!Flags.statusBarScreenSharingChips()) {
             tearDownChipView()
         }
-        statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+        statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
+            false
+        )
         swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
         sendStateChangeEvent()
         uidObserver.unregister()
@@ -429,7 +434,9 @@
     private fun onSwipeAwayGestureDetected() {
         logger.log(TAG, LogLevel.DEBUG, {}, { "Swipe away gesture detected" })
         callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
-        statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
+        statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
+            false
+        )
         swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 4850049..935b101 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -48,10 +48,10 @@
 import com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel.DeviceBasedSatelliteViewModelImpl
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
-import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
-import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinderImpl
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinderImpl
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModelImpl
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
@@ -131,14 +131,10 @@
     abstract fun bindCarrierConfigStartable(impl: CarrierConfigCoreStartable): CoreStartable
 
     @Binds
-    abstract fun collapsedStatusBarViewModel(
-        impl: CollapsedStatusBarViewModelImpl
-    ): CollapsedStatusBarViewModel
+    abstract fun homeStatusBarViewModel(impl: HomeStatusBarViewModelImpl): HomeStatusBarViewModel
 
     @Binds
-    abstract fun collapsedStatusBarViewBinder(
-        impl: CollapsedStatusBarViewBinderImpl
-    ): CollapsedStatusBarViewBinder
+    abstract fun homeStatusBarViewBinder(impl: HomeStatusBarViewBinderImpl): HomeStatusBarViewBinder
 
     companion object {
 
@@ -162,7 +158,7 @@
         @SysUISingleton
         @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON)
         fun provideFirstMobileSubShowingNetworkTypeIconProvider(
-            mobileIconsViewModel: MobileIconsViewModel,
+            mobileIconsViewModel: MobileIconsViewModel
         ): Supplier<Flow<Boolean>> {
             return Supplier<Flow<Boolean>> {
                 mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index bad6f80..694a5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlagsClassic
@@ -29,8 +29,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancel
@@ -116,7 +116,7 @@
 
     private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> {
         // Create a child scope so we can cancel it
-        val vmScope = scope.createChildScope(createCoroutineTracingContext("MobileIconViewModel"))
+        val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel"))
         val vm =
             MobileIconViewModel(
                 subId,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 03ec41d..470abe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -182,7 +182,7 @@
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
-                TelephonyManager.RADIO_POWER_UNAVAILABLE
+                TelephonyManager.RADIO_POWER_UNAVAILABLE,
             )
 
     /**
@@ -265,9 +265,10 @@
 
                 var registered = false
                 try {
+                    logBuffer.i { "registerForCommunicationAllowedStateChanged" }
                     sm.registerForCommunicationAllowedStateChanged(
                         bgDispatcher.asExecutor(),
-                        callback
+                        callback,
                     )
                     registered = true
                 } catch (e: Exception) {
@@ -276,6 +277,7 @@
 
                 awaitClose {
                     if (registered) {
+                        logBuffer.i { "unRegisterForCommunicationAllowedStateChanged" }
                         sm.unregisterForCommunicationAllowedStateChanged(callback)
                     }
                 }
@@ -321,9 +323,10 @@
 
                 var registered = false
                 try {
+                    logBuffer.i { "registerForSupportedStateChanged" }
                     satelliteManager.registerForSupportedStateChanged(
                         bgDispatcher.asExecutor(),
-                        callback
+                        callback,
                     )
                     registered = true
                 } catch (e: Exception) {
@@ -332,6 +335,7 @@
 
                 awaitClose {
                     if (registered) {
+                        logBuffer.i { "unregisterForSupportedStateChanged" }
                         satelliteManager.unregisterForSupportedStateChanged(callback)
                     }
                 }
@@ -366,10 +370,7 @@
                 var registered = false
                 try {
                     logBuffer.i { "registerForProvisionStateChanged" }
-                    sm.registerForProvisionStateChanged(
-                        bgDispatcher.asExecutor(),
-                        callback,
-                    )
+                    sm.registerForProvisionStateChanged(bgDispatcher.asExecutor(), callback)
                     registered = true
                 } catch (e: Exception) {
                     logBuffer.e("error registering for provisioning state callback", e)
@@ -377,6 +378,7 @@
 
                 awaitClose {
                     if (registered) {
+                        logBuffer.i { "unregisterForProvisionStateChanged" }
                         sm.unregisterForProvisionStateChanged(callback)
                     }
                 }
@@ -526,17 +528,10 @@
             uptime - (clock.uptimeMillis() - android.os.Process.getStartUptimeMillis())
 
         /** A couple of convenience logging methods rather than a whole class */
-        private fun LogBuffer.i(
-            initializer: MessageInitializer = {},
-            printer: MessagePrinter,
-        ) = this.log(TAG, LogLevel.INFO, initializer, printer)
+        private fun LogBuffer.i(initializer: MessageInitializer = {}, printer: MessagePrinter) =
+            this.log(TAG, LogLevel.INFO, initializer, printer)
 
         private fun LogBuffer.e(message: String, exception: Throwable? = null) =
-            this.log(
-                tag = TAG,
-                level = LogLevel.ERROR,
-                message = message,
-                exception = exception,
-            )
+            this.log(tag = TAG, level = LogLevel.ERROR, message = message, exception = exception)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
index f5cfc8c..e0bf00f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -26,6 +26,7 @@
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.vcn.VcnTransportInfo
+import android.net.vcn.VcnUtils
 import android.net.wifi.WifiInfo
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import androidx.annotation.ArrayRes
@@ -161,7 +162,9 @@
         defaultNetworkCapabilities
             .map { networkCapabilities ->
                 networkCapabilities?.run {
-                    val subId = (transportInfo as? VcnTransportInfo)?.subId
+                    val subId =
+                        VcnUtils.getSubIdFromVcnCaps(connectivityManager, networkCapabilities)
+
                     // Never return an INVALID_SUBSCRIPTION_ID (-1)
                     if (subId != INVALID_SUBSCRIPTION_ID) {
                         subId
@@ -245,9 +248,9 @@
          * info.
          */
         fun NetworkCapabilities.getMainOrUnderlyingWifiInfo(
-            connectivityManager: ConnectivityManager,
+            connectivityManager: ConnectivityManager
         ): WifiInfo? {
-            val mainWifiInfo = this.getMainWifiInfo()
+            val mainWifiInfo = this.getMainWifiInfo(connectivityManager)
             if (mainWifiInfo != null) {
                 return mainWifiInfo
             }
@@ -264,7 +267,9 @@
             // eventually traced to a wifi or carrier merged connection. So, check those underlying
             // networks for possible wifi information as well. See b/225902574.
             return this.underlyingNetworks?.firstNotNullOfOrNull { underlyingNetwork ->
-                connectivityManager.getNetworkCapabilities(underlyingNetwork)?.getMainWifiInfo()
+                connectivityManager
+                    .getNetworkCapabilities(underlyingNetwork)
+                    ?.getMainWifiInfo(connectivityManager)
             }
         }
 
@@ -272,7 +277,9 @@
          * Checks the network capabilities for wifi info, but does *not* check the underlying
          * networks. See [getMainOrUnderlyingWifiInfo].
          */
-        private fun NetworkCapabilities.getMainWifiInfo(): WifiInfo? {
+        private fun NetworkCapabilities.getMainWifiInfo(
+            connectivityManager: ConnectivityManager
+        ): WifiInfo? {
             // Wifi info can either come from a WIFI Transport, or from a CELLULAR transport for
             // virtual networks like VCN.
             val canHaveWifiInfo =
@@ -286,7 +293,7 @@
                 // [com.android.settingslib.Utils.tryGetWifiInfoForVcn]. It's copied instead of
                 // re-used because it makes the logic here clearer, and because the method will be
                 // removed once this pipeline is fully launched.
-                is VcnTransportInfo -> currentTransportInfo.wifiInfo
+                is VcnTransportInfo -> VcnUtils.getWifiInfoFromVcnCaps(connectivityManager, this)
                 is WifiInfo -> currentTransportInfo
                 else -> null
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 9c168be..8d7b57d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -27,36 +27,38 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.core.StatusBarSimpleFragment
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
 import javax.inject.Inject
 import kotlinx.coroutines.launch
 
 /**
- * Interface to assist with binding the [CollapsedStatusBarFragment] to
- * [CollapsedStatusBarViewModel]. Used only to enable easy testing of [CollapsedStatusBarFragment].
+ * Interface to assist with binding the [CollapsedStatusBarFragment] to [HomeStatusBarViewModel].
+ * Used only to enable easy testing of [CollapsedStatusBarFragment].
  */
-interface CollapsedStatusBarViewBinder {
+interface HomeStatusBarViewBinder {
     /**
      * Binds the view to the view-model. [listener] will be notified whenever an event that may
      * change the status bar visibility occurs.
      */
     fun bind(
         view: View,
-        viewModel: CollapsedStatusBarViewModel,
+        viewModel: HomeStatusBarViewModel,
         listener: StatusBarVisibilityChangeListener,
     )
 }
 
 @SysUISingleton
-class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBarViewBinder {
+class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinder {
     override fun bind(
         view: View,
-        viewModel: CollapsedStatusBarViewModel,
+        viewModel: HomeStatusBarViewModel,
         listener: StatusBarVisibilityChangeListener,
     ) {
         view.repeatWhenAttached {
@@ -83,7 +85,7 @@
                     }
                 }
 
-                if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) {
+                if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) {
                     val primaryChipView: View =
                         view.requireViewById(R.id.ongoing_activity_chip_primary)
                     launch {
@@ -119,7 +121,7 @@
                     }
                 }
 
-                if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) {
+                if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) {
                     val primaryChipView: View =
                         view.requireViewById(R.id.ongoing_activity_chip_primary)
                     val secondaryChipView: View =
@@ -184,9 +186,8 @@
         }
     }
 
-    private fun OngoingActivityChipModel.toVisibilityModel():
-        CollapsedStatusBarViewModel.VisibilityModel {
-        return CollapsedStatusBarViewModel.VisibilityModel(
+    private fun OngoingActivityChipModel.toVisibilityModel(): VisibilityModel {
+        return VisibilityModel(
             visibility = if (this is OngoingActivityChipModel.Shown) View.VISIBLE else View.GONE,
             // TODO(b/364653005): Figure out the animation story here.
             shouldAnimateChange = true,
@@ -223,7 +224,7 @@
             .start()
     }
 
-    private fun View.adjustVisibility(model: CollapsedStatusBarViewModel.VisibilityModel) {
+    private fun View.adjustVisibility(model: VisibilityModel) {
         if (model.visibility == View.VISIBLE) {
             this.show(model.shouldAnimateChange)
         } else {
@@ -297,7 +298,7 @@
 
     /**
      * Called when the scene state has changed such that the home status bar is newly allowed or no
-     * longer allowed. See [CollapsedStatusBarViewModel.isHomeStatusBarAllowedByScene].
+     * longer allowed. See [HomeStatusBarViewModel.isHomeStatusBarAllowedByScene].
      */
     fun onIsHomeStatusBarAllowedBySceneChanged(isHomeStatusBarAllowedByScene: Boolean)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/RetroText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/RetroText.kt
new file mode 100644
index 0000000..71bbdea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/RetroText.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.composable
+
+import androidx.compose.foundation.layout.offset
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+private val retroColors =
+    listOf(
+        Color(0xFFEADFB4), // beige
+        Color(0xFF9BB0C1), // gray-blue
+        Color(0xFFF6995C), // orange
+        Color(0xFF51829B), // cyan
+    )
+
+/** Render a single string multiple times (with offsets) kinda like retro vintage text */
+@Composable
+fun RetroText(text: String = "") {
+    // Render the text for each retroColor, and then once for the foreground
+    for (i in retroColors.size downTo 1) {
+        val color = retroColors[i - 1]
+        RetroTextLayer(text = text, color = color, (-1.5 * i).dp, i.dp)
+    }
+
+    RetroTextLayer(text = text, color = Color.Black, ox = 0.dp, oy = 0.dp)
+}
+
+@Composable
+fun RetroTextLayer(text: String, color: Color, ox: Dp, oy: Dp) {
+    Text(
+        text = text,
+        modifier = Modifier.offset(ox, oy),
+        textAlign = TextAlign.Center,
+        style =
+            TextStyle(
+                fontSize = 18.sp,
+                fontWeight = FontWeight.Bold,
+                fontStyle = FontStyle.Italic,
+                color = color,
+            ),
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
new file mode 100644
index 0000000..a21cc22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.composable
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.phone.PhoneStatusBarView
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
+import com.android.systemui.statusbar.phone.ui.DarkIconManager
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.launch
+
+/** Factory to simplify the dependency management for [StatusBarRoot] */
+class StatusBarRootFactory
+@Inject
+constructor(
+    private val homeStatusBarViewModel: HomeStatusBarViewModel,
+    private val homeStatusBarViewBinder: HomeStatusBarViewBinder,
+    private val notificationIconsBinder: NotificationIconContainerStatusBarViewBinder,
+    private val darkIconManagerFactory: DarkIconManager.Factory,
+    private val iconController: StatusBarIconController,
+    private val ongoingCallController: OngoingCallController,
+) {
+    fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView {
+        val composeView = ComposeView(root.context)
+        composeView.apply {
+            setContent {
+                StatusBarRoot(
+                    parent = root,
+                    statusBarViewModel = homeStatusBarViewModel,
+                    statusBarViewBinder = homeStatusBarViewBinder,
+                    notificationIconsBinder = notificationIconsBinder,
+                    darkIconManagerFactory = darkIconManagerFactory,
+                    iconController = iconController,
+                    ongoingCallController = ongoingCallController,
+                    onViewCreated = andThen,
+                )
+            }
+        }
+
+        return composeView
+    }
+}
+
+/**
+ * For now, this class exists only to replace the former CollapsedStatusBarFragment. We simply stand
+ * up the PhoneStatusBarView here (allowing the component to be initialized from the [init] block).
+ * This is the place, for now, where we can manually set up lingering dependencies that came from
+ * the fragment until we can move them to recommended-arch style repos.
+ *
+ * @param onViewCreated called immediately after the view is inflated, and takes as a parameter the
+ *   newly-inflated PhoneStatusBarView. This lambda is useful for tying together old initialization
+ *   logic until it can be replaced.
+ */
+@Composable
+fun StatusBarRoot(
+    parent: ViewGroup,
+    statusBarViewModel: HomeStatusBarViewModel,
+    statusBarViewBinder: HomeStatusBarViewBinder,
+    notificationIconsBinder: NotificationIconContainerStatusBarViewBinder,
+    darkIconManagerFactory: DarkIconManager.Factory,
+    iconController: StatusBarIconController,
+    ongoingCallController: OngoingCallController,
+    onViewCreated: (ViewGroup) -> Unit,
+) {
+    // None of these methods are used when [StatusBarSimpleFragment] is on.
+    // This can be deleted once the fragment is gone
+    val nopVisibilityChangeListener =
+        object : StatusBarVisibilityChangeListener {
+            override fun onStatusBarVisibilityMaybeChanged() {}
+
+            override fun onTransitionFromLockscreenToDreamStarted() {}
+
+            override fun onOngoingActivityStatusChanged(
+                hasPrimaryOngoingActivity: Boolean,
+                hasSecondaryOngoingActivity: Boolean,
+                shouldAnimate: Boolean,
+            ) {}
+
+            override fun onIsHomeStatusBarAllowedBySceneChanged(
+                isHomeStatusBarAllowedByScene: Boolean
+            ) {}
+        }
+
+    Box(Modifier.fillMaxSize()) {
+        // TODO(b/364360986): remove this before rolling the flag forward
+        Disambiguation(viewModel = statusBarViewModel)
+
+        Row(Modifier.fillMaxSize()) {
+            val scope = rememberCoroutineScope()
+            AndroidView(
+                factory = { context ->
+                    val inflater = LayoutInflater.from(context)
+                    val phoneStatusBarView =
+                        inflater.inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
+
+                    // For now, just set up the system icons the same way we used to
+                    val statusIconContainer =
+                        phoneStatusBarView.requireViewById<StatusIconContainer>(R.id.statusIcons)
+                    // TODO(b/364360986): turn this into a repo/intr/viewmodel
+                    val darkIconManager =
+                        darkIconManagerFactory.create(statusIconContainer, StatusBarLocation.HOME)
+                    iconController.addIconGroup(darkIconManager)
+
+                    // TODO(b/372657935): This won't be needed once OngoingCallController is
+                    // implemented in recommended architecture
+                    ongoingCallController.setChipView(
+                        phoneStatusBarView.requireViewById(R.id.ongoing_activity_chip_primary)
+                    )
+
+                    // For notifications, first inflate the [NotificationIconContainer]
+                    val notificationIconArea =
+                        phoneStatusBarView.requireViewById<ViewGroup>(R.id.notification_icon_area)
+                    inflater.inflate(R.layout.notification_icon_area, notificationIconArea, true)
+                    // Then bind it using the icons binder
+                    val notificationIconContainer =
+                        phoneStatusBarView.requireViewById<NotificationIconContainer>(
+                            R.id.notificationIcons
+                        )
+                    scope.launch {
+                        notificationIconsBinder.bindWhileAttached(notificationIconContainer)
+                    }
+
+                    // This binder handles everything else
+                    scope.launch {
+                        statusBarViewBinder.bind(
+                            phoneStatusBarView,
+                            statusBarViewModel,
+                            nopVisibilityChangeListener,
+                        )
+                    }
+                    onViewCreated(phoneStatusBarView)
+                    phoneStatusBarView
+                }
+            )
+        }
+    }
+}
+
+/**
+ * This is our analog of the flexi "ribbon", which just shows some text so we know if the flag is on
+ */
+@Composable
+fun Disambiguation(viewModel: HomeStatusBarViewModel) {
+    val clockVisibilityModel =
+        viewModel.isClockVisible.collectAsStateWithLifecycle(
+            initialValue =
+                HomeStatusBarViewModel.VisibilityModel(
+                    visibility = View.GONE,
+                    shouldAnimateChange = false,
+                )
+        )
+    if (clockVisibilityModel.value.visibility == View.VISIBLE) {
+        Box(modifier = Modifier.fillMaxSize().alpha(0.5f), contentAlignment = Alignment.Center) {
+            RetroText(text = "COMPOSE->BAR")
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 366ea35..4277a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -19,6 +19,7 @@
 import android.view.View
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -38,7 +39,7 @@
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
 import com.android.systemui.statusbar.pipeline.shared.domain.interactor.CollapsedStatusBarInteractor
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel.VisibilityModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -61,7 +62,7 @@
  * [StatusBarHideIconsForBouncerManager]. We should move those pieces of logic to this class instead
  * so that it's all in one place and easily testable outside of the fragment.
  */
-interface CollapsedStatusBarViewModel {
+interface HomeStatusBarViewModel {
     /**
      * True if the device is currently transitioning from lockscreen to occluded and false
      * otherwise.
@@ -116,19 +117,20 @@
 }
 
 @SysUISingleton
-class CollapsedStatusBarViewModelImpl
+class HomeStatusBarViewModelImpl
 @Inject
 constructor(
     collapsedStatusBarInteractor: CollapsedStatusBarInteractor,
     private val lightsOutInteractor: LightsOutInteractor,
     private val notificationsInteractor: ActiveNotificationsInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    keyguardInteractor: KeyguardInteractor,
     sceneInteractor: SceneInteractor,
     sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
     shadeInteractor: ShadeInteractor,
     ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
     @Application coroutineScope: CoroutineScope,
-) : CollapsedStatusBarViewModel {
+) : HomeStatusBarViewModel {
     override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
         keyguardTransitionInteractor
             .isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED))
@@ -184,29 +186,43 @@
             // TODO(b/364360986): Add edge cases, like secure camera launch.
         }
 
-    private val isHomeScreenStatusBarAllowed: Flow<Boolean> =
+    private val isHomeStatusBarAllowed: Flow<Boolean> =
         if (SceneContainerFlag.isEnabled) {
             isHomeStatusBarAllowedByScene
         } else {
             isHomeScreenStatusBarAllowedLegacy
         }
 
+    private val shouldHomeStatusBarBeVisible =
+        combine(isHomeStatusBarAllowed, keyguardInteractor.isSecureCameraActive) {
+            isHomeStatusBarAllowed,
+            isSecureCameraActive ->
+            // When launching the camera over the lockscreen, the status icons would typically
+            // become visible momentarily before animating out, since we're not yet aware that the
+            // launching camera activity is fullscreen. Even once the activity finishes launching,
+            // it takes a short time before WM decides that the top app wants to hide the icons and
+            // tells us to hide them.
+            // To ensure that this high-visibility animation is smooth, keep the icons hidden during
+            // a camera launch. See b/257292822.
+            isHomeStatusBarAllowed && !isSecureCameraActive
+        }
+
     override val isClockVisible: Flow<VisibilityModel> =
         combine(
-            isHomeScreenStatusBarAllowed,
+            shouldHomeStatusBarBeVisible,
             collapsedStatusBarInteractor.visibilityViaDisableFlags,
-        ) { isStatusBarAllowed, visibilityViaDisableFlags ->
-            val showClock = isStatusBarAllowed && visibilityViaDisableFlags.isClockAllowed
+        ) { shouldStatusBarBeVisible, visibilityViaDisableFlags ->
+            val showClock = shouldStatusBarBeVisible && visibilityViaDisableFlags.isClockAllowed
             // TODO(b/364360986): Take CollapsedStatusBarFragment.clockHiddenMode into account.
             VisibilityModel(showClock.toVisibilityInt(), visibilityViaDisableFlags.animate)
         }
     override val isNotificationIconContainerVisible: Flow<VisibilityModel> =
         combine(
-            isHomeScreenStatusBarAllowed,
+            shouldHomeStatusBarBeVisible,
             collapsedStatusBarInteractor.visibilityViaDisableFlags,
-        ) { isStatusBarAllowed, visibilityViaDisableFlags ->
+        ) { shouldStatusBarBeVisible, visibilityViaDisableFlags ->
             val showNotificationIconContainer =
-                isStatusBarAllowed && visibilityViaDisableFlags.areNotificationIconsAllowed
+                shouldStatusBarBeVisible && visibilityViaDisableFlags.areNotificationIconsAllowed
             VisibilityModel(
                 showNotificationIconContainer.toVisibilityInt(),
                 visibilityViaDisableFlags.animate,
@@ -214,10 +230,11 @@
         }
     override val isSystemInfoVisible: Flow<VisibilityModel> =
         combine(
-            isHomeScreenStatusBarAllowed,
+            shouldHomeStatusBarBeVisible,
             collapsedStatusBarInteractor.visibilityViaDisableFlags,
-        ) { isStatusBarAllowed, visibilityViaDisableFlags ->
-            val showSystemInfo = isStatusBarAllowed && visibilityViaDisableFlags.isSystemInfoAllowed
+        ) { shouldStatusBarBeVisible, visibilityViaDisableFlags ->
+            val showSystemInfo =
+                shouldStatusBarBeVisible && visibilityViaDisableFlags.isSystemInfoAllowed
             VisibilityModel(showSystemInfo.toVisibilityInt(), visibilityViaDisableFlags.animate)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt
new file mode 100644
index 0000000..32b476b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.app.NotificationManager
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.modes.shared.ModesUi
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Cleanup task that deletes the obsolete "Gaming" AutomaticZenRule that was created by SystemUI in
+ * the faraway past, and still exists on some devices through upgrades or B&R.
+ */
+// TODO: b/372874878 - Remove this thing once it has run long enough
+class ZenModesCleanupStartable
+@Inject
+constructor(
+    @Application private val applicationCoroutineScope: CoroutineScope,
+    @Background private val bgContext: CoroutineContext,
+    val notificationManager: NotificationManager,
+) : CoreStartable {
+
+    override fun start() {
+        if (!ModesUi.isEnabled) {
+            return
+        }
+        applicationCoroutineScope.launch { deleteObsoleteGamingMode() }
+    }
+
+    private suspend fun deleteObsoleteGamingMode() {
+        withContext(bgContext) {
+            val allRules = notificationManager.automaticZenRules
+            val gamingModeEntry =
+                allRules.entries.firstOrNull { entry ->
+                    entry.value.packageName == "com.android.systemui" &&
+                        entry.value.conditionId?.toString() ==
+                            "android-app://com.android.systemui/game-mode-dnd-controller"
+                }
+            if (gamingModeEntry != null) {
+                notificationManager.removeAutomaticZenRule(gamingModeEntry.key)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index daba109..9839f9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -16,10 +16,12 @@
 
 package com.android.systemui.statusbar.policy.domain.interactor
 
+import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
 import android.content.Context
 import android.provider.Settings
 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import android.service.notification.ZenPolicy.STATE_DISALLOW
 import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST
 import android.util.Log
 import androidx.concurrent.futures.await
@@ -115,6 +117,26 @@
             .flowOn(bgDispatcher)
             .distinctUntilChanged()
 
+    val activeModesBlockingEverything: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
+        mode.interruptionFilter == INTERRUPTION_FILTER_NONE
+    }
+
+    val activeModesBlockingMedia: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
+        mode.policy.priorityCategoryMedia == STATE_DISALLOW
+    }
+
+    val activeModesBlockingAlarms: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
+        mode.policy.priorityCategoryAlarms == STATE_DISALLOW
+    }
+
+    private fun getFilteredActiveModesFlow(predicate: (ZenMode) -> Boolean): Flow<ActiveZenModes> {
+        return modes
+            .map { modes -> modes.filter { mode -> predicate(mode) } }
+            .map { modes -> buildActiveZenModes(modes) }
+            .flowOn(bgDispatcher)
+            .distinctUntilChanged()
+    }
+
     suspend fun getActiveModes() = buildActiveZenModes(zenModeRepository.getModes())
 
     private suspend fun buildActiveZenModes(modes: List<ZenMode>): ActiveZenModes {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
index 421e5c4..ae0e76f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
@@ -16,10 +16,13 @@
 
 package com.android.systemui.statusbar.window
 
+import android.content.Context
 import android.view.View
 import android.view.ViewGroup
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
 import java.util.Optional
 
 /** Encapsulates all logic for the status bar window state management. */
@@ -73,4 +76,12 @@
      *   this#setForceStatusBarVisible} together and use some sort of ranking system instead.
      */
     fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean)
+
+    interface Factory {
+        fun create(
+            context: Context,
+            viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+            statusBarConfigurationController: StatusBarConfigurationController,
+        ): StatusBarWindowController
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
index 1ee7cf3..e4c6737 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
@@ -54,6 +54,8 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.res.R;
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.window.StatusBarWindowModule.InternalWindowViewInflater;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -74,13 +76,14 @@
 
     private final Context mContext;
     private final ViewCaptureAwareWindowManager mWindowManager;
+    private final StatusBarConfigurationController mStatusBarConfigurationController;
     private final IWindowManager mIWindowManager;
     private final StatusBarContentInsetsProvider mContentInsetsProvider;
     private int mBarHeight = -1;
     private final State mCurrentState = new State();
     private boolean mIsAttached;
 
-    private final ViewGroup mStatusBarWindowView;
+    private final StatusBarWindowView mStatusBarWindowView;
     private final FragmentService mFragmentService;
     // The container in which we should run launch animations started from the status bar and
     //   expanding into the opening window.
@@ -94,12 +97,14 @@
             @Assisted Context context,
             @InternalWindowViewInflater StatusBarWindowViewInflater statusBarWindowViewInflater,
             @Assisted ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+            @Assisted StatusBarConfigurationController statusBarConfigurationController,
             IWindowManager iWindowManager,
             StatusBarContentInsetsProvider contentInsetsProvider,
             FragmentService fragmentService,
             Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
         mContext = context;
         mWindowManager = viewCaptureAwareWindowManager;
+        mStatusBarConfigurationController = statusBarConfigurationController;
         mIWindowManager = iWindowManager;
         mContentInsetsProvider = contentInsetsProvider;
         mStatusBarWindowView = statusBarWindowViewInflater.inflate(context);
@@ -141,6 +146,10 @@
 
     @Override
     public void attach() {
+        if (StatusBarConnectedDisplays.isEnabled()) {
+            mStatusBarWindowView.setStatusBarConfigurationController(
+                    mStatusBarConfigurationController);
+        }
         // Now that the status bar window encompasses the sliding panel and its
         // translucent backdrop, the entire thing is made TRANSLUCENT and is
         // hardware-accelerated.
@@ -354,11 +363,14 @@
     }
 
     @AssistedFactory
-    public interface Factory {
+    public interface Factory extends StatusBarWindowController.Factory {
         /** Creates a new instance. */
+        @NonNull
+        @Override
         StatusBarWindowControllerImpl create(
-                Context context,
-                ViewCaptureAwareWindowManager viewCaptureAwareWindowManager);
+                @NonNull Context context,
+                @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+                @NonNull StatusBarConfigurationController statusBarConfigurationController);
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
new file mode 100644
index 0000000..d83a237
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.content.Context
+import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Store that allows to retrieve per display instances of [StatusBarWindowController]. */
+interface StatusBarWindowControllerStore : PerDisplayStore<StatusBarWindowController>
+
+@SysUISingleton
+class MultiDisplayStatusBarWindowControllerStore
+@Inject
+constructor(
+    @Background backgroundApplicationScope: CoroutineScope,
+    private val controllerFactory: StatusBarWindowController.Factory,
+    private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
+    private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory,
+    private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
+    displayRepository: DisplayRepository,
+) :
+    StatusBarWindowControllerStore,
+    PerDisplayStoreImpl<StatusBarWindowController>(backgroundApplicationScope, displayRepository) {
+
+    init {
+        StatusBarConnectedDisplays.assertInNewMode()
+    }
+
+    override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController {
+        val statusBarDisplayContext =
+            displayWindowPropertiesRepository.get(
+                displayId = displayId,
+                windowType = WindowManager.LayoutParams.TYPE_STATUS_BAR,
+            )
+        val viewCaptureAwareWindowManager =
+            viewCaptureAwareWindowManagerFactory.create(statusBarDisplayContext.windowManager)
+        return controllerFactory.create(
+            statusBarDisplayContext.context,
+            viewCaptureAwareWindowManager,
+            statusBarConfigurationControllerStore.forDisplay(displayId),
+        )
+    }
+
+    override val instanceClass = StatusBarWindowController::class.java
+}
+
+@SysUISingleton
+class SingleDisplayStatusBarWindowControllerStore
+@Inject
+constructor(
+    context: Context,
+    viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+    factory: StatusBarWindowControllerImpl.Factory,
+    statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
+) :
+    StatusBarWindowControllerStore,
+    PerDisplayStore<StatusBarWindowController> by SingleDisplayStore(
+        factory.create(
+            context,
+            viewCaptureAwareWindowManager,
+            statusBarConfigurationControllerStore.defaultDisplay,
+        )
+    ) {
+
+    init {
+        StatusBarConnectedDisplays.assertInLegacyMode()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
index d696979..3f6ef16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -22,6 +22,7 @@
 import static android.view.WindowInsets.Type.systemBars;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.util.AttributeSet;
 import android.view.DisplayCutout;
@@ -30,8 +31,16 @@
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.compose.ComposeInitializer;
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
+
 /**
  * Status bar view.
+ * We now extend WindowRootView so that we can host Compose views
  */
 public class StatusBarWindowView extends FrameLayout {
 
@@ -44,12 +53,49 @@
 
     private float mTouchDownY = 0;
 
+    @Nullable private StatusBarConfigurationController mConfigurationController;
+
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setClipChildren(false);
     }
 
     @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (StatusBarSimpleFragment.isEnabled()) {
+            ComposeInitializer.INSTANCE.onAttachedToWindow(this);
+        }
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        if (StatusBarSimpleFragment.isEnabled()) {
+            ComposeInitializer.INSTANCE.onDetachedFromWindow(this);
+        }
+    }
+
+    /**
+     * Sets the {@link StatusBarConfigurationController} that is associated with the display that
+     * this view is attached to.
+     */
+    public void setStatusBarConfigurationController(
+            @NonNull StatusBarConfigurationController configurationController) {
+        mConfigurationController = configurationController;
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        StatusBarConfigurationController configurationController = mConfigurationController;
+        if (configurationController != null) {
+            configurationController.onConfigurationChanged(newConfig);
+        }
+    }
+
+    @Override
     public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
         final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
         mLeftInset = insets.left;
@@ -89,8 +135,8 @@
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            if (child.getLayoutParams() instanceof LayoutParams) {
-                LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (child.getLayoutParams() instanceof FrameLayout.LayoutParams) {
+                FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
                 if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset
                         || lp.topMargin != mTopInset) {
                     lp.rightMargin = mRightInset;
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
index 411ff8b..e89a31f 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
@@ -16,20 +16,21 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
+import android.content.res.Resources
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
 import com.airbnb.lottie.compose.rememberLottieDynamicProperties
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
 import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty
 import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureMonitor
+import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
 
 @Composable
-fun BackGestureTutorialScreen(
-    onDoneButtonClicked: () -> Unit,
-    onBack: () -> Unit,
-) {
+fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
     val screenConfig =
         TutorialScreenConfig(
             colors = rememberScreenColors(),
@@ -38,26 +39,30 @@
                     titleResId = R.string.touchpad_back_gesture_action_title,
                     bodyResId = R.string.touchpad_back_gesture_guidance,
                     titleSuccessResId = R.string.touchpad_back_gesture_success_title,
-                    bodySuccessResId = R.string.touchpad_back_gesture_success_body
+                    bodySuccessResId = R.string.touchpad_back_gesture_success_body,
                 ),
             animations =
                 TutorialScreenConfig.Animations(
                     educationResId = R.raw.trackpad_back_edu,
-                    successResId = R.raw.trackpad_back_success
-                )
+                    successResId = R.raw.trackpad_back_success,
+                ),
         )
-    val gestureMonitorProvider =
-        DistanceBasedGestureMonitorProvider(
-            monitorFactory = { distanceThresholdPx, gestureStateCallback ->
-                BackGestureMonitor(distanceThresholdPx, gestureStateCallback)
-            }
+    val recognizer = rememberBackGestureRecognizer(LocalContext.current.resources)
+    GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack)
+}
+
+@Composable
+private fun rememberBackGestureRecognizer(resources: Resources): GestureRecognizer {
+    val distance =
+        resources.getDimensionPixelSize(
+            com.android.internal.R.dimen.system_gestures_distance_threshold
         )
-    GestureTutorialScreen(screenConfig, gestureMonitorProvider, onDoneButtonClicked, onBack)
+    return remember(distance) { BackGestureRecognizer(distance) }
 }
 
 @Composable
 private fun rememberScreenColors(): TutorialScreenConfig.Colors {
-    val onTertiary = LocalAndroidColorScheme.current.onTertiary
+    val onTertiary = MaterialTheme.colorScheme.onTertiary
     val onTertiaryFixed = LocalAndroidColorScheme.current.onTertiaryFixed
     val onTertiaryFixedVariant = LocalAndroidColorScheme.current.onTertiaryFixedVariant
     val tertiaryFixedDim = LocalAndroidColorScheme.current.tertiaryFixedDim
@@ -66,7 +71,7 @@
             rememberColorFilterProperty(".tertiaryFixedDim", tertiaryFixedDim),
             rememberColorFilterProperty(".onTertiaryFixed", onTertiaryFixed),
             rememberColorFilterProperty(".onTertiary", onTertiary),
-            rememberColorFilterProperty(".onTertiaryFixedVariant", onTertiaryFixedVariant)
+            rememberColorFilterProperty(".onTertiaryFixedVariant", onTertiaryFixedVariant),
         )
     val screenColors =
         remember(dynamicProperties) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
index 72389cd..7899f5b 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
-import android.content.res.Resources
 import androidx.activity.compose.BackHandler
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.tween
@@ -32,52 +31,22 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInteropFilter
-import androidx.compose.ui.platform.LocalContext
 import com.android.systemui.inputdevice.tutorial.ui.composable.ActionTutorialContent
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
 import com.android.systemui.touchpad.tutorial.ui.gesture.EasterEggGestureMonitor
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureHandler
-import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureMonitor
-
-interface GestureMonitorProvider {
-
-    @Composable
-    fun rememberGestureMonitor(
-        resources: Resources,
-        gestureStateChangedCallback: (GestureState) -> Unit,
-    ): TouchpadGestureMonitor
-}
-
-typealias gestureStateCallback = (GestureState) -> Unit
-
-class DistanceBasedGestureMonitorProvider(
-    val monitorFactory: (Int, gestureStateCallback) -> TouchpadGestureMonitor
-) : GestureMonitorProvider {
-
-    @Composable
-    override fun rememberGestureMonitor(
-        resources: Resources,
-        gestureStateChangedCallback: (GestureState) -> Unit,
-    ): TouchpadGestureMonitor {
-        val distanceThresholdPx =
-            resources.getDimensionPixelSize(
-                com.android.internal.R.dimen.system_gestures_distance_threshold
-            )
-        return remember(distanceThresholdPx) {
-            monitorFactory(distanceThresholdPx, gestureStateChangedCallback)
-        }
-    }
-}
 
 fun GestureState.toTutorialActionState(): TutorialActionState {
     return when (this) {
         NotStarted -> TutorialActionState.NotStarted
-        is InProgress -> TutorialActionState.InProgress()
+        // progress is disabled for now as views are not ready to handle varying progress
+        is InProgress -> TutorialActionState.InProgress(0f)
         Finished -> TutorialActionState.Finished
     }
 }
@@ -85,21 +54,19 @@
 @Composable
 fun GestureTutorialScreen(
     screenConfig: TutorialScreenConfig,
-    gestureMonitorProvider: GestureMonitorProvider,
+    gestureRecognizer: GestureRecognizer,
     onDoneButtonClicked: () -> Unit,
     onBack: () -> Unit,
 ) {
     BackHandler(onBack = onBack)
     var gestureState: GestureState by remember { mutableStateOf(NotStarted) }
     var easterEggTriggered by remember { mutableStateOf(false) }
-    val gestureMonitor =
-        gestureMonitorProvider.rememberGestureMonitor(
-            resources = LocalContext.current.resources,
-            gestureStateChangedCallback = { gestureState = it },
-        )
+    LaunchedEffect(gestureRecognizer) {
+        gestureRecognizer.addGestureStateCallback { gestureState = it }
+    }
     val easterEggMonitor = EasterEggGestureMonitor { easterEggTriggered = true }
     val gestureHandler =
-        remember(gestureMonitor) { TouchpadGestureHandler(gestureMonitor, easterEggMonitor) }
+        remember(gestureRecognizer) { TouchpadGestureHandler(gestureRecognizer, easterEggMonitor) }
     TouchpadGesturesHandlingBox(
         gestureHandler,
         gestureState,
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index f2fec5f..3ddf760 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
@@ -16,20 +16,20 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
+import android.content.res.Resources
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
 import com.airbnb.lottie.compose.rememberLottieDynamicProperties
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
 import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty
 import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureMonitor
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer
 
 @Composable
-fun HomeGestureTutorialScreen(
-    onDoneButtonClicked: () -> Unit,
-    onBack: () -> Unit,
-) {
+fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
     val screenConfig =
         TutorialScreenConfig(
             colors = rememberScreenColors(),
@@ -38,21 +38,25 @@
                     titleResId = R.string.touchpad_home_gesture_action_title,
                     bodyResId = R.string.touchpad_home_gesture_guidance,
                     titleSuccessResId = R.string.touchpad_home_gesture_success_title,
-                    bodySuccessResId = R.string.touchpad_home_gesture_success_body
+                    bodySuccessResId = R.string.touchpad_home_gesture_success_body,
                 ),
             animations =
                 TutorialScreenConfig.Animations(
                     educationResId = R.raw.trackpad_home_edu,
-                    successResId = R.raw.trackpad_home_success
-                )
+                    successResId = R.raw.trackpad_home_success,
+                ),
         )
-    val gestureMonitorProvider =
-        DistanceBasedGestureMonitorProvider(
-            monitorFactory = { distanceThresholdPx, gestureStateCallback ->
-                HomeGestureMonitor(distanceThresholdPx, gestureStateCallback)
-            }
+    val recognizer = rememberHomeGestureRecognizer(LocalContext.current.resources)
+    GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack)
+}
+
+@Composable
+private fun rememberHomeGestureRecognizer(resources: Resources): GestureRecognizer {
+    val distance =
+        resources.getDimensionPixelSize(
+            com.android.internal.R.dimen.system_gestures_distance_threshold
         )
-    GestureTutorialScreen(screenConfig, gestureMonitorProvider, onDoneButtonClicked, onBack)
+    return remember(distance) { HomeGestureRecognizer(distance) }
 }
 
 @Composable
@@ -64,7 +68,7 @@
         rememberLottieDynamicProperties(
             rememberColorFilterProperty(".primaryFixedDim", primaryFixedDim),
             rememberColorFilterProperty(".onPrimaryFixed", onPrimaryFixed),
-            rememberColorFilterProperty(".onPrimaryFixedVariant", onPrimaryFixedVariant)
+            rememberColorFilterProperty(".onPrimaryFixedVariant", onPrimaryFixedVariant),
         )
     val screenColors =
         remember(dynamicProperties) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
index b2fb6cd..30a21bf 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
@@ -19,20 +19,17 @@
 import android.content.res.Resources
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
 import com.airbnb.lottie.compose.rememberLottieDynamicProperties
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
 import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty
 import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
-import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureMonitor
-import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureMonitor
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer
 
 @Composable
-fun RecentAppsGestureTutorialScreen(
-    onDoneButtonClicked: () -> Unit,
-    onBack: () -> Unit,
-) {
+fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
     val screenConfig =
         TutorialScreenConfig(
             colors = rememberScreenColors(),
@@ -41,37 +38,26 @@
                     titleResId = R.string.touchpad_recent_apps_gesture_action_title,
                     bodyResId = R.string.touchpad_recent_apps_gesture_guidance,
                     titleSuccessResId = R.string.touchpad_recent_apps_gesture_success_title,
-                    bodySuccessResId = R.string.touchpad_recent_apps_gesture_success_body
+                    bodySuccessResId = R.string.touchpad_recent_apps_gesture_success_body,
                 ),
             animations =
                 TutorialScreenConfig.Animations(
                     educationResId = R.raw.trackpad_recent_apps_edu,
-                    successResId = R.raw.trackpad_recent_apps_success
-                )
+                    successResId = R.raw.trackpad_recent_apps_success,
+                ),
         )
-    val gestureMonitorProvider =
-        object : GestureMonitorProvider {
-            @Composable
-            override fun rememberGestureMonitor(
-                resources: Resources,
-                gestureStateChangedCallback: (GestureState) -> Unit
-            ): TouchpadGestureMonitor {
-                val distanceThresholdPx =
-                    resources.getDimensionPixelSize(
-                        com.android.internal.R.dimen.system_gestures_distance_threshold
-                    )
-                val velocityThresholdPxPerMs =
-                    resources.getDimension(R.dimen.touchpad_recent_apps_gesture_velocity_threshold)
-                return remember(distanceThresholdPx, velocityThresholdPxPerMs) {
-                    RecentAppsGestureMonitor(
-                        distanceThresholdPx,
-                        gestureStateChangedCallback,
-                        velocityThresholdPxPerMs
-                    )
-                }
-            }
-        }
-    GestureTutorialScreen(screenConfig, gestureMonitorProvider, onDoneButtonClicked, onBack)
+    val recognizer = rememberRecentAppsGestureRecognizer(LocalContext.current.resources)
+    GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack)
+}
+
+@Composable
+private fun rememberRecentAppsGestureRecognizer(resources: Resources): GestureRecognizer {
+    val distance =
+        resources.getDimensionPixelSize(
+            com.android.internal.R.dimen.system_gestures_distance_threshold
+        )
+    val velocity = resources.getDimension(R.dimen.touchpad_recent_apps_gesture_velocity_threshold)
+    return remember(distance, velocity) { RecentAppsGestureRecognizer(distance, velocity) }
 }
 
 @Composable
@@ -83,7 +69,7 @@
         rememberLottieDynamicProperties(
             rememberColorFilterProperty(".secondaryFixedDim", secondaryFixedDim),
             rememberColorFilterProperty(".onSecondaryFixed", onSecondaryFixed),
-            rememberColorFilterProperty(".onSecondaryFixedVariant", onSecondaryFixedVariant)
+            rememberColorFilterProperty(".onSecondaryFixedVariant", onSecondaryFixedVariant),
         )
     val screenColors =
         remember(dynamicProperties) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 94e19de..c209311 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -39,11 +39,14 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.input.pointer.pointerInteropFilter
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.vectorResource
 import androidx.compose.ui.unit.dp
 import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton
 import com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe
+import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe
 
 @Composable
 fun TutorialSelectionScreen(
@@ -55,7 +58,16 @@
     Column(
         verticalArrangement = Arrangement.Center,
         modifier =
-            Modifier.background(color = MaterialTheme.colorScheme.surfaceContainer).fillMaxSize(),
+            Modifier.background(color = MaterialTheme.colorScheme.surfaceContainer)
+                .fillMaxSize()
+                .pointerInteropFilter(
+                    onTouchEvent = { event ->
+                        // Because of window flag we're intercepting 3 and 4-finger swipes.
+                        // Although we don't handle them in this screen, we want to disable them so
+                        // that user is not clicking button by mistake by performing these swipes.
+                        isThreeFingerTouchpadSwipe(event) || isFourFingerTouchpadSwipe(event)
+                    }
+                ),
     ) {
         TutorialSelectionButtons(
             onBackTutorialClicked = onBackTutorialClicked,
@@ -84,7 +96,7 @@
     ) {
         TutorialButton(
             text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
-            icon = Icons.AutoMirrored.Outlined.ArrowBack,
+            icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
             iconColor = MaterialTheme.colorScheme.onPrimary,
             onClick = onHomeTutorialClicked,
             backgroundColor = MaterialTheme.colorScheme.primary,
@@ -92,7 +104,7 @@
         )
         TutorialButton(
             text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
-            icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
+            icon = Icons.AutoMirrored.Outlined.ArrowBack,
             iconColor = MaterialTheme.colorScheme.onTertiary,
             onClick = onBackTutorialClicked,
             backgroundColor = MaterialTheme.colorScheme.tertiary,
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
deleted file mode 100644
index 084da2c..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui.gesture
-
-import kotlin.math.abs
-
-/** Monitors for touchpad back gesture, that is three fingers swiping left or right */
-class BackGestureMonitor(
-    override val gestureDistanceThresholdPx: Int,
-    override val gestureStateChangedCallback: (GestureState) -> Unit
-) :
-    TouchpadGestureMonitor by ThreeFingerDistanceBasedGestureMonitor(
-        gestureDistanceThresholdPx = gestureDistanceThresholdPx,
-        gestureStateChangedCallback = gestureStateChangedCallback,
-        donePredicate =
-            object : GestureDonePredicate {
-                override fun wasGestureDone(
-                    startX: Float,
-                    startY: Float,
-                    endX: Float,
-                    endY: Float
-                ): Boolean {
-                    val distance = abs(endX - startX)
-                    return distance >= gestureDistanceThresholdPx
-                }
-            }
-    )
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
new file mode 100644
index 0000000..80f8003
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.util.MathUtils
+import android.view.MotionEvent
+import kotlin.math.abs
+
+/**
+ * Recognizes touchpad back gesture, that is - using three fingers on touchpad - swiping left or
+ * right.
+ */
+class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer {
+
+    private val distanceTracker = DistanceTracker()
+    private var gestureStateChangedCallback: (GestureState) -> Unit = {}
+
+    override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
+        gestureStateChangedCallback = callback
+    }
+
+    override fun accept(event: MotionEvent) {
+        if (!isThreeFingerTouchpadSwipe(event)) return
+        val gestureState = distanceTracker.processEvent(event)
+        updateGestureState(
+            gestureStateChangedCallback,
+            gestureState,
+            isFinished = { abs(it.deltaX) >= gestureDistanceThresholdPx },
+            progress = { MathUtils.saturate(abs(it.deltaX / gestureDistanceThresholdPx)) },
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/DistanceTracker.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/DistanceTracker.kt
new file mode 100644
index 0000000..d482358
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/DistanceTracker.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+
+/**
+ * Tracks distance change for processed MotionEvents. Useful for recognizing gestures based on
+ * distance travelled instead of specific position on the screen.
+ */
+class DistanceTracker(var startX: Float = 0f, var startY: Float = 0f) {
+    fun processEvent(event: MotionEvent): DistanceGestureState? {
+        val action = event.actionMasked
+        return when (action) {
+            MotionEvent.ACTION_DOWN -> {
+                startX = event.x
+                startY = event.y
+                Started(event.x, event.y)
+            }
+            MotionEvent.ACTION_MOVE -> Moving(event.x - startX, event.y - startY)
+            MotionEvent.ACTION_UP -> Finished(event.x - startX, event.y - startY)
+            else -> null
+        }
+    }
+}
+
+sealed class DistanceGestureState(val deltaX: Float, val deltaY: Float)
+
+class Started(deltaX: Float, deltaY: Float) : DistanceGestureState(deltaX, deltaY)
+
+class Moving(deltaX: Float, deltaY: Float) : DistanceGestureState(deltaX, deltaY)
+
+class Finished(deltaX: Float, deltaY: Float) : DistanceGestureState(deltaX, deltaY)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt
rename to packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
index 8774a92..d146268 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
@@ -17,17 +17,11 @@
 package com.android.systemui.touchpad.tutorial.ui.gesture
 
 import android.view.MotionEvent
+import java.util.function.Consumer
 
-/**
- * Monitor for touchpad gestures that calls [gestureStateChangedCallback] when [GestureState]
- * changes. All tracked motion events should be passed to [processTouchpadEvent]
- */
-interface TouchpadGestureMonitor {
-
-    val gestureDistanceThresholdPx: Int
-    val gestureStateChangedCallback: (GestureState) -> Unit
-
-    fun processTouchpadEvent(event: MotionEvent)
+/** Based on passed [MotionEvent]s recognizes different states of gesture and notifies callback. */
+interface GestureRecognizer : Consumer<MotionEvent> {
+    fun addGestureStateCallback(callback: (GestureState) -> Unit)
 }
 
 fun isThreeFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 3)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
new file mode 100644
index 0000000..f194677
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+/** Helper function for gesture recognizers to have common state triggering logic */
+inline fun updateGestureState(
+    gestureStateChangedCallback: (GestureState) -> Unit,
+    gestureState: DistanceGestureState?,
+    isFinished: (Finished) -> Boolean,
+    progress: (Moving) -> Float,
+) {
+    when (gestureState) {
+        is Finished -> {
+            if (isFinished(gestureState)) {
+                gestureStateChangedCallback(GestureState.Finished)
+            } else {
+                gestureStateChangedCallback(GestureState.NotStarted)
+            }
+        }
+        is Moving -> {
+            gestureStateChangedCallback(GestureState.InProgress(progress(gestureState)))
+        }
+        is Started -> gestureStateChangedCallback(GestureState.InProgress())
+        else -> {}
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
deleted file mode 100644
index a9aa5c8..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui.gesture
-
-/** Monitors for touchpad home gesture, that is three fingers swiping up */
-class HomeGestureMonitor(
-    override val gestureDistanceThresholdPx: Int,
-    override val gestureStateChangedCallback: (GestureState) -> Unit
-) :
-    TouchpadGestureMonitor by ThreeFingerDistanceBasedGestureMonitor(
-        gestureDistanceThresholdPx = gestureDistanceThresholdPx,
-        gestureStateChangedCallback = gestureStateChangedCallback,
-        donePredicate =
-            object : GestureDonePredicate {
-                override fun wasGestureDone(
-                    startX: Float,
-                    startY: Float,
-                    endX: Float,
-                    endY: Float
-                ): Boolean {
-                    val distance = startY - endY
-                    return distance >= gestureDistanceThresholdPx
-                }
-            }
-    )
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
new file mode 100644
index 0000000..2b84a4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.util.MathUtils
+import android.view.MotionEvent
+
+/** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */
+class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer {
+
+    private val distanceTracker = DistanceTracker()
+    private var gestureStateChangedCallback: (GestureState) -> Unit = {}
+
+    override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
+        gestureStateChangedCallback = callback
+    }
+
+    override fun accept(event: MotionEvent) {
+        if (!isThreeFingerTouchpadSwipe(event)) return
+        val gestureState = distanceTracker.processEvent(event)
+        updateGestureState(
+            gestureStateChangedCallback,
+            gestureState,
+            isFinished = { -it.deltaY >= gestureDistanceThresholdPx },
+            progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) },
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt
deleted file mode 100644
index ca3880a..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui.gesture
-
-import android.view.MotionEvent
-import androidx.compose.ui.input.pointer.util.VelocityTracker1D
-import kotlin.math.abs
-
-/**
- * Monitors recent apps gesture completion. That is - using three fingers on touchpad - swipe up
- * over some distance threshold and then slow down gesture before fingers are lifted. Implementation
- * is based on [com.android.quickstep.util.TriggerSwipeUpTouchTracker]
- */
-class RecentAppsGestureMonitor(
-    override val gestureDistanceThresholdPx: Int,
-    override val gestureStateChangedCallback: (GestureState) -> Unit,
-    private val velocityThresholdPxPerMs: Float,
-    private val velocityTracker: VelocityTracker1D = VelocityTracker1D(isDataDifferential = false),
-) : TouchpadGestureMonitor {
-
-    private var xStart = 0f
-    private var yStart = 0f
-
-    override fun processTouchpadEvent(event: MotionEvent) {
-        val action = event.actionMasked
-        velocityTracker.addDataPoint(event.eventTime, event.y)
-        when (action) {
-            MotionEvent.ACTION_DOWN -> {
-                if (isThreeFingerTouchpadSwipe(event)) {
-                    xStart = event.x
-                    yStart = event.y
-                    gestureStateChangedCallback(GestureState.InProgress())
-                }
-            }
-            MotionEvent.ACTION_UP -> {
-                if (isThreeFingerTouchpadSwipe(event) && isRecentAppsGesture(event)) {
-                    gestureStateChangedCallback(GestureState.Finished)
-                } else {
-                    gestureStateChangedCallback(GestureState.NotStarted)
-                }
-                velocityTracker.resetTracking()
-            }
-            MotionEvent.ACTION_CANCEL -> {
-                velocityTracker.resetTracking()
-            }
-        }
-    }
-
-    private fun isRecentAppsGesture(event: MotionEvent): Boolean {
-        // below is trying to mirror behavior of TriggerSwipeUpTouchTracker#onGestureEnd.
-        // We're diving velocity by 1000, to have the same unit of measure: pixels/ms.
-        val swipeDistance = yStart - event.y
-        val velocity = velocityTracker.calculateVelocity() / 1000
-        return swipeDistance >= gestureDistanceThresholdPx &&
-            abs(velocity) <= velocityThresholdPxPerMs
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
new file mode 100644
index 0000000..69b7c5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.util.MathUtils
+import android.view.MotionEvent
+import kotlin.math.abs
+
+/**
+ * Recognizes recent apps gesture, that is - using three fingers on touchpad - swipe up over some
+ * distance threshold and then slow down gesture before fingers are lifted. Implementation is based
+ * on [com.android.quickstep.util.TriggerSwipeUpTouchTracker]
+ */
+class RecentAppsGestureRecognizer(
+    private val gestureDistanceThresholdPx: Int,
+    private val velocityThresholdPxPerMs: Float,
+    private val distanceTracker: DistanceTracker = DistanceTracker(),
+    private val velocityTracker: VerticalVelocityTracker = VerticalVelocityTracker(),
+) : GestureRecognizer {
+
+    private var gestureStateChangedCallback: (GestureState) -> Unit = {}
+
+    override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
+        gestureStateChangedCallback = callback
+    }
+
+    override fun accept(event: MotionEvent) {
+        if (!isThreeFingerTouchpadSwipe(event)) return
+        val gestureState = distanceTracker.processEvent(event)
+        velocityTracker.accept(event)
+
+        updateGestureState(
+            gestureStateChangedCallback,
+            gestureState,
+            isFinished = { state ->
+                -state.deltaY >= gestureDistanceThresholdPx &&
+                    abs(velocityTracker.calculateVelocity().value) <= velocityThresholdPxPerMs
+            },
+            progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) },
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerDistanceBasedGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerDistanceBasedGestureMonitor.kt
deleted file mode 100644
index 12bcaea..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerDistanceBasedGestureMonitor.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui.gesture
-
-import android.view.MotionEvent
-
-interface GestureDonePredicate {
-    /**
-     * Should return if gesture was finished. The only events this predicate receives are ACTION_UP.
-     */
-    fun wasGestureDone(startX: Float, startY: Float, endX: Float, endY: Float): Boolean
-}
-
-/**
- * Common implementation for three-finger gesture monitors that are only distance-based. E.g. recent
- * apps gesture is not only distance-based because it requires going over threshold distance and
- * slowing down the movement.
- */
-class ThreeFingerDistanceBasedGestureMonitor(
-    override val gestureDistanceThresholdPx: Int,
-    override val gestureStateChangedCallback: (GestureState) -> Unit,
-    private val donePredicate: GestureDonePredicate
-) : TouchpadGestureMonitor {
-
-    private var xStart = 0f
-    private var yStart = 0f
-
-    override fun processTouchpadEvent(event: MotionEvent) {
-        val action = event.actionMasked
-        when (action) {
-            MotionEvent.ACTION_DOWN -> {
-                if (isThreeFingerTouchpadSwipe(event)) {
-                    xStart = event.x
-                    yStart = event.y
-                    gestureStateChangedCallback(GestureState.InProgress())
-                }
-            }
-            MotionEvent.ACTION_UP -> {
-                if (isThreeFingerTouchpadSwipe(event)) {
-                    if (donePredicate.wasGestureDone(xStart, yStart, event.x, event.y)) {
-                        gestureStateChangedCallback(GestureState.Finished)
-                    } else {
-                        gestureStateChangedCallback(GestureState.NotStarted)
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
index 88671d4..21e2917 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
@@ -18,13 +18,14 @@
 
 import android.view.InputDevice
 import android.view.MotionEvent
+import java.util.function.Consumer
 
 /**
  * Allows listening to touchpadGesture and calling onDone when gesture was triggered. Can have all
  * motion events passed to [onMotionEvent] and will filter touchpad events accordingly
  */
 class TouchpadGestureHandler(
-    private val gestureMonitor: TouchpadGestureMonitor,
+    private val gestureRecognizer: Consumer<MotionEvent>,
     private val easterEggGestureMonitor: EasterEggGestureMonitor,
 ) {
 
@@ -40,7 +41,7 @@
             if (isTwoFingerSwipe(event)) {
                 easterEggGestureMonitor.processTouchpadEvent(event)
             } else {
-                gestureMonitor.processTouchpadEvent(event)
+                gestureRecognizer.accept(event)
             }
             true
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/VelocityTracker.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/VelocityTracker.kt
new file mode 100644
index 0000000..9b38eca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/VelocityTracker.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+import java.util.function.Consumer
+
+/** Velocity in pixels/ms. */
+@JvmInline value class Velocity(val value: Float)
+
+/**
+ * Tracks velocity for processed MotionEvents. Useful for recognizing gestures based on velocity.
+ */
+interface VelocityTracker : Consumer<MotionEvent> {
+
+    fun calculateVelocity(): Velocity
+}
+
+class VerticalVelocityTracker(
+    private val velocityTracker: VelocityTracker1D = VelocityTracker1D(isDataDifferential = false)
+) : VelocityTracker {
+
+    override fun accept(event: MotionEvent) {
+        val action = event.actionMasked
+        if (action == MotionEvent.ACTION_DOWN) {
+            velocityTracker.resetTracking()
+        }
+        velocityTracker.addDataPoint(event.eventTime, event.y)
+    }
+
+    /**
+     * Calculates velocity on demand - this calculation can be expensive so shouldn't be called
+     * after every event.
+     */
+    override fun calculateVelocity() = Velocity(velocityTracker.calculateVelocity() / 1000)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 2135817..d0817d7 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -61,7 +61,6 @@
 
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
 
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
         requestWindowFeature(Window.FEATURE_NO_TITLE);
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index b3e60e3..d4686e2 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.unfold
 
 import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
 import android.util.Log
 import com.android.app.tracing.TraceUtils.traceAsync
 import com.android.app.tracing.instantForTrack
@@ -72,7 +73,8 @@
     @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor,
     @Application private val applicationScope: CoroutineScope,
     private val displaySwitchLatencyLogger: DisplaySwitchLatencyLogger,
-    private val systemClock: SystemClock
+    private val systemClock: SystemClock,
+    private val deviceStateManager: DeviceStateManager
 ) : CoreStartable {
 
     private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher()
@@ -81,7 +83,7 @@
 
     @OptIn(ExperimentalCoroutinesApi::class)
     override fun start() {
-        if (!isDeviceFoldable(context)) {
+        if (!isDeviceFoldable(context.resources, deviceStateManager)) {
             return
         }
         applicationScope.launch(backgroundDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
index 33fa9b8..f806a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
 import com.android.systemui.util.Compile
+import com.android.systemui.util.Utils.isDeviceFoldable
 import java.util.Optional
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -51,18 +52,14 @@
     @UiBackground private val uiBgExecutor: Executor,
     private val context: Context,
     private val contentResolver: ContentResolver,
-    private val screenLifecycle: ScreenLifecycle
+    private val screenLifecycle: ScreenLifecycle,
 ) : ScreenLifecycle.Observer, TransitionProgressListener {
 
     private var folded: Boolean? = null
     private var isTransitionEnabled: Boolean? = null
     private val foldStateListener = FoldStateListener(context)
     private var unfoldInProgress = false
-    private val isFoldable: Boolean
-        get() =
-            context.resources
-                .getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
-                .isNotEmpty()
+    private val isFoldable: Boolean = isDeviceFoldable(context.resources, deviceStateManager)
 
     /** Registers for relevant events only if the device is foldable. */
     fun init() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index adf50a1..a6224dc 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.unfold
 
 import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
 import android.os.Trace
 import com.android.app.tracing.TraceStateLogger
 import com.android.systemui.CoreStartable
@@ -24,6 +25,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.unfold.data.repository.FoldStateRepository
 import com.android.systemui.unfold.system.DeviceStateRepository
+import com.android.systemui.util.Utils.isDeviceFoldable
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
@@ -42,13 +44,10 @@
     private val foldStateRepository: FoldStateRepository,
     @Application applicationScope: CoroutineScope,
     @Background private val coroutineContext: CoroutineContext,
-    private val deviceStateRepository: DeviceStateRepository
+    private val deviceStateRepository: DeviceStateRepository,
+    private val deviceStateManager: DeviceStateManager
 ) : CoreStartable {
-    private val isFoldable: Boolean
-        get() =
-            context.resources
-                .getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
-                .isNotEmpty()
+    private val isFoldable: Boolean = isDeviceFoldable(context.resources, deviceStateManager)
 
     private val bgScope = applicationScope.plus(coroutineContext)
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
index 8957fe1..09cef1e 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.user.legacyhelper.ui
 
 import android.content.Context
+import android.util.Log
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
 import com.android.systemui.res.R
@@ -32,6 +33,8 @@
  */
 object LegacyUserUiHelper {
 
+    private const val TAG = "LegacyUserUiHelper"
+
     @JvmStatic
     @DrawableRes
     fun getUserSwitcherActionIconResourceId(
@@ -67,7 +70,9 @@
         val resourceId: Int? = getGuestUserRecordNameResourceId(record)
         return when {
             resourceId != null -> context.getString(resourceId)
-            record.info != null -> checkNotNull(record.info.name)
+            record.info != null ->
+                record.info.name
+                    ?: "".also { Log.i(TAG, "Expected display name for: ${record.info}") }
             else ->
                 context.getString(
                     getUserSwitcherActionTextResourceId(
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 3953188..800d289 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -14,11 +14,17 @@
 
 package com.android.systemui.util;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+
 import android.Manifest;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.feature.flags.Flags;
 import android.provider.Settings;
 import android.view.DisplayCutout;
 
@@ -84,9 +90,23 @@
     /**
      * Returns {@code true} if the device is a foldable device
      */
-    public static boolean isDeviceFoldable(Context context) {
-        return context.getResources()
-                .getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+    public static boolean isDeviceFoldable(Resources resources,
+            DeviceStateManager deviceStateManager) {
+        if (Flags.deviceStatePropertyMigration()) {
+            List<DeviceState> deviceStates = deviceStateManager.getSupportedDeviceStates();
+            for (int i = 0; i < deviceStates.size(); i++) {
+                DeviceState state = deviceStates.get(i);
+                if (state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+                        || state.hasProperty(
+                        PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) {
+                    return true;
+                }
+            }
+            return false;
+        } else {
+            return resources.getIntArray(
+                    com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
index de92318..d87607d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
@@ -11,6 +11,7 @@
 import android.graphics.drawable.AnimatedVectorDrawable
 import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
 import android.util.Log
 import androidx.annotation.Px
 import com.android.app.tracing.traceSection
@@ -22,36 +23,35 @@
         const val TAG = "SysUiDrawableSize"
 
         /**
-         * Downscales passed Drawable to set maximum width and height. This will only
-         * be done for Drawables that can be downscaled non-destructively - e.g. animated
-         * and stateful drawables will no be downscaled.
+         * Downscales passed Drawable to set maximum width and height. This will only be done for
+         * Drawables that can be downscaled non-destructively - e.g. animated drawables, stateful
+         * drawables, and drawables with mixed-type layers will not be downscaled.
          *
-         * Downscaling will keep the aspect ratio.
-         * This method will not touch drawables that already fit into size specification.
+         * Downscaling will keep the aspect ratio. This method will not touch drawables that already
+         * fit into size specification.
          *
          * @param resources Resources on which to base the density of resized drawable.
          * @param drawable Drawable to downscale.
          * @param maxWidth Maximum width of the downscaled drawable.
          * @param maxHeight Maximum height of the downscaled drawable.
-         *
          * @return returns downscaled drawable if it's possible to downscale it or original if it's
-         *         not.
+         *   not.
          */
         @JvmStatic
         fun downscaleToSize(
             res: Resources,
             drawable: Drawable,
             @Px maxWidth: Int,
-            @Px maxHeight: Int
+            @Px maxHeight: Int,
         ): Drawable {
             traceSection("DrawableSize#downscaleToSize") {
                 // Bitmap drawables can contain big bitmaps as their content while sneaking it past
                 // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap
                 // size for those.
-                val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width
-                                    ?: drawable.intrinsicWidth
-                val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height
-                                    ?: drawable.intrinsicHeight
+                val originalWidth =
+                    (drawable as? BitmapDrawable)?.bitmap?.width ?: drawable.intrinsicWidth
+                val originalHeight =
+                    (drawable as? BitmapDrawable)?.bitmap?.height ?: drawable.intrinsicHeight
 
                 // Don't touch drawable if we can't resolve sizes for whatever reason.
                 if (originalWidth <= 0 || originalHeight <= 0) {
@@ -61,14 +61,18 @@
                 // Do not touch drawables that are already within bounds.
                 if (originalWidth < maxWidth && originalHeight < maxHeight) {
                     if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " +
-                                "to $maxWidth x $maxHeight")
+                        Log.d(
+                            TAG,
+                            "Not resizing $originalWidth x $originalHeight" +
+                                " " +
+                                "to $maxWidth x $maxHeight",
+                        )
                     }
 
                     return drawable
                 }
 
-                if (!isSimpleBitmap(drawable)) {
+                if (isComplicatedBitmap(drawable)) {
                     return drawable
                 }
 
@@ -80,19 +84,25 @@
                 val height = (originalHeight * scale).toInt()
 
                 if (width <= 0 || height <= 0) {
-                    Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " +
-                            "from $originalWidth x $originalHeight to invalid $width x $height.")
+                    Log.w(
+                        TAG,
+                        "Attempted to resize ${drawable.javaClass.simpleName} " +
+                            "from $originalWidth x $originalHeight to invalid $width x $height.",
+                    )
                     return drawable
                 }
 
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " +
-                            "from $originalWidth x $originalHeight to $width x $height")
+                    Log.d(
+                        TAG,
+                        "Resizing large drawable (${drawable.javaClass.simpleName}) " +
+                            "from $originalWidth x $originalHeight to $width x $height",
+                    )
                 }
 
                 // We want to keep existing config if it's more efficient than 32-bit RGB.
-                val config = (drawable as? BitmapDrawable)?.bitmap?.config
-                        ?: Bitmap.Config.ARGB_8888
+                val config =
+                    (drawable as? BitmapDrawable)?.bitmap?.config ?: Bitmap.Config.ARGB_8888
                 val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config)
                 val canvas = Canvas(scaledDrawableBitmap)
 
@@ -105,8 +115,8 @@
             }
         }
 
-        private fun isSimpleBitmap(drawable: Drawable): Boolean {
-            return !(drawable.isStateful || isAnimated(drawable))
+        private fun isComplicatedBitmap(drawable: Drawable): Boolean {
+            return drawable.isStateful || isAnimated(drawable) || hasComplicatedLayers(drawable)
         }
 
         private fun isAnimated(drawable: Drawable): Boolean {
@@ -119,5 +129,30 @@
                 drawable is AnimatedStateListDrawable ||
                 drawable is AnimatedVectorDrawable
         }
+
+        private fun hasComplicatedLayers(drawable: Drawable): Boolean {
+            if (drawable !is LayerDrawable) {
+                return false
+            }
+            if (drawable.numberOfLayers == 1) {
+                return false
+            }
+
+            val firstLayerType = drawable.getDrawable(0).javaClass
+            for (i in 1..<drawable.numberOfLayers) {
+                val layer = drawable.getDrawable(i)
+                if (layer.javaClass != firstLayerType) {
+                    // If different layers have different drawable types, we shouldn't scale it down
+                    // because we may lose the level information if one of the layers is a bitmap
+                    // and another layer is a level-list. See b/244282477.
+                    return true
+                }
+                if (isComplicatedBitmap(layer)) {
+                    return true
+                }
+            }
+
+            return false
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
index 2af84c7..579af73 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
@@ -16,10 +16,9 @@
 
 package com.android.systemui.util.kotlin
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dagger.qualifiers.Tracing
 import dagger.Module
 import dagger.Provides
 import javax.inject.Singleton
@@ -27,7 +26,6 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Providers for various application-wide coroutines-related constructs. */
 @Module
@@ -35,16 +33,15 @@
     @Provides
     @Singleton
     @Application
-    fun applicationScope(
-        @Main dispatcherContext: CoroutineContext,
-    ): CoroutineScope = CoroutineScope(dispatcherContext + createCoroutineTracingContext("ApplicationScope"))
+    fun applicationScope(@Main dispatcherContext: CoroutineContext): CoroutineScope =
+        CoroutineScope(dispatcherContext + newTracingContext("ApplicationScope"))
 
     @Provides
     @Singleton
     @Main
     @Deprecated(
         "Use @Main CoroutineContext instead",
-        ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext")
+        ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext"),
     )
     fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index 4d9aaa6..ea8709f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.util.settings
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.annotation.UserIdInt
 import android.content.ContentResolver
 import android.database.ContentObserver
@@ -24,6 +23,7 @@
 import androidx.annotation.AnyThread
 import androidx.annotation.WorkerThread
 import com.android.app.tracing.TraceUtils.trace
+import com.android.systemui.coroutines.newTracingContext
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
@@ -94,7 +94,7 @@
      */
     @AnyThread
     fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-A")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-A")).launch {
             registerContentObserverSync(getUriFor(name), settingsObserver)
         }
 
@@ -109,9 +109,9 @@
     fun registerContentObserverAsync(
         name: String,
         settingsObserver: ContentObserver,
-        @WorkerThread registered: Runnable
+        @WorkerThread registered: Runnable,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-B")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-B")).launch {
             registerContentObserverSync(getUriFor(name), settingsObserver)
             registered.run()
         }
@@ -144,7 +144,7 @@
      */
     @AnyThread
     fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-C")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-C")).launch {
             registerContentObserverSync(uri, settingsObserver)
         }
 
@@ -159,9 +159,9 @@
     fun registerContentObserverAsync(
         uri: Uri,
         settingsObserver: ContentObserver,
-        @WorkerThread registered: Runnable
+        @WorkerThread registered: Runnable,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-D")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-D")).launch {
             registerContentObserverSync(uri, settingsObserver)
             registered.run()
         }
@@ -175,7 +175,7 @@
     fun registerContentObserverSync(
         name: String,
         notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
+        settingsObserver: ContentObserver,
     ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
 
     /**
@@ -204,9 +204,9 @@
     fun registerContentObserverAsync(
         name: String,
         notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
+        settingsObserver: ContentObserver,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-E")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-E")).launch {
             registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
         }
 
@@ -222,9 +222,9 @@
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-        @WorkerThread registered: Runnable
+        @WorkerThread registered: Runnable,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-F")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-F")).launch {
             registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
             registered.run()
         }
@@ -273,9 +273,9 @@
     fun registerContentObserverAsync(
         uri: Uri,
         notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
+        settingsObserver: ContentObserver,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-G")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-G")).launch {
             registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
         }
 
@@ -291,9 +291,9 @@
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-        @WorkerThread registered: Runnable
+        @WorkerThread registered: Runnable,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-H")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-H")).launch {
             registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
             registered.run()
         }
@@ -330,7 +330,9 @@
      */
     @AnyThread
     fun unregisterContentObserverAsync(settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-I")).launch { unregisterContentObserver(settingsObserver) }
+        CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-I")).launch {
+            unregisterContentObserver(settingsObserver)
+        }
 
     /**
      * Look up a name in the database.
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index c820c07..c5deca2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.util.settings
 
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.annotation.UserIdInt
 import android.annotation.WorkerThread
 import android.content.ContentResolver
@@ -24,6 +23,7 @@
 import android.os.UserHandle
 import android.provider.Settings.SettingNotFoundException
 import com.android.app.tracing.TraceUtils.trace
+import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
@@ -79,7 +79,7 @@
     }
 
     override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-A")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-A")).launch {
             registerContentObserverForUserSync(uri, settingsObserver, userId)
         }
 
@@ -88,7 +88,7 @@
     override fun registerContentObserverSync(
         uri: Uri,
         notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
+        settingsObserver: ContentObserver,
     ) {
         registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
     }
@@ -111,9 +111,9 @@
     override fun registerContentObserverAsync(
         uri: Uri,
         notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
+        settingsObserver: ContentObserver,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-B")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-B")).launch {
             registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
         }
 
@@ -156,9 +156,9 @@
     fun registerContentObserverForUserAsync(
         name: String,
         settingsObserver: ContentObserver,
-        userHandle: Int
+        userHandle: Int,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-C")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-C")).launch {
             registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
         }
 
@@ -197,9 +197,9 @@
     fun registerContentObserverForUserAsync(
         uri: Uri,
         settingsObserver: ContentObserver,
-        userHandle: Int
+        userHandle: Int,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-D")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-D")).launch {
             registerContentObserverForUserSync(uri, settingsObserver, userHandle)
         }
 
@@ -214,9 +214,9 @@
         uri: Uri,
         settingsObserver: ContentObserver,
         userHandle: Int,
-        @WorkerThread registered: Runnable
+        @WorkerThread registered: Runnable,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-E")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-E")).launch {
             registerContentObserverForUserSync(uri, settingsObserver, userHandle)
             registered.run()
         }
@@ -273,14 +273,14 @@
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-        userHandle: Int
+        userHandle: Int,
     ) {
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-F")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-F")).launch {
             registerContentObserverForUserSync(
                 getUriFor(name),
                 notifyForDescendants,
                 settingsObserver,
-                userHandle
+                userHandle,
             )
         }
     }
@@ -337,14 +337,14 @@
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
-        userHandle: Int
+        userHandle: Int,
     ) =
-        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-G")).launch {
+        CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-G")).launch {
             registerContentObserverForUserSync(
                 uri,
                 notifyForDescendants,
                 settingsObserver,
-                userHandle
+                userHandle,
             )
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 1f92bc1..bbd8f3dc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -59,7 +59,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.Observer;
 
 import com.android.internal.annotations.GuardedBy;
@@ -110,8 +109,8 @@
     // It is safe to use 99 as the broadcast stream now. There are only 10+ default audio
     // streams defined in AudioSystem for now and audio team is in the middle of restructure,
     // no new default stream is preferred.
-    @VisibleForTesting static final int DYNAMIC_STREAM_BROADCAST = 99;
-    private static final int DYNAMIC_STREAM_REMOTE_START_INDEX = 100;
+    public static final int DYNAMIC_STREAM_BROADCAST = 99;
+    public static final int DYNAMIC_STREAM_REMOTE_START_INDEX = 100;
     private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES =
             new AudioAttributes.Builder()
                     .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7166428..7c5116d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -537,7 +537,7 @@
 
         mWindow.setAttributes(lp);
         mWindow.setLayout(WRAP_CONTENT, WRAP_CONTENT);
-        mDialog.setContentView(R.layout.volume_dialog);
+        mDialog.setContentView(R.layout.volume_dialog_legacy);
         mDialogView = mDialog.findViewById(R.id.volume_dialog);
         mDialogView.setAlpha(0);
         mDialogTimeoutMillis = mSecureSettings.get().getInt(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 20d598a..617aaa7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -27,6 +27,8 @@
 import com.android.settingslib.volume.data.repository.AudioSharingRepository
 import com.android.settingslib.volume.data.repository.AudioSharingRepositoryEmptyImpl
 import com.android.settingslib.volume.data.repository.AudioSharingRepositoryImpl
+import com.android.settingslib.volume.data.repository.AudioSystemRepository
+import com.android.settingslib.volume.data.repository.AudioSystemRepositoryImpl
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
 import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
@@ -106,5 +108,11 @@
             notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
         ): AudioVolumeInteractor =
             AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
+
+        @Provides
+        @SysUISingleton
+        fun provideAudioSystemRepository(
+            @Application context: Context,
+        ): AudioSystemRepository = AudioSystemRepositoryImpl(context)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt
index 3fdf86a..cd8cdc8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogPluginModule.kt
@@ -17,6 +17,13 @@
 package com.android.systemui.volume.dialog.dagger.module
 
 import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
+import com.android.systemui.volume.dialog.utils.VolumeTracer
+import com.android.systemui.volume.dialog.utils.VolumeTracerImpl
+import dagger.Binds
 import dagger.Module
 
-@Module(subcomponents = [VolumeDialogComponent::class]) interface VolumeDialogPluginModule
+@Module(subcomponents = [VolumeDialogComponent::class])
+interface VolumeDialogPluginModule {
+
+    @Binds fun bindVolumeTracer(volumeTracer: VolumeTracerImpl): VolumeTracer
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/data/VolumeDialogVisibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/data/VolumeDialogVisibilityRepository.kt
new file mode 100644
index 0000000..2aeaa5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/data/VolumeDialogVisibilityRepository.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.data
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+@SysUISingleton
+class VolumeDialogVisibilityRepository @Inject constructor() {
+
+    private val mutableDialogVisibility =
+        MutableStateFlow<VolumeDialogVisibilityModel>(VolumeDialogVisibilityModel.Invisible)
+    val dialogVisibility: Flow<VolumeDialogVisibilityModel> = mutableDialogVisibility.asStateFlow()
+
+    fun updateVisibility(
+        update: (current: VolumeDialogVisibilityModel) -> VolumeDialogVisibilityModel
+    ) {
+        mutableDialogVisibility.update(update)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepository.kt
new file mode 100644
index 0000000..26fdb9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.data.repository
+
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+/** Holds current [VolumeDialogStateModel]. */
+@VolumeDialogPlugin
+class VolumeDialogStateRepository @Inject constructor() {
+
+    private val mutableState = MutableStateFlow(VolumeDialogStateModel())
+    val state: Flow<VolumeDialogStateModel> = mutableState.asStateFlow()
+
+    fun updateState(update: (VolumeDialogStateModel) -> VolumeDialogStateModel) {
+        mutableState.update(update)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 2e26fd6..3d125b8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
 import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
-import com.android.systemui.volume.dialog.domain.model.VolumeDialogStateModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.ProducerScope
@@ -50,7 +49,7 @@
     @Background private val bgHandler: Handler,
 ) {
 
-    @SuppressLint("SharedFlowCreation") // event-but needed
+    @SuppressLint("SharedFlowCreation") // event-bus needed
     val event: Flow<VolumeDialogEventModel> =
         callbackFlow {
                 val producer = VolumeDialogEventModelProducer(this)
@@ -79,7 +78,7 @@
 
         override fun onStateChanged(state: VolumeDialogController.State?) {
             if (state != null) {
-                scope.trySend(VolumeDialogEventModel.StateChanged(VolumeDialogStateModel(state)))
+                scope.trySend(VolumeDialogEventModel.StateChanged(state))
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
index 4a709a44b..5c7289b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
@@ -16,20 +16,21 @@
 
 package com.android.systemui.volume.dialog.domain.interactor
 
+import android.util.SparseArray
+import androidx.core.util.keyIterator
 import com.android.systemui.plugins.VolumeDialogController
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.data.repository.VolumeDialogStateRepository
 import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
-import com.android.systemui.volume.dialog.domain.model.VolumeDialogStateModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
 
 /**
  * Exposes [VolumeDialogController.getState] in the [volumeDialogState].
@@ -42,14 +43,69 @@
 constructor(
     volumeDialogCallbacksInteractor: VolumeDialogCallbacksInteractor,
     private val volumeDialogController: VolumeDialogController,
+    private val volumeDialogStateRepository: VolumeDialogStateRepository,
     @VolumeDialogPlugin private val coroutineScope: CoroutineScope,
 ) {
 
-    val volumeDialogState: Flow<VolumeDialogStateModel> =
+    init {
         volumeDialogCallbacksInteractor.event
+            .onEach { event ->
+                when (event) {
+                    is VolumeDialogEventModel.StateChanged -> {
+                        volumeDialogStateRepository.updateState { oldState ->
+                            event.state.copyIntoModel(oldState)
+                        }
+                    }
+                    is VolumeDialogEventModel.AccessibilityModeChanged -> {
+                        volumeDialogStateRepository.updateState { oldState ->
+                            oldState.copy(shouldShowA11ySlider = event.showA11yStream)
+                        }
+                    }
+                    else -> {
+                        // do nothing
+                    }
+                }
+            }
             .onStart { volumeDialogController.getState() }
-            .filterIsInstance(VolumeDialogEventModel.StateChanged::class)
-            .map { it.state }
-            .stateIn(scope = coroutineScope, started = SharingStarted.Eagerly, initialValue = null)
-            .filterNotNull()
+            .launchIn(coroutineScope)
+    }
+
+    val volumeDialogState: Flow<VolumeDialogStateModel> = volumeDialogStateRepository.state
+
+    /** Returns a copy of [model] filled with the values from [VolumeDialogController.State]. */
+    private fun VolumeDialogController.State.copyIntoModel(
+        model: VolumeDialogStateModel
+    ): VolumeDialogStateModel {
+        return model.copy(
+            streamModels =
+                states.mapToMap { stream, streamState ->
+                    VolumeDialogStreamModel(
+                        stream = stream,
+                        isActive = stream == activeStream,
+                        legacyState = streamState,
+                    )
+                },
+            ringerModeInternal = ringerModeInternal,
+            ringerModeExternal = ringerModeExternal,
+            zenMode = zenMode,
+            effectsSuppressor = effectsSuppressor,
+            effectsSuppressorName = effectsSuppressorName,
+            activeStream = activeStream,
+            disallowAlarms = disallowAlarms,
+            disallowMedia = disallowMedia,
+            disallowSystem = disallowSystem,
+            disallowRinger = disallowRinger,
+        )
+    }
+}
+
+private fun <INPUT, OUTPUT> SparseArray<INPUT>.mapToMap(
+    map: (Int, INPUT) -> OUTPUT
+): Map<Int, OUTPUT> {
+    val resultMap = mutableMapOf<Int, OUTPUT>()
+    for (key in keyIterator()) {
+        val mappedValue: OUTPUT = map(key, get(key)!!)
+        resultMap[key] = mappedValue
+    }
+    return resultMap
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index f7d6d90..2668589b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -20,8 +20,12 @@
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.data.VolumeDialogVisibilityRepository
 import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
-import com.android.systemui.volume.dialog.domain.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel.Dismissed
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel.Visible
+import com.android.systemui.volume.dialog.utils.VolumeTracer
 import javax.inject.Inject
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.seconds
@@ -30,13 +34,11 @@
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.update
 
 private val MAX_DIALOG_SHOW_TIME: Duration = 3.seconds
 
@@ -53,14 +55,13 @@
 constructor(
     @VolumeDialogPlugin coroutineScope: CoroutineScope,
     callbacksInteractor: VolumeDialogCallbacksInteractor,
+    private val tracer: VolumeTracer,
+    private val repository: VolumeDialogVisibilityRepository,
 ) {
 
     @SuppressLint("SharedFlowCreation")
     private val mutableDismissDialogEvents = MutableSharedFlow<Unit>()
-    private val mutableDialogVisibility =
-        MutableStateFlow<VolumeDialogVisibilityModel>(VolumeDialogVisibilityModel.Invisible)
-
-    val dialogVisibility: Flow<VolumeDialogVisibilityModel> = mutableDialogVisibility.asStateFlow()
+    val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility
 
     init {
         merge(
@@ -70,12 +71,11 @@
                 },
                 callbacksInteractor.event,
             )
-            .onEach { event ->
-                VolumeDialogVisibilityModel.fromEvent(event)?.let { model ->
-                    mutableDialogVisibility.value = model
-                    if (model is VolumeDialogVisibilityModel.Visible) {
-                        resetDismissTimeout()
-                    }
+            .mapNotNull { it.toVisibilityModel() }
+            .onEach { model ->
+                updateVisibility { model }
+                if (model is VolumeDialogVisibilityModel.Visible) {
+                    resetDismissTimeout()
                 }
             }
             .launchIn(coroutineScope)
@@ -86,9 +86,9 @@
      * [dialogVisibility].
      */
     fun dismissDialog(reason: Int) {
-        mutableDialogVisibility.update {
-            if (it is VolumeDialogVisibilityModel.Dismissed) {
-                it
+        updateVisibility { visibilityModel ->
+            if (visibilityModel is VolumeDialogVisibilityModel.Dismissed) {
+                visibilityModel
             } else {
                 VolumeDialogVisibilityModel.Dismissed(reason)
             }
@@ -99,4 +99,19 @@
     suspend fun resetDismissTimeout() {
         mutableDismissDialogEvents.emit(Unit)
     }
+
+    private fun updateVisibility(
+        update: (VolumeDialogVisibilityModel) -> VolumeDialogVisibilityModel
+    ) {
+        repository.updateVisibility { update(it).also(tracer::traceVisibilityStart) }
+    }
+
+    private fun VolumeDialogEventModel.toVisibilityModel(): VolumeDialogVisibilityModel? {
+        return when (this) {
+            is VolumeDialogEventModel.DismissRequested -> Dismissed(reason)
+            is VolumeDialogEventModel.ShowRequested ->
+                Visible(reason, keyguardLocked, lockTaskModeState)
+            else -> null
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
index ca0310e..80e4238 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.dialog.domain.model
 
 import android.media.AudioManager
+import com.android.systemui.plugins.VolumeDialogController
 
 /**
  * Models VolumeDialogController callback events.
@@ -33,7 +34,7 @@
 
     data class DismissRequested(val reason: Int) : VolumeDialogEventModel
 
-    data class StateChanged(val state: VolumeDialogStateModel) : VolumeDialogEventModel
+    data class StateChanged(val state: VolumeDialogController.State) : VolumeDialogEventModel
 
     data class LayoutDirectionChanged(val layoutDirection: Int) : VolumeDialogEventModel
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt
deleted file mode 100644
index f1443e3..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume.dialog.domain.model
-
-import android.content.ComponentName
-import android.util.SparseArray
-import androidx.core.util.keyIterator
-import com.android.systemui.plugins.VolumeDialogController
-
-/** Models a state of the Volume Dialog. */
-data class VolumeDialogStateModel(
-    val states: Map<Int, VolumeDialogStreamStateModel>,
-    val ringerModeInternal: Int = 0,
-    val ringerModeExternal: Int = 0,
-    val zenMode: Int = 0,
-    val effectsSuppressor: ComponentName? = null,
-    val effectsSuppressorName: String? = null,
-    val activeStream: Int = NO_ACTIVE_STREAM,
-    val disallowAlarms: Boolean = false,
-    val disallowMedia: Boolean = false,
-    val disallowSystem: Boolean = false,
-    val disallowRinger: Boolean = false,
-) {
-
-    constructor(
-        legacyState: VolumeDialogController.State
-    ) : this(
-        states = legacyState.states.mapToMap { VolumeDialogStreamStateModel(it) },
-        ringerModeInternal = legacyState.ringerModeInternal,
-        ringerModeExternal = legacyState.ringerModeExternal,
-        zenMode = legacyState.zenMode,
-        effectsSuppressor = legacyState.effectsSuppressor,
-        effectsSuppressorName = legacyState.effectsSuppressorName,
-        activeStream = legacyState.activeStream,
-        disallowAlarms = legacyState.disallowAlarms,
-        disallowMedia = legacyState.disallowMedia,
-        disallowSystem = legacyState.disallowSystem,
-        disallowRinger = legacyState.disallowRinger,
-    )
-
-    companion object {
-        const val NO_ACTIVE_STREAM: Int = -1
-    }
-}
-
-private fun <INPUT, OUTPUT> SparseArray<INPUT>.mapToMap(map: (INPUT) -> OUTPUT): Map<Int, OUTPUT> {
-    val resultMap = mutableMapOf<Int, OUTPUT>()
-    for (key in keyIterator()) {
-        val mappedValue: OUTPUT = map(get(key)!!)
-        resultMap[key] = mappedValue
-    }
-    return resultMap
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
index db19634..5e0af63 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
@@ -17,19 +17,20 @@
 package com.android.systemui.volume.dialog.settings.domain
 
 import android.app.ActivityManager
-import com.android.app.tracing.coroutines.flow.map
+import com.android.app.tracing.coroutines.flow.flowName
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
-import com.android.systemui.volume.dialog.domain.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 @VolumeDialogScope
@@ -49,6 +50,7 @@
                 deviceProvisionedController.isCurrentUserSetup() &&
                     model.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE
             }
+            .flowName("VDSBI#isVisible")
             .stateIn(coroutineScope, SharingStarted.Eagerly, false)
 
     fun onButtonClicked() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
index ba08876..b2f6cb3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -34,7 +34,7 @@
 
     fun bind(view: View) {
         with(view) {
-            val button = requireViewById<View>(R.id.settings)
+            val button = requireViewById<View>(R.id.volume_dialog_settings)
             repeatWhenAttached {
                 viewModel(
                     traceName = "VolumeDialogViewBinder",
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
new file mode 100644
index 0000000..1792b99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.shared.model
+
+import android.content.ComponentName
+
+/** Models a state of the Volume Dialog. */
+data class VolumeDialogStateModel(
+    val shouldShowA11ySlider: Boolean = false,
+    val streamModels: Map<Int, VolumeDialogStreamModel> = mapOf(),
+    val ringerModeInternal: Int = 0,
+    val ringerModeExternal: Int = 0,
+    val zenMode: Int = 0,
+    val effectsSuppressor: ComponentName? = null,
+    val effectsSuppressorName: String? = null,
+    val activeStream: Int = NO_ACTIVE_STREAM,
+    val disallowAlarms: Boolean = false,
+    val disallowMedia: Boolean = false,
+    val disallowSystem: Boolean = false,
+    val disallowRinger: Boolean = false,
+) {
+
+    companion object {
+        const val NO_ACTIVE_STREAM: Int = -1
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
similarity index 79%
rename from packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamStateModel.kt
rename to packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
index a9d367d..be3cd97 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt
@@ -14,26 +14,32 @@
  * limitations under the License.
  */
 
-package com.android.systemui.volume.dialog.domain.model
+package com.android.systemui.volume.dialog.shared.model
 
-import android.annotation.IntegerRes
+import androidx.annotation.StringRes
 import com.android.systemui.plugins.VolumeDialogController
 
 /** Models a state of an audio stream of the Volume Dialog. */
-data class VolumeDialogStreamStateModel(
+data class VolumeDialogStreamModel(
+    val stream: Int,
     val isDynamic: Boolean = false,
+    val isActive: Boolean,
     val level: Int = 0,
     val levelMin: Int = 0,
     val levelMax: Int = 0,
     val muted: Boolean = false,
     val muteSupported: Boolean = false,
-    @IntegerRes val name: Int = 0,
+    @StringRes val name: Int = 0,
     val remoteLabel: String? = null,
     val routedToBluetooth: Boolean = false,
 ) {
     constructor(
-        legacyState: VolumeDialogController.StreamState
+        stream: Int,
+        isActive: Boolean,
+        legacyState: VolumeDialogController.StreamState,
     ) : this(
+        stream = stream,
+        isActive = isActive,
         isDynamic = legacyState.dynamic,
         level = legacyState.level,
         levelMin = legacyState.levelMin,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogVisibilityModel.kt
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogVisibilityModel.kt
rename to packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogVisibilityModel.kt
index 646445d..56a707d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogVisibilityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogVisibilityModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.volume.dialog.domain.model
+package com.android.systemui.volume.dialog.shared.model
 
 /** Models current Volume Dialog visibility state. */
 sealed interface VolumeDialogVisibilityModel {
@@ -30,19 +30,4 @@
 
     /** Dialog has been shown and then dismissed. */
     data class Dismissed(val reason: Int) : Invisible
-
-    companion object {
-
-        /**
-         * Creates [VolumeDialogVisibilityModel] from appropriate events and returns null otherwise.
-         */
-        fun fromEvent(event: VolumeDialogEventModel): VolumeDialogVisibilityModel? {
-            return when (event) {
-                is VolumeDialogEventModel.DismissRequested -> Dismissed(event.reason)
-                is VolumeDialogEventModel.ShowRequested ->
-                    Visible(event.reason, event.keyguardLocked, event.lockTaskModeState)
-                else -> null
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
new file mode 100644
index 0000000..f78a8dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.interactor
+
+import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.mapNotNull
+
+/** Operates a state of particular slider of the Volume Dialog. */
+class VolumeDialogSliderInteractor
+@AssistedInject
+constructor(
+    @Assisted private val sliderType: VolumeDialogSliderType,
+    volumeDialogStateInteractor: VolumeDialogStateInteractor,
+    private val volumeDialogController: VolumeDialogController,
+) {
+
+    val slider: Flow<VolumeDialogStreamModel> =
+        volumeDialogStateInteractor.volumeDialogState.mapNotNull {
+            it.streamModels[sliderType.audioStream]
+        }
+
+    fun setStreamVolume(userLevel: Int) {
+        volumeDialogController.setStreamVolume(sliderType.audioStream, userLevel)
+    }
+
+    @VolumeDialogScope
+    @AssistedFactory
+    interface Factory {
+
+        fun create(sliderType: VolumeDialogSliderType): VolumeDialogSliderInteractor
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
new file mode 100644
index 0000000..7af4258
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.interactor
+
+import android.content.pm.PackageManager
+import android.media.AudioManager
+import android.media.AudioSystem
+import com.android.settingslib.flags.Flags
+import com.android.systemui.volume.VolumeDialogControllerImpl
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSlidersModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+private const val DEFAULT_STREAM = AudioManager.STREAM_MUSIC
+
+/** Provides a state for the Sliders section of the Volume Dialog. */
+@VolumeDialogScope
+class VolumeDialogSlidersInteractor
+@Inject
+constructor(
+    volumeDialogStateInteractor: VolumeDialogStateInteractor,
+    private val packageManager: PackageManager,
+    @VolumeDialog private val coroutineScope: CoroutineScope,
+) {
+
+    private val streamsSorter = StreamsSorter()
+    val sliders: Flow<VolumeDialogSlidersModel> =
+        volumeDialogStateInteractor.volumeDialogState
+            .filter { it.streamModels.isNotEmpty() }
+            .map { stateModel ->
+                stateModel.streamModels.values
+                    .filter { streamModel -> shouldShowSliders(stateModel, streamModel) }
+                    .sortedWith(streamsSorter)
+            }
+            .map { models ->
+                val sliderTypes: List<VolumeDialogSliderType> =
+                    models.map { model -> model.toType() }
+                VolumeDialogSlidersModel(
+                    slider = sliderTypes.first(),
+                    floatingSliders = sliderTypes.drop(1),
+                )
+            }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .filterNotNull()
+
+    private fun shouldShowSliders(
+        stateModel: VolumeDialogStateModel,
+        streamModel: VolumeDialogStreamModel,
+    ): Boolean {
+        if (streamModel.isActive) {
+            return true
+        }
+
+        if (!packageManager.isTv()) {
+            if (streamModel.stream == AudioSystem.STREAM_ACCESSIBILITY) {
+                return stateModel.shouldShowA11ySlider
+            }
+
+            // Always show the stream for audio sharing if it exists.
+            if (
+                Flags.volumeDialogAudioSharingFix() &&
+                    streamModel.stream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST
+            ) {
+                return true
+            }
+
+            return streamModel.stream == DEFAULT_STREAM || streamModel.isDynamic
+        }
+
+        return false
+    }
+
+    private fun VolumeDialogStreamModel.toType(): VolumeDialogSliderType {
+        return when {
+            stream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST ->
+                VolumeDialogSliderType.AudioSharingStream(stream)
+            stream >= VolumeDialogControllerImpl.DYNAMIC_STREAM_REMOTE_START_INDEX ->
+                VolumeDialogSliderType.RemoteMediaStream(stream)
+            else -> VolumeDialogSliderType.Stream(stream)
+        }
+    }
+
+    private class StreamsSorter : Comparator<VolumeDialogStreamModel> {
+
+        /**
+         * This list reflects the order of the sorted collection. Elements that satisfy predicates
+         * at the beginning of this list will be earlier in the sorted collection.
+         */
+        private val priorityPredicates: List<(VolumeDialogStreamModel) -> Boolean> =
+            listOf(
+                { it.isActive },
+                { it.stream == AudioManager.STREAM_MUSIC },
+                { it.stream == AudioManager.STREAM_ACCESSIBILITY },
+                { it.stream == AudioManager.STREAM_RING },
+                { it.stream == AudioManager.STREAM_NOTIFICATION },
+                { it.stream == AudioManager.STREAM_VOICE_CALL },
+                { it.stream == AudioManager.STREAM_SYSTEM },
+                { it.isDynamic },
+            )
+
+        override fun compare(lhs: VolumeDialogStreamModel, rhs: VolumeDialogStreamModel): Int {
+            return lhs.getPriority() - rhs.getPriority()
+        }
+
+        private fun VolumeDialogStreamModel.getPriority(): Int {
+            val index = priorityPredicates.indexOfFirst { it(this) }
+            return if (index >= 0) {
+                index
+            } else {
+                stream
+            }
+        }
+    }
+}
+
+private fun PackageManager.isTv(): Boolean = hasSystemFeature(PackageManager.FEATURE_LEANBACK)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt
new file mode 100644
index 0000000..605b54a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.model
+
+/** Models different possible audio sliders shown in the Volume Dialog. */
+sealed interface VolumeDialogSliderType {
+
+    // VolumeDialogController uses the same model for every slider type. We need to follow the same
+    // logic until we refactor and decouple data and domain layers from the VolumeDialogController
+    // into separated interactors.
+    val audioStream: Int
+
+    data class Stream(override val audioStream: Int) : VolumeDialogSliderType
+
+    data class RemoteMediaStream(override val audioStream: Int) : VolumeDialogSliderType
+
+    data class AudioSharingStream(override val audioStream: Int) : VolumeDialogSliderType
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSlidersModel.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSlidersModel.kt
index 2f5daaa..91a3328 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSlidersModel.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.volume.dialog.sliders.domain.model
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+/** Models a state of the sliders section of the Volume Dialog. */
+data class VolumeDialogSlidersModel(
+    val slider: VolumeDialogSliderType,
+    val floatingSliders: List<VolumeDialogSliderType>,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
new file mode 100644
index 0000000..25a5f28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.ui
+
+import android.view.View
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.systemui.lifecycle.WindowLifecycleState
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+
+class VolumeDialogSliderViewBinder
+@AssistedInject
+constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel) {
+
+    fun bind(view: View) {
+        with(view) {
+            repeatWhenAttached {
+                viewModel(
+                    traceName = "VolumeDialogSliderViewBinder",
+                    minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+                    factory = { viewModelProvider() },
+                ) { viewModel ->
+                    setSnapshotBinding {}
+
+                    awaitCancellation()
+                }
+            }
+        }
+    }
+
+    @AssistedFactory
+    @VolumeDialogScope
+    interface Factory {
+
+        fun create(
+            viewModelProvider: () -> VolumeDialogSliderViewModel
+        ): VolumeDialogSliderViewBinder
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
new file mode 100644
index 0000000..0a00f70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.ui
+
+import android.view.View
+import com.android.systemui.lifecycle.WindowLifecycleState
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
+
+@VolumeDialogScope
+class VolumeDialogSlidersViewBinder
+@Inject
+constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory) {
+
+    fun bind(view: View) {
+        with(view) {
+            repeatWhenAttached {
+                viewModel(
+                    traceName = "VolumeDialogSlidersViewBinder",
+                    minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+                    factory = { viewModelFactory.create() },
+                ) { viewModel ->
+                    setSnapshotBinding {}
+
+                    awaitCancellation()
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
new file mode 100644
index 0000000..7ee722d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.Flow
+
+class VolumeDialogSliderViewModel
+@AssistedInject
+constructor(@Assisted private val interactor: VolumeDialogSliderInteractor) {
+
+    val model: Flow<VolumeDialogStreamModel> = interactor.slider
+
+    fun setStreamVolume(volume: Int) {
+        interactor.setStreamVolume(volume)
+    }
+
+    @AssistedFactory
+    interface Factory {
+
+        fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
new file mode 100644
index 0000000..b5b292f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+class VolumeDialogSlidersViewModel
+@AssistedInject
+constructor(
+    @VolumeDialog coroutineScope: CoroutineScope,
+    private val slidersInteractor: VolumeDialogSlidersInteractor,
+    private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
+    private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
+    private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
+) {
+
+    val sliders: Flow<VolumeDialogSliderUiModel> =
+        slidersInteractor.sliders
+            .distinctUntilChanged()
+            .map { slidersModel ->
+                VolumeDialogSliderUiModel(
+                    sliderViewBinder = createSliderViewBinder(slidersModel.slider),
+                    floatingSliderViewBinders =
+                        slidersModel.floatingSliders.map(::createSliderViewBinder),
+                )
+            }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .filterNotNull()
+
+    private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
+        sliderViewBinderFactory.create {
+            sliderViewModelFactory.create(sliderInteractorFactory.create(type))
+        }
+
+    @AssistedFactory
+    interface Factory {
+
+        fun create(): VolumeDialogSlidersViewModel
+    }
+}
+
+/** Models slider ui */
+data class VolumeDialogSliderUiModel(
+    val sliderViewBinder: VolumeDialogSliderViewBinder,
+    val floatingSliderViewBinders: List<VolumeDialogSliderViewBinder>,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
index 9c88303..77733fe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
@@ -33,6 +33,7 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 
+/** Binds the Volume Dialog itself. */
 @VolumeDialogScope
 class VolumeDialogBinder
 @Inject
@@ -47,9 +48,13 @@
         with(dialog) {
             setupWindow(window!!)
             dialog.setContentView(R.layout.volume_dialog)
+            dialog.setCanceledOnTouchOutside(true)
 
-            settingsButtonViewBinder.bind(dialog.requireViewById(R.id.settings_container))
-            volumeDialogViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_container))
+            settingsButtonViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_settings))
+            volumeDialogViewBinder.bind(
+                dialog,
+                dialog.requireViewById(R.id.volume_dialog_container),
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 600d176..23e6eac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -16,32 +16,144 @@
 
 package com.android.systemui.volume.dialog.ui.binder
 
+import android.app.Dialog
+import android.view.Gravity
 import android.view.View
+import com.android.internal.view.RotationPolicy
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
 import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.volume.SystemUIInterpolators
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
+import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
+import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
+import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogResourcesViewModel
 import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel
+import com.android.systemui.volume.dialog.utils.VolumeTracer
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
 
+/** Binds the root view of the Volume Dialog. */
+@OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogScope
 class VolumeDialogViewBinder
 @Inject
-constructor(private val volumeDialogViewModelFactory: VolumeDialogViewModel.Factory) {
+constructor(
+    private val volumeResources: VolumeDialogResourcesViewModel,
+    private val gravityViewModel: VolumeDialogGravityViewModel,
+    private val viewModelFactory: VolumeDialogViewModel.Factory,
+    private val jankListenerFactory: JankListenerFactory,
+    private val tracer: VolumeTracer,
+) {
 
-    fun bind(view: View) {
+    fun bind(dialog: Dialog, view: View) {
+        view.alpha = 0f
         view.repeatWhenAttached {
             view.viewModel(
                 traceName = "VolumeDialogViewBinder",
                 minWindowLifecycleState = WindowLifecycleState.ATTACHED,
-                factory = { volumeDialogViewModelFactory.create() },
+                factory = { viewModelFactory.create() },
             ) { viewModel ->
-                view.setSnapshotBinding {}
+                animateVisibility(view, dialog, viewModel.dialogVisibilityModel)
 
                 awaitCancellation()
             }
         }
     }
+
+    private fun CoroutineScope.animateVisibility(
+        view: View,
+        dialog: Dialog,
+        visibilityModel: Flow<VolumeDialogVisibilityModel>,
+    ) {
+        visibilityModel
+            .mapLatest {
+                when (it) {
+                    is VolumeDialogVisibilityModel.Visible -> {
+                        tracer.traceVisibilityEnd(it)
+                        calculateTranslationX(view)?.let(view::setTranslationX)
+                        view.animateShow(volumeResources.dialogShowDurationMillis.first())
+                    }
+                    is VolumeDialogVisibilityModel.Dismissed -> {
+                        tracer.traceVisibilityEnd(it)
+                        view.animateHide(
+                            duration = volumeResources.dialogHideDurationMillis.first(),
+                            translationX = calculateTranslationX(view),
+                        )
+                        dialog.dismiss()
+                    }
+                    is VolumeDialogVisibilityModel.Invisible -> {
+                        // do nothing
+                    }
+                }
+            }
+            .launchIn(this)
+    }
+
+    private suspend fun calculateTranslationX(view: View): Float? {
+        return if (view.display.rotation == RotationPolicy.NATURAL_ROTATION) {
+            val dialogGravity = gravityViewModel.dialogGravity.first()
+            val isGravityLeft = (dialogGravity and Gravity.LEFT) == Gravity.LEFT
+            if (isGravityLeft) {
+                -1
+            } else {
+                1
+            } * view.width / 2.0f
+        } else {
+            null
+        }
+    }
+
+    private suspend fun View.animateShow(duration: Long) {
+        animate()
+            .alpha(1f)
+            .translationX(0f)
+            .setDuration(duration)
+            .setInterpolator(SystemUIInterpolators.LogDecelerateInterpolator())
+            .suspendAnimate(jankListenerFactory.show(this, duration))
+        /* TODO(b/369993851)
+        .withEndAction(Runnable {
+            if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
+                if (mRingerIcon != null) {
+                    mRingerIcon.postOnAnimationDelayed(
+                        getSinglePressFor(mRingerIcon), 1500
+                    )
+                }
+            }
+        })
+         */
+    }
+
+    private suspend fun View.animateHide(duration: Long, translationX: Float?) {
+        val animator =
+            animate()
+                .alpha(0f)
+                .setDuration(duration)
+                .setInterpolator(SystemUIInterpolators.LogAccelerateInterpolator())
+        /*  TODO(b/369993851)
+        .withEndAction(
+            Runnable {
+                mHandler.postDelayed(
+                    Runnable {
+                        hideRingerDrawer()
+
+                    },
+                    50
+                )
+            }
+        )
+         */
+        if (translationX != null) {
+            animator.translationX(translationX)
+        }
+        animator.suspendAnimate(jankListenerFactory.dismiss(this, duration))
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/JankListenerFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/JankListenerFactory.kt
new file mode 100644
index 0000000..9fcd777
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/JankListenerFactory.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ui.utils
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.view.View
+import com.android.internal.jank.Cuj
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import javax.inject.Inject
+
+/** Provides [Animator.AnimatorListener] to measure Volume CUJ Jank */
+@VolumeDialogPluginScope
+class JankListenerFactory
+@Inject
+constructor(private val interactionJankMonitor: InteractionJankMonitor) {
+
+    fun show(view: View, timeout: Long) = getJunkListener(view, "show", timeout)
+
+    fun update(view: View, timeout: Long) = getJunkListener(view, "update", timeout)
+
+    fun dismiss(view: View, timeout: Long) = getJunkListener(view, "dismiss", timeout)
+
+    private fun getJunkListener(
+        view: View,
+        type: String,
+        timeout: Long,
+    ): Animator.AnimatorListener {
+        return object : AnimatorListenerAdapter() {
+            override fun onAnimationStart(animation: Animator) {
+                interactionJankMonitor.begin(
+                    InteractionJankMonitor.Configuration.Builder.withView(
+                            Cuj.CUJ_VOLUME_CONTROL,
+                            view,
+                        )
+                        .setTag(type)
+                        .setTimeout(timeout)
+                )
+            }
+
+            override fun onAnimationEnd(animation: Animator) {
+                interactionJankMonitor.end(Cuj.CUJ_VOLUME_CONTROL)
+            }
+
+            override fun onAnimationCancel(animation: Animator) {
+                interactionJankMonitor.cancel(Cuj.CUJ_VOLUME_CONTROL)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
new file mode 100644
index 0000000..4eae3b9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ui.utils
+
+import android.animation.Animator
+import android.view.ViewPropertyAnimator
+import kotlin.coroutines.resume
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Starts animation and suspends until it's finished. Cancels the animation if the running coroutine
+ * is cancelled.
+ *
+ * Careful! This method overrides [ViewPropertyAnimator.setListener]. Use [animationListener]
+ * instead.
+ */
+suspend fun ViewPropertyAnimator.suspendAnimate(
+    animationListener: Animator.AnimatorListener? = null
+) = suspendCancellableCoroutine { continuation ->
+    start()
+    setListener(
+        object : Animator.AnimatorListener {
+            override fun onAnimationStart(animation: Animator) {
+                animationListener?.onAnimationStart(animation)
+            }
+
+            override fun onAnimationEnd(animation: Animator) {
+                continuation.resume(Unit)
+                animationListener?.onAnimationEnd(animation)
+            }
+
+            override fun onAnimationCancel(animation: Animator) {
+                animationListener?.onAnimationCancel(animation)
+            }
+
+            override fun onAnimationRepeat(animation: Animator) {
+                animationListener?.onAnimationRepeat(animation)
+            }
+        }
+    )
+    continuation.invokeOnCancellation { this.cancel() }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt
index df6523c..112afb1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt
@@ -39,6 +39,7 @@
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.withContext
 
+/** Exposes dialog [GravityInt] for use in the UI layer. */
 @VolumeDialogScope
 class VolumeDialogGravityViewModel
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index 8aa0d09..f336d46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -16,15 +16,14 @@
 
 package com.android.systemui.volume.dialog.ui.viewmodel
 
-import android.app.Dialog
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.plugins.VolumeDialogController
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
-import com.android.systemui.volume.dialog.domain.model.VolumeDialogVisibilityModel
 import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
@@ -32,8 +31,7 @@
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.flow.onEach
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @VolumeDialogPluginScope
@@ -49,6 +47,7 @@
     override suspend fun onActivated(): Nothing {
         coroutineScope {
             dialogVisibilityInteractor.dialogVisibility
+                .onEach { controller.notifyVisible(it is VolumeDialogVisibilityModel.Visible) }
                 .mapLatest { visibilityModel ->
                     with(visibilityModel) {
                         if (this is VolumeDialogVisibilityModel.Visible) {
@@ -78,15 +77,8 @@
                     dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
                 }
             }
-        launch { dialog.awaitShow() }
+        dialog.show()
 
         Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
     }
 }
-
-/** Shows [Dialog] until suspend function is cancelled. */
-private suspend fun Dialog.awaitShow() =
-    suspendCancellableCoroutine<Unit> {
-        show()
-        it.invokeOnCancellation { dismiss() }
-    }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt
new file mode 100644
index 0000000..da9be98
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogResourcesViewModel.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ui.viewmodel
+
+import android.content.Context
+import android.content.res.Resources
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Provides cached resources [Flow]s that update when the configuration changes.
+ *
+ * Consume or use [kotlinx.coroutines.flow.first] to get the value.
+ */
+@VolumeDialogScope
+class VolumeDialogResourcesViewModel
+@Inject
+constructor(
+    @VolumeDialog private val coroutineScope: CoroutineScope,
+    @UiBackground private val uiBackgroundContext: CoroutineContext,
+    private val context: Context,
+    private val configurationController: ConfigurationController,
+) {
+
+    val dialogShowDurationMillis: Flow<Long> = configurationResource {
+        getInteger(R.integer.config_dialogShowAnimationDurationMs).toLong()
+    }
+
+    val dialogHideDurationMillis: Flow<Long> = configurationResource {
+        getInteger(R.integer.config_dialogHideAnimationDurationMs).toLong()
+    }
+
+    private fun <T> configurationResource(get: Resources.() -> T): Flow<T> =
+        configurationController.onConfigChanged
+            .map { context.resources.get() }
+            .onStart { emit(context.resources.get()) }
+            .flowOn(uiBackgroundContext)
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .filterNotNull()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 30c8c15..84c837c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -17,11 +17,20 @@
 package com.android.systemui.volume.dialog.ui.viewmodel
 
 import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.Flow
 
-class VolumeDialogViewModel @AssistedInject constructor() : ExclusiveActivatable() {
+/** Provides a state for the Volume Dialog. */
+class VolumeDialogViewModel
+@AssistedInject
+constructor(dialogVisibilityInteractor: VolumeDialogVisibilityInteractor) : ExclusiveActivatable() {
+
+    val dialogVisibilityModel: Flow<VolumeDialogVisibilityModel> =
+        dialogVisibilityInteractor.dialogVisibility
 
     override suspend fun onActivated(): Nothing {
         awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/utils/VolumeTracer.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/utils/VolumeTracer.kt
new file mode 100644
index 0000000..db35ca7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/utils/VolumeTracer.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.utils
+
+import android.os.Trace
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import javax.inject.Inject
+
+/** Traces the async sections for the Volume Dialog. */
+interface VolumeTracer {
+
+    fun traceVisibilityStart(model: VolumeDialogVisibilityModel)
+
+    fun traceVisibilityEnd(model: VolumeDialogVisibilityModel)
+}
+
+@VolumeDialogPluginScope
+class VolumeTracerImpl @Inject constructor() : VolumeTracer {
+
+    override fun traceVisibilityStart(model: VolumeDialogVisibilityModel) =
+        with(model) { Trace.beginAsyncSection(methodName, tracingCookie) }
+
+    override fun traceVisibilityEnd(model: VolumeDialogVisibilityModel) =
+        with(model) { Trace.endAsyncSection(methodName, tracingCookie) }
+
+    private val VolumeDialogVisibilityModel.tracingCookie
+        get() = this.hashCode()
+
+    private val VolumeDialogVisibilityModel.methodName
+        get() =
+            when (this) {
+                is VolumeDialogVisibilityModel.Visible -> "VolumeDialog#show"
+                is VolumeDialogVisibilityModel.Dismissed -> "VolumeDialog#dismiss"
+                is VolumeDialogVisibilityModel.Invisible -> error("Invisible is unsupported")
+            }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
index f94cbda..609ba02 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
@@ -16,7 +16,10 @@
 
 package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
 
+import com.android.settingslib.media.PhoneMediaDevice.inputRoutingEnabledAndIsDesktop
+import android.content.Context
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
 import com.android.systemui.volume.domain.interactor.AudioSharingInteractor
 import com.android.systemui.volume.domain.model.AudioOutputDevice
@@ -46,6 +49,7 @@
 class MediaOutputComponentInteractor
 @Inject
 constructor(
+    @Application private val context: Context,
     @VolumePanelScope private val coroutineScope: CoroutineScope,
     private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
     audioOutputInteractor: AudioOutputInteractor,
@@ -91,7 +95,8 @@
                         MediaOutputComponentModel.Calling(
                             device = currentAudioDevice,
                             isInAudioSharing = isInAudioSharing,
-                            canOpenAudioSwitcher = false,
+                            /* allow open switcher when input routing is enabled in desktop */
+                            canOpenAudioSwitcher = inputRoutingEnabledAndIsDesktop(context),
                         )
                     )
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
index 4be680e..1e4afc0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
@@ -17,8 +17,10 @@
 package com.android.systemui.volume.panel.component.volume.domain.interactor
 
 import android.media.AudioManager
+import com.android.settingslib.volume.data.repository.AudioSystemRepository
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.Flags
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.isTheSameSession
@@ -41,6 +43,7 @@
     @VolumePanelScope scope: CoroutineScope,
     mediaOutputInteractor: MediaOutputInteractor,
     audioModeInteractor: AudioModeInteractor,
+    private val audioSystemRepository: AudioSystemRepository,
 ) {
 
     val volumePanelSliders: StateFlow<List<SliderType>> =
@@ -83,6 +86,16 @@
     }
 
     private fun MutableList<SliderType>.addStream(stream: Int) {
+        // Hide other streams except STREAM_MUSIC if the isSingleVolume mode is on. This makes sure
+        // the volume slider in volume panel is consistent with the volume slider inside system
+        // settings app.
+        if (Flags.onlyShowMediaStreamSliderInSingleVolumeMode() &&
+            audioSystemRepository.isSingleVolume &&
+            stream != AudioManager.STREAM_MUSIC
+        ) {
+            return
+        }
+
         add(SliderType.Stream(AudioStream(stream)))
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index ffb1f11..2aa1ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -18,6 +18,9 @@
 
 import android.content.Context
 import android.media.AudioManager
+import android.media.AudioManager.STREAM_ALARM
+import android.media.AudioManager.STREAM_MUSIC
+import android.media.AudioManager.STREAM_NOTIFICATION
 import android.util.Log
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
@@ -25,7 +28,11 @@
 import com.android.settingslib.volume.shared.model.AudioStreamModel
 import com.android.settingslib.volume.shared.model.RingerMode
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.modes.shared.ModesUiIcons
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
+import com.android.systemui.util.kotlin.combine
 import com.android.systemui.volume.panel.shared.VolumePanelLogger
 import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
 import dagger.assisted.Assisted
@@ -51,16 +58,14 @@
     @Assisted private val coroutineScope: CoroutineScope,
     private val context: Context,
     private val audioVolumeInteractor: AudioVolumeInteractor,
+    private val zenModeInteractor: ZenModeInteractor,
     private val uiEventLogger: UiEventLogger,
     private val volumePanelLogger: VolumePanelLogger,
 ) : SliderViewModel {
 
     private val volumeChanges = MutableStateFlow<Int?>(null)
     private val streamsAffectedByRing =
-        setOf(
-            AudioManager.STREAM_RING,
-            AudioManager.STREAM_NOTIFICATION,
-        )
+        setOf(AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION)
     private val audioStream = audioStreamWrapper.audioStream
     private val iconsByStream =
         mapOf(
@@ -78,11 +83,6 @@
             AudioStream(AudioManager.STREAM_NOTIFICATION) to R.string.stream_notification,
             AudioStream(AudioManager.STREAM_ALARM) to R.string.stream_alarm,
         )
-    private val disabledTextByStream =
-        mapOf(
-            AudioStream(AudioManager.STREAM_NOTIFICATION) to
-                R.string.stream_notification_unavailable,
-        )
     private val uiEventByStream =
         mapOf(
             AudioStream(AudioManager.STREAM_MUSIC) to
@@ -98,15 +98,48 @@
         )
 
     override val slider: StateFlow<SliderState> =
-        combine(
-                audioVolumeInteractor.getAudioStream(audioStream),
-                audioVolumeInteractor.canChangeVolume(audioStream),
-                audioVolumeInteractor.ringerMode,
-            ) { model, isEnabled, ringerMode ->
-                volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
-                model.toState(isEnabled, ringerMode)
-            }
-            .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
+        if (ModesUiIcons.isEnabled) {
+            combine(
+                    audioVolumeInteractor.getAudioStream(audioStream),
+                    audioVolumeInteractor.canChangeVolume(audioStream),
+                    audioVolumeInteractor.ringerMode,
+                    zenModeInteractor.activeModesBlockingEverything,
+                    zenModeInteractor.activeModesBlockingAlarms,
+                    zenModeInteractor.activeModesBlockingMedia,
+                ) {
+                    model,
+                    isEnabled,
+                    ringerMode,
+                    modesBlockingEverything,
+                    modesBlockingAlarms,
+                    modesBlockingMedia ->
+                    volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
+                    model.toState(
+                        isEnabled,
+                        ringerMode,
+                        getStreamDisabledMessage(
+                            modesBlockingEverything,
+                            modesBlockingAlarms,
+                            modesBlockingMedia,
+                        ),
+                    )
+                }
+                .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
+        } else {
+            combine(
+                    audioVolumeInteractor.getAudioStream(audioStream),
+                    audioVolumeInteractor.canChangeVolume(audioStream),
+                    audioVolumeInteractor.ringerMode,
+                ) { model, isEnabled, ringerMode ->
+                    volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
+                    model.toState(
+                        isEnabled,
+                        ringerMode,
+                        getStreamDisabledMessageWithoutModes(audioStream),
+                    )
+                }
+                .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
+        }
 
     init {
         volumeChanges
@@ -139,6 +172,7 @@
     private fun AudioStreamModel.toState(
         isEnabled: Boolean,
         ringerMode: RingerMode,
+        disabledMessage: String?,
     ): State {
         val label =
             labelsByStream[audioStream]?.let(context::getString)
@@ -148,13 +182,7 @@
             valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
             icon = getIcon(ringerMode),
             label = label,
-            disabledMessage =
-                context.getString(
-                    disabledTextByStream.getOrDefault(
-                        audioStream,
-                        R.string.stream_alarm_unavailable,
-                    )
-                ),
+            disabledMessage = disabledMessage,
             isEnabled = isEnabled,
             a11yStep = volumeRange.step,
             a11yClickDescription =
@@ -191,6 +219,43 @@
         )
     }
 
+    private fun getStreamDisabledMessage(
+        blockingEverything: ActiveZenModes,
+        blockingAlarms: ActiveZenModes,
+        blockingMedia: ActiveZenModes,
+    ): String {
+        // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
+        //  In fact, VOICE_CALL should not be affected by interruption filtering at all.
+        return if (audioStream.value == STREAM_NOTIFICATION) {
+            context.getString(R.string.stream_notification_unavailable)
+        } else {
+            val blockingModeName =
+                when {
+                    blockingEverything.mainMode != null -> blockingEverything.mainMode.name
+                    audioStream.value == STREAM_ALARM -> blockingAlarms.mainMode?.name
+                    audioStream.value == STREAM_MUSIC -> blockingMedia.mainMode?.name
+                    else -> null
+                }
+
+            if (blockingModeName != null) {
+                context.getString(R.string.stream_unavailable_by_modes, blockingModeName)
+            } else {
+                // Should not actually be visible, but as a catch-all.
+                context.getString(R.string.stream_unavailable_by_unknown)
+            }
+        }
+    }
+
+    private fun getStreamDisabledMessageWithoutModes(audioStream: AudioStream): String {
+        // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
+        //  In fact, VOICE_CALL should not be affected by interruption filtering at all.
+        return if (audioStream.value == STREAM_NOTIFICATION) {
+            context.getString(R.string.stream_notification_unavailable)
+        } else {
+            context.getString(R.string.stream_alarm_unavailable)
+        }
+    }
+
     private fun AudioStreamModel.getIcon(ringerMode: RingerMode): Icon {
         val iconRes =
             if (isAffectedByMute && isMuted) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
index 3da725b..e590a7de 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
@@ -22,6 +22,7 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.unit.dp
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -89,7 +90,7 @@
             VolumePanelRoute.SETTINGS_VOLUME_PANEL ->
                 activityStarter.startActivity(
                     /* intent= */ Intent(Settings.Panel.ACTION_VOLUME),
-                    /* dismissShade= */ true
+                    /* dismissShade= */ true,
                 )
             VolumePanelRoute.SYSTEM_UI_VOLUME_PANEL ->
                 volumePanelFactory.create(aboveStatusBar = true, view = null)
@@ -122,6 +123,9 @@
                     remember(coroutineScope) { viewModelFactory.create(coroutineScope) }
                 )
             },
+            isDraggable = false,
+            // TODO(b/337205027) change maxWidth
+            maxWidth = 800.dp,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 203e1da..efdd98d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.shared.Flags.ambientAod
 import com.android.systemui.user.data.model.SelectedUserModel
 import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.UserRepository
@@ -144,14 +145,21 @@
     override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
         wallpaperInfo
             .map {
-                // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
-                it?.supportsAmbientMode() == true
+                if (ambientAod()) {
+                    // Force this mode for now, until ImageWallpaper supports it directly
+                    // TODO(b/371236225)
+                    true
+                } else {
+                    // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient
+                    // mode.
+                    it?.supportsAmbientMode() == true
+                }
             }
             .stateIn(
                 scope,
                 // Always be listening for wallpaper changes.
                 SharingStarted.Eagerly,
-                initialValue = wallpaperInfo.value?.supportsAmbientMode() == true,
+                initialValue = if (ambientAod()) true else false,
             )
 
     override var rootView: View? = null
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
index 79ebf01..fe6977c 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
@@ -18,9 +18,13 @@
 
 import com.android.systemui.wallpapers.data.repository.WallpaperRepository
 import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
 
 class WallpaperInteractor @Inject constructor(val wallpaperRepository: WallpaperRepository) {
     fun setNotificationStackAbsoluteBottom(bottom: Float) {
         wallpaperRepository.setNotificationStackAbsoluteBottom(bottom)
     }
+
+    val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+        wallpaperRepository.wallpaperSupportsAmbientMode
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt
similarity index 67%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt
index 9591002..a51acf6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperViewModel.kt
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.wallpapers.ui.viewmodel
 
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.data.repository.FixedColumnsRepository
+import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.StateFlow
 
-@SysUISingleton
-class FixedColumnsSizeInteractor @Inject constructor(repo: FixedColumnsRepository) {
-    val columns: StateFlow<Int> = repo.columns
+class WallpaperViewModel @Inject constructor(interactor: WallpaperInteractor) {
+    val wallpaperSupportsAmbientMode: StateFlow<Boolean> = interactor.wallpaperSupportsAmbientMode
 }
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json
index 60bff17..7f62357 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching.json
@@ -1,25 +1,30 @@
 {
   "frame_ids": [
-    "before",
     0,
-    26,
-    52,
-    78,
-    105,
-    131,
-    157,
-    184,
-    210,
-    236,
-    263,
-    289,
-    315,
-    342,
-    368,
-    394,
-    421,
-    447,
-    473,
+    20,
+    40,
+    60,
+    80,
+    100,
+    120,
+    140,
+    160,
+    180,
+    200,
+    220,
+    240,
+    260,
+    280,
+    300,
+    320,
+    340,
+    360,
+    380,
+    400,
+    420,
+    440,
+    460,
+    480,
     500
   ],
   "features": [
@@ -28,70 +33,82 @@
       "type": "rect",
       "data_points": [
         {
-          "left": 0,
-          "top": 0,
-          "right": 0,
-          "bottom": 0
-        },
-        {
           "left": 100,
           "top": 300,
           "right": 200,
           "bottom": 400
         },
         {
-          "left": 98,
-          "top": 293,
-          "right": 203,
-          "bottom": 407
+          "left": 99,
+          "top": 296,
+          "right": 202,
+          "bottom": 404
         },
         {
-          "left": 91,
-          "top": 269,
-          "right": 213,
-          "bottom": 430
+          "left": 95,
+          "top": 283,
+          "right": 207,
+          "bottom": 417
         },
         {
-          "left": 71,
-          "top": 206,
-          "right": 240,
-          "bottom": 491
+          "left": 86,
+          "top": 256,
+          "right": 219,
+          "bottom": 443
         },
         {
-          "left": 34,
-          "top": 98,
-          "right": 283,
-          "bottom": 595
+          "left": 68,
+          "top": 198,
+          "right": 243,
+          "bottom": 499
         },
         {
-          "left": 22,
-          "top": 63,
-          "right": 296,
-          "bottom": 629
+          "left": 39,
+          "top": 110,
+          "right": 278,
+          "bottom": 584
+        },
+        {
+          "left": 26,
+          "top": 74,
+          "right": 292,
+          "bottom": 618
+        },
+        {
+          "left": 19,
+          "top": 55,
+          "right": 299,
+          "bottom": 637
         },
         {
           "left": 15,
-          "top": 44,
-          "right": 303,
-          "bottom": 648
+          "top": 42,
+          "right": 304,
+          "bottom": 649
         },
         {
-          "left": 11,
-          "top": 32,
-          "right": 308,
-          "bottom": 659
+          "left": 12,
+          "top": 33,
+          "right": 307,
+          "bottom": 658
         },
         {
-          "left": 8,
-          "top": 23,
-          "right": 311,
-          "bottom": 667
+          "left": 9,
+          "top": 27,
+          "right": 310,
+          "bottom": 664
+        },
+        {
+          "left": 7,
+          "top": 21,
+          "right": 312,
+          "bottom": 669
         },
         {
           "left": 6,
-          "top": 18,
-          "right": 313,
-          "bottom": 673
+          "top": 17,
+          "right": 314,
+          "bottom": 674
         },
         {
           "left": 5,
@@ -100,16 +117,22 @@
           "bottom": 677
         },
         {
-          "left": 3,
-          "top": 9,
+          "left": 4,
+          "top": 10,
           "right": 316,
-          "bottom": 681
+          "bottom": 680
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 317,
+          "bottom": 682
         },
         {
           "left": 2,
-          "top": 7,
-          "right": 317,
-          "bottom": 683
+          "top": 6,
+          "right": 318,
+          "bottom": 684
         },
         {
           "left": 2,
@@ -119,7 +142,7 @@
         },
         {
           "left": 1,
-          "top": 3,
+          "top": 4,
           "right": 319,
           "bottom": 687
         },
@@ -130,6 +153,18 @@
           "bottom": 688
         },
         {
+          "left": 1,
+          "top": 2,
+          "right": 319,
+          "bottom": 688
+        },
+        {
+          "left": 0,
+          "top": 1,
+          "right": 320,
+          "bottom": 689
+        },
+        {
           "left": 0,
           "top": 1,
           "right": 320,
@@ -159,7 +194,6 @@
       "name": "corner_radii",
       "type": "cornerRadii",
       "data_points": [
-        null,
         {
           "top_left_x": 10,
           "top_left_y": 10,
@@ -171,184 +205,244 @@
           "bottom_left_y": 20
         },
         {
-          "top_left_x": 9.762664,
-          "top_left_y": 9.762664,
-          "top_right_x": 9.762664,
-          "top_right_y": 9.762664,
-          "bottom_right_x": 19.525328,
-          "bottom_right_y": 19.525328,
-          "bottom_left_x": 19.525328,
-          "bottom_left_y": 19.525328
+          "top_left_x": 9.865689,
+          "top_left_y": 9.865689,
+          "top_right_x": 9.865689,
+          "top_right_y": 9.865689,
+          "bottom_right_x": 19.731379,
+          "bottom_right_y": 19.731379,
+          "bottom_left_x": 19.731379,
+          "bottom_left_y": 19.731379
         },
         {
-          "top_left_x": 8.969244,
-          "top_left_y": 8.969244,
-          "top_right_x": 8.969244,
-          "top_right_y": 8.969244,
-          "bottom_right_x": 17.938488,
-          "bottom_right_y": 17.938488,
-          "bottom_left_x": 17.938488,
-          "bottom_left_y": 17.938488
+          "top_left_x": 9.419104,
+          "top_left_y": 9.419104,
+          "top_right_x": 9.419104,
+          "top_right_y": 9.419104,
+          "bottom_right_x": 18.838207,
+          "bottom_right_y": 18.838207,
+          "bottom_left_x": 18.838207,
+          "bottom_left_y": 18.838207
         },
         {
-          "top_left_x": 6.8709626,
-          "top_left_y": 6.8709626,
-          "top_right_x": 6.8709626,
-          "top_right_y": 6.8709626,
-          "bottom_right_x": 13.741925,
-          "bottom_right_y": 13.741925,
-          "bottom_left_x": 13.741925,
-          "bottom_left_y": 13.741925
+          "top_left_x": 8.533693,
+          "top_left_y": 8.533693,
+          "top_right_x": 8.533693,
+          "top_right_y": 8.533693,
+          "bottom_right_x": 17.067387,
+          "bottom_right_y": 17.067387,
+          "bottom_left_x": 17.067387,
+          "bottom_left_y": 17.067387
         },
         {
-          "top_left_x": 3.260561,
-          "top_left_y": 3.260561,
-          "top_right_x": 3.260561,
-          "top_right_y": 3.260561,
-          "bottom_right_x": 6.521122,
-          "bottom_right_y": 6.521122,
-          "bottom_left_x": 6.521122,
-          "bottom_left_y": 6.521122
+          "top_left_x": 6.5919456,
+          "top_left_y": 6.5919456,
+          "top_right_x": 6.5919456,
+          "top_right_y": 6.5919456,
+          "bottom_right_x": 13.183891,
+          "bottom_right_y": 13.183891,
+          "bottom_left_x": 13.183891,
+          "bottom_left_y": 13.183891
         },
         {
-          "top_left_x": 2.0915751,
-          "top_left_y": 2.0915751,
-          "top_right_x": 2.0915751,
-          "top_right_y": 2.0915751,
-          "bottom_right_x": 4.1831503,
-          "bottom_right_y": 4.1831503,
-          "bottom_left_x": 4.1831503,
-          "bottom_left_y": 4.1831503
+          "top_left_x": 3.6674318,
+          "top_left_y": 3.6674318,
+          "top_right_x": 3.6674318,
+          "top_right_y": 3.6674318,
+          "bottom_right_x": 7.3348637,
+          "bottom_right_y": 7.3348637,
+          "bottom_left_x": 7.3348637,
+          "bottom_left_y": 7.3348637
         },
         {
-          "top_left_x": 1.4640827,
-          "top_left_y": 1.4640827,
-          "top_right_x": 1.4640827,
-          "top_right_y": 1.4640827,
-          "bottom_right_x": 2.9281654,
-          "bottom_right_y": 2.9281654,
-          "bottom_left_x": 2.9281654,
-          "bottom_left_y": 2.9281654
+          "top_left_x": 2.4832253,
+          "top_left_y": 2.4832253,
+          "top_right_x": 2.4832253,
+          "top_right_y": 2.4832253,
+          "bottom_right_x": 4.9664507,
+          "bottom_right_y": 4.9664507,
+          "bottom_left_x": 4.9664507,
+          "bottom_left_y": 4.9664507
         },
         {
-          "top_left_x": 1.057313,
-          "top_left_y": 1.057313,
-          "top_right_x": 1.057313,
-          "top_right_y": 1.057313,
-          "bottom_right_x": 2.114626,
-          "bottom_right_y": 2.114626,
-          "bottom_left_x": 2.114626,
-          "bottom_left_y": 2.114626
+          "top_left_x": 1.8252907,
+          "top_left_y": 1.8252907,
+          "top_right_x": 1.8252907,
+          "top_right_y": 1.8252907,
+          "bottom_right_x": 3.6505814,
+          "bottom_right_y": 3.6505814,
+          "bottom_left_x": 3.6505814,
+          "bottom_left_y": 3.6505814
         },
         {
-          "top_left_x": 0.7824335,
-          "top_left_y": 0.7824335,
-          "top_right_x": 0.7824335,
-          "top_right_y": 0.7824335,
-          "bottom_right_x": 1.564867,
-          "bottom_right_y": 1.564867,
-          "bottom_left_x": 1.564867,
-          "bottom_left_y": 1.564867
+          "top_left_x": 1.4077549,
+          "top_left_y": 1.4077549,
+          "top_right_x": 1.4077549,
+          "top_right_y": 1.4077549,
+          "bottom_right_x": 2.8155098,
+          "bottom_right_y": 2.8155098,
+          "bottom_left_x": 2.8155098,
+          "bottom_left_y": 2.8155098
         },
         {
-          "top_left_x": 0.5863056,
-          "top_left_y": 0.5863056,
-          "top_right_x": 0.5863056,
-          "top_right_y": 0.5863056,
-          "bottom_right_x": 1.1726112,
-          "bottom_right_y": 1.1726112,
-          "bottom_left_x": 1.1726112,
-          "bottom_left_y": 1.1726112
+          "top_left_x": 1.1067667,
+          "top_left_y": 1.1067667,
+          "top_right_x": 1.1067667,
+          "top_right_y": 1.1067667,
+          "bottom_right_x": 2.2135334,
+          "bottom_right_y": 2.2135334,
+          "bottom_left_x": 2.2135334,
+          "bottom_left_y": 2.2135334
         },
         {
-          "top_left_x": 0.4332962,
-          "top_left_y": 0.4332962,
-          "top_right_x": 0.4332962,
-          "top_right_y": 0.4332962,
-          "bottom_right_x": 0.8665924,
-          "bottom_right_y": 0.8665924,
-          "bottom_left_x": 0.8665924,
-          "bottom_left_y": 0.8665924
+          "top_left_x": 0.88593864,
+          "top_left_y": 0.88593864,
+          "top_right_x": 0.88593864,
+          "top_right_y": 0.88593864,
+          "bottom_right_x": 1.7718773,
+          "bottom_right_y": 1.7718773,
+          "bottom_left_x": 1.7718773,
+          "bottom_left_y": 1.7718773
         },
         {
-          "top_left_x": 0.3145876,
-          "top_left_y": 0.3145876,
-          "top_right_x": 0.3145876,
-          "top_right_y": 0.3145876,
-          "bottom_right_x": 0.6291752,
-          "bottom_right_y": 0.6291752,
-          "bottom_left_x": 0.6291752,
-          "bottom_left_y": 0.6291752
+          "top_left_x": 0.7069988,
+          "top_left_y": 0.7069988,
+          "top_right_x": 0.7069988,
+          "top_right_y": 0.7069988,
+          "bottom_right_x": 1.4139977,
+          "bottom_right_y": 1.4139977,
+          "bottom_left_x": 1.4139977,
+          "bottom_left_y": 1.4139977
         },
         {
-          "top_left_x": 0.22506618,
-          "top_left_y": 0.22506618,
-          "top_right_x": 0.22506618,
-          "top_right_y": 0.22506618,
-          "bottom_right_x": 0.45013237,
-          "bottom_right_y": 0.45013237,
-          "bottom_left_x": 0.45013237,
-          "bottom_left_y": 0.45013237
+          "top_left_x": 0.55613136,
+          "top_left_y": 0.55613136,
+          "top_right_x": 0.55613136,
+          "top_right_y": 0.55613136,
+          "bottom_right_x": 1.1122627,
+          "bottom_right_y": 1.1122627,
+          "bottom_left_x": 1.1122627,
+          "bottom_left_y": 1.1122627
         },
         {
-          "top_left_x": 0.15591621,
-          "top_left_y": 0.15591621,
-          "top_right_x": 0.15591621,
-          "top_right_y": 0.15591621,
-          "bottom_right_x": 0.31183243,
-          "bottom_right_y": 0.31183243,
-          "bottom_left_x": 0.31183243,
-          "bottom_left_y": 0.31183243
+          "top_left_x": 0.44889355,
+          "top_left_y": 0.44889355,
+          "top_right_x": 0.44889355,
+          "top_right_y": 0.44889355,
+          "bottom_right_x": 0.8977871,
+          "bottom_right_y": 0.8977871,
+          "bottom_left_x": 0.8977871,
+          "bottom_left_y": 0.8977871
         },
         {
-          "top_left_x": 0.100948334,
-          "top_left_y": 0.100948334,
-          "top_right_x": 0.100948334,
-          "top_right_y": 0.100948334,
-          "bottom_right_x": 0.20189667,
-          "bottom_right_y": 0.20189667,
-          "bottom_left_x": 0.20189667,
-          "bottom_left_y": 0.20189667
+          "top_left_x": 0.34557533,
+          "top_left_y": 0.34557533,
+          "top_right_x": 0.34557533,
+          "top_right_y": 0.34557533,
+          "bottom_right_x": 0.69115067,
+          "bottom_right_y": 0.69115067,
+          "bottom_left_x": 0.69115067,
+          "bottom_left_y": 0.69115067
         },
         {
-          "top_left_x": 0.06496239,
-          "top_left_y": 0.06496239,
-          "top_right_x": 0.06496239,
-          "top_right_y": 0.06496239,
-          "bottom_right_x": 0.12992477,
-          "bottom_right_y": 0.12992477,
-          "bottom_left_x": 0.12992477,
-          "bottom_left_y": 0.12992477
+          "top_left_x": 0.27671337,
+          "top_left_y": 0.27671337,
+          "top_right_x": 0.27671337,
+          "top_right_y": 0.27671337,
+          "bottom_right_x": 0.55342674,
+          "bottom_right_y": 0.55342674,
+          "bottom_left_x": 0.55342674,
+          "bottom_left_y": 0.55342674
         },
         {
-          "top_left_x": 0.03526497,
-          "top_left_y": 0.03526497,
-          "top_right_x": 0.03526497,
-          "top_right_y": 0.03526497,
-          "bottom_right_x": 0.07052994,
-          "bottom_right_y": 0.07052994,
-          "bottom_left_x": 0.07052994,
-          "bottom_left_y": 0.07052994
+          "top_left_x": 0.20785141,
+          "top_left_y": 0.20785141,
+          "top_right_x": 0.20785141,
+          "top_right_y": 0.20785141,
+          "bottom_right_x": 0.41570282,
+          "bottom_right_y": 0.41570282,
+          "bottom_left_x": 0.41570282,
+          "bottom_left_y": 0.41570282
         },
         {
-          "top_left_x": 0.014661789,
-          "top_left_y": 0.014661789,
-          "top_right_x": 0.014661789,
-          "top_right_y": 0.014661789,
-          "bottom_right_x": 0.029323578,
-          "bottom_right_y": 0.029323578,
-          "bottom_left_x": 0.029323578,
-          "bottom_left_y": 0.029323578
+          "top_left_x": 0.1601448,
+          "top_left_y": 0.1601448,
+          "top_right_x": 0.1601448,
+          "top_right_y": 0.1601448,
+          "bottom_right_x": 0.3202896,
+          "bottom_right_y": 0.3202896,
+          "bottom_left_x": 0.3202896,
+          "bottom_left_y": 0.3202896
         },
         {
-          "top_left_x": 0.0041856766,
-          "top_left_y": 0.0041856766,
-          "top_right_x": 0.0041856766,
-          "top_right_y": 0.0041856766,
-          "bottom_right_x": 0.008371353,
-          "bottom_right_y": 0.008371353,
-          "bottom_left_x": 0.008371353,
-          "bottom_left_y": 0.008371353
+          "top_left_x": 0.117860794,
+          "top_left_y": 0.117860794,
+          "top_right_x": 0.117860794,
+          "top_right_y": 0.117860794,
+          "bottom_right_x": 0.23572159,
+          "bottom_right_y": 0.23572159,
+          "bottom_left_x": 0.23572159,
+          "bottom_left_y": 0.23572159
+        },
+        {
+          "top_left_x": 0.08036041,
+          "top_left_y": 0.08036041,
+          "top_right_x": 0.08036041,
+          "top_right_y": 0.08036041,
+          "bottom_right_x": 0.16072083,
+          "bottom_right_y": 0.16072083,
+          "bottom_left_x": 0.16072083,
+          "bottom_left_y": 0.16072083
+        },
+        {
+          "top_left_x": 0.05836296,
+          "top_left_y": 0.05836296,
+          "top_right_x": 0.05836296,
+          "top_right_y": 0.05836296,
+          "bottom_right_x": 0.11672592,
+          "bottom_right_y": 0.11672592,
+          "bottom_left_x": 0.11672592,
+          "bottom_left_y": 0.11672592
+        },
+        {
+          "top_left_x": 0.03636551,
+          "top_left_y": 0.03636551,
+          "top_right_x": 0.03636551,
+          "top_right_y": 0.03636551,
+          "bottom_right_x": 0.07273102,
+          "bottom_right_y": 0.07273102,
+          "bottom_left_x": 0.07273102,
+          "bottom_left_y": 0.07273102
+        },
+        {
+          "top_left_x": 0.018137932,
+          "top_left_y": 0.018137932,
+          "top_right_x": 0.018137932,
+          "top_right_y": 0.018137932,
+          "bottom_right_x": 0.036275864,
+          "bottom_right_y": 0.036275864,
+          "bottom_left_x": 0.036275864,
+          "bottom_left_y": 0.036275864
+        },
+        {
+          "top_left_x": 0.0082063675,
+          "top_left_y": 0.0082063675,
+          "top_right_x": 0.0082063675,
+          "top_right_y": 0.0082063675,
+          "bottom_right_x": 0.016412735,
+          "bottom_right_y": 0.016412735,
+          "bottom_left_x": 0.016412735,
+          "bottom_left_y": 0.016412735
+        },
+        {
+          "top_left_x": 0.0031013489,
+          "top_left_y": 0.0031013489,
+          "top_right_x": 0.0031013489,
+          "top_right_y": 0.0031013489,
+          "bottom_right_x": 0.0062026978,
+          "bottom_right_y": 0.0062026978,
+          "bottom_left_x": 0.0062026978,
+          "bottom_left_y": 0.0062026978
         },
         {
           "top_left_x": 0,
@@ -367,12 +461,17 @@
       "type": "int",
       "data_points": [
         0,
-        0,
-        115,
-        178,
-        217,
-        241,
-        253,
+        96,
+        153,
+        192,
+        220,
+        238,
+        249,
+        254,
+        255,
+        255,
+        255,
+        255,
         255,
         255,
         255,
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json
new file mode 100644
index 0000000..18eedd4
--- /dev/null
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenLaunching_withSpring.json
@@ -0,0 +1,375 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304
+  ],
+  "features": [
+    {
+      "name": "bounds",
+      "type": "rect",
+      "data_points": [
+        {
+          "left": 0,
+          "top": 0,
+          "right": 0,
+          "bottom": 0
+        },
+        {
+          "left": 94,
+          "top": 284,
+          "right": 206,
+          "bottom": 414
+        },
+        {
+          "left": 83,
+          "top": 251,
+          "right": 219,
+          "bottom": 447
+        },
+        {
+          "left": 70,
+          "top": 212,
+          "right": 234,
+          "bottom": 485
+        },
+        {
+          "left": 57,
+          "top": 173,
+          "right": 250,
+          "bottom": 522
+        },
+        {
+          "left": 46,
+          "top": 139,
+          "right": 264,
+          "bottom": 555
+        },
+        {
+          "left": 36,
+          "top": 109,
+          "right": 276,
+          "bottom": 584
+        },
+        {
+          "left": 28,
+          "top": 84,
+          "right": 285,
+          "bottom": 608
+        },
+        {
+          "left": 21,
+          "top": 65,
+          "right": 293,
+          "bottom": 627
+        },
+        {
+          "left": 16,
+          "top": 49,
+          "right": 300,
+          "bottom": 642
+        },
+        {
+          "left": 12,
+          "top": 36,
+          "right": 305,
+          "bottom": 653
+        },
+        {
+          "left": 9,
+          "top": 27,
+          "right": 308,
+          "bottom": 662
+        },
+        {
+          "left": 7,
+          "top": 20,
+          "right": 312,
+          "bottom": 669
+        },
+        {
+          "left": 5,
+          "top": 14,
+          "right": 314,
+          "bottom": 675
+        },
+        {
+          "left": 4,
+          "top": 11,
+          "right": 315,
+          "bottom": 678
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 316,
+          "bottom": 681
+        },
+        {
+          "left": 2,
+          "top": 5,
+          "right": 317,
+          "bottom": 684
+        },
+        {
+          "left": 1,
+          "top": 4,
+          "right": 318,
+          "bottom": 685
+        },
+        {
+          "left": 1,
+          "top": 3,
+          "right": 318,
+          "bottom": 686
+        },
+        {
+          "left": 0,
+          "top": 2,
+          "right": 319,
+          "bottom": 687
+        }
+      ]
+    },
+    {
+      "name": "corner_radii",
+      "type": "cornerRadii",
+      "data_points": [
+        null,
+        {
+          "top_left_x": 9.492916,
+          "top_left_y": 9.492916,
+          "top_right_x": 9.492916,
+          "top_right_y": 9.492916,
+          "bottom_right_x": 18.985832,
+          "bottom_right_y": 18.985832,
+          "bottom_left_x": 18.985832,
+          "bottom_left_y": 18.985832
+        },
+        {
+          "top_left_x": 8.381761,
+          "top_left_y": 8.381761,
+          "top_right_x": 8.381761,
+          "top_right_y": 8.381761,
+          "bottom_right_x": 16.763521,
+          "bottom_right_y": 16.763521,
+          "bottom_left_x": 16.763521,
+          "bottom_left_y": 16.763521
+        },
+        {
+          "top_left_x": 7.07397,
+          "top_left_y": 7.07397,
+          "top_right_x": 7.07397,
+          "top_right_y": 7.07397,
+          "bottom_right_x": 14.14794,
+          "bottom_right_y": 14.14794,
+          "bottom_left_x": 14.14794,
+          "bottom_left_y": 14.14794
+        },
+        {
+          "top_left_x": 5.7880254,
+          "top_left_y": 5.7880254,
+          "top_right_x": 5.7880254,
+          "top_right_y": 5.7880254,
+          "bottom_right_x": 11.576051,
+          "bottom_right_y": 11.576051,
+          "bottom_left_x": 11.576051,
+          "bottom_left_y": 11.576051
+        },
+        {
+          "top_left_x": 4.6295347,
+          "top_left_y": 4.6295347,
+          "top_right_x": 4.6295347,
+          "top_right_y": 4.6295347,
+          "bottom_right_x": 9.259069,
+          "bottom_right_y": 9.259069,
+          "bottom_left_x": 9.259069,
+          "bottom_left_y": 9.259069
+        },
+        {
+          "top_left_x": 3.638935,
+          "top_left_y": 3.638935,
+          "top_right_x": 3.638935,
+          "top_right_y": 3.638935,
+          "bottom_right_x": 7.27787,
+          "bottom_right_y": 7.27787,
+          "bottom_left_x": 7.27787,
+          "bottom_left_y": 7.27787
+        },
+        {
+          "top_left_x": 2.8209057,
+          "top_left_y": 2.8209057,
+          "top_right_x": 2.8209057,
+          "top_right_y": 2.8209057,
+          "bottom_right_x": 5.6418114,
+          "bottom_right_y": 5.6418114,
+          "bottom_left_x": 5.6418114,
+          "bottom_left_y": 5.6418114
+        },
+        {
+          "top_left_x": 2.1620893,
+          "top_left_y": 2.1620893,
+          "top_right_x": 2.1620893,
+          "top_right_y": 2.1620893,
+          "bottom_right_x": 4.3241787,
+          "bottom_right_y": 4.3241787,
+          "bottom_left_x": 4.3241787,
+          "bottom_left_y": 4.3241787
+        },
+        {
+          "top_left_x": 1.6414614,
+          "top_left_y": 1.6414614,
+          "top_right_x": 1.6414614,
+          "top_right_y": 1.6414614,
+          "bottom_right_x": 3.2829227,
+          "bottom_right_y": 3.2829227,
+          "bottom_left_x": 3.2829227,
+          "bottom_left_y": 3.2829227
+        },
+        {
+          "top_left_x": 1.2361269,
+          "top_left_y": 1.2361269,
+          "top_right_x": 1.2361269,
+          "top_right_y": 1.2361269,
+          "bottom_right_x": 2.4722538,
+          "bottom_right_y": 2.4722538,
+          "bottom_left_x": 2.4722538,
+          "bottom_left_y": 2.4722538
+        },
+        {
+          "top_left_x": 0.92435074,
+          "top_left_y": 0.92435074,
+          "top_right_x": 0.92435074,
+          "top_right_y": 0.92435074,
+          "bottom_right_x": 1.8487015,
+          "bottom_right_y": 1.8487015,
+          "bottom_left_x": 1.8487015,
+          "bottom_left_y": 1.8487015
+        },
+        {
+          "top_left_x": 0.68693924,
+          "top_left_y": 0.68693924,
+          "top_right_x": 0.68693924,
+          "top_right_y": 0.68693924,
+          "bottom_right_x": 1.3738785,
+          "bottom_right_y": 1.3738785,
+          "bottom_left_x": 1.3738785,
+          "bottom_left_y": 1.3738785
+        },
+        {
+          "top_left_x": 0.5076904,
+          "top_left_y": 0.5076904,
+          "top_right_x": 0.5076904,
+          "top_right_y": 0.5076904,
+          "bottom_right_x": 1.0153809,
+          "bottom_right_y": 1.0153809,
+          "bottom_left_x": 1.0153809,
+          "bottom_left_y": 1.0153809
+        },
+        {
+          "top_left_x": 0.3733511,
+          "top_left_y": 0.3733511,
+          "top_right_x": 0.3733511,
+          "top_right_y": 0.3733511,
+          "bottom_right_x": 0.7467022,
+          "bottom_right_y": 0.7467022,
+          "bottom_left_x": 0.7467022,
+          "bottom_left_y": 0.7467022
+        },
+        {
+          "top_left_x": 0.27331638,
+          "top_left_y": 0.27331638,
+          "top_right_x": 0.27331638,
+          "top_right_y": 0.27331638,
+          "bottom_right_x": 0.54663277,
+          "bottom_right_y": 0.54663277,
+          "bottom_left_x": 0.54663277,
+          "bottom_left_y": 0.54663277
+        },
+        {
+          "top_left_x": 0.19925308,
+          "top_left_y": 0.19925308,
+          "top_right_x": 0.19925308,
+          "top_right_y": 0.19925308,
+          "bottom_right_x": 0.39850616,
+          "bottom_right_y": 0.39850616,
+          "bottom_left_x": 0.39850616,
+          "bottom_left_y": 0.39850616
+        },
+        {
+          "top_left_x": 0.14470005,
+          "top_left_y": 0.14470005,
+          "top_right_x": 0.14470005,
+          "top_right_y": 0.14470005,
+          "bottom_right_x": 0.2894001,
+          "bottom_right_y": 0.2894001,
+          "bottom_left_x": 0.2894001,
+          "bottom_left_y": 0.2894001
+        },
+        {
+          "top_left_x": 0.10470486,
+          "top_left_y": 0.10470486,
+          "top_right_x": 0.10470486,
+          "top_right_y": 0.10470486,
+          "bottom_right_x": 0.20940971,
+          "bottom_right_y": 0.20940971,
+          "bottom_left_x": 0.20940971,
+          "bottom_left_y": 0.20940971
+        },
+        {
+          "top_left_x": 0.07550812,
+          "top_left_y": 0.07550812,
+          "top_right_x": 0.07550812,
+          "top_right_y": 0.07550812,
+          "bottom_right_x": 0.15101624,
+          "bottom_right_y": 0.15101624,
+          "bottom_left_x": 0.15101624,
+          "bottom_left_y": 0.15101624
+        }
+      ]
+    },
+    {
+      "name": "alpha",
+      "type": "int",
+      "data_points": [
+        0,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        249,
+        226,
+        192,
+        153,
+        112,
+        72,
+        34,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json
index ea768c0..98005c5 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning.json
@@ -1,25 +1,30 @@
 {
   "frame_ids": [
-    "before",
     0,
-    26,
-    52,
-    78,
-    105,
-    131,
-    157,
-    184,
-    210,
-    236,
-    263,
-    289,
-    315,
-    342,
-    368,
-    394,
-    421,
-    447,
-    473,
+    20,
+    40,
+    60,
+    80,
+    100,
+    120,
+    140,
+    160,
+    180,
+    200,
+    220,
+    240,
+    260,
+    280,
+    300,
+    320,
+    340,
+    360,
+    380,
+    400,
+    420,
+    440,
+    460,
+    480,
     500
   ],
   "features": [
@@ -28,70 +33,82 @@
       "type": "rect",
       "data_points": [
         {
-          "left": 0,
-          "top": 0,
-          "right": 0,
-          "bottom": 0
-        },
-        {
           "left": 100,
           "top": 300,
           "right": 200,
           "bottom": 400
         },
         {
-          "left": 98,
-          "top": 293,
-          "right": 203,
-          "bottom": 407
+          "left": 99,
+          "top": 296,
+          "right": 202,
+          "bottom": 404
         },
         {
-          "left": 91,
-          "top": 269,
-          "right": 213,
-          "bottom": 430
+          "left": 95,
+          "top": 283,
+          "right": 207,
+          "bottom": 417
         },
         {
-          "left": 71,
-          "top": 206,
-          "right": 240,
-          "bottom": 491
+          "left": 86,
+          "top": 256,
+          "right": 219,
+          "bottom": 443
         },
         {
-          "left": 34,
-          "top": 98,
-          "right": 283,
-          "bottom": 595
+          "left": 68,
+          "top": 198,
+          "right": 243,
+          "bottom": 499
         },
         {
-          "left": 22,
-          "top": 63,
-          "right": 296,
-          "bottom": 629
+          "left": 39,
+          "top": 110,
+          "right": 278,
+          "bottom": 584
+        },
+        {
+          "left": 26,
+          "top": 74,
+          "right": 292,
+          "bottom": 618
+        },
+        {
+          "left": 19,
+          "top": 55,
+          "right": 299,
+          "bottom": 637
         },
         {
           "left": 15,
-          "top": 44,
-          "right": 303,
-          "bottom": 648
+          "top": 42,
+          "right": 304,
+          "bottom": 649
         },
         {
-          "left": 11,
-          "top": 32,
-          "right": 308,
-          "bottom": 659
+          "left": 12,
+          "top": 33,
+          "right": 307,
+          "bottom": 658
         },
         {
-          "left": 8,
-          "top": 23,
-          "right": 311,
-          "bottom": 667
+          "left": 9,
+          "top": 27,
+          "right": 310,
+          "bottom": 664
+        },
+        {
+          "left": 7,
+          "top": 21,
+          "right": 312,
+          "bottom": 669
         },
         {
           "left": 6,
-          "top": 18,
-          "right": 313,
-          "bottom": 673
+          "top": 17,
+          "right": 314,
+          "bottom": 674
         },
         {
           "left": 5,
@@ -100,16 +117,22 @@
           "bottom": 677
         },
         {
-          "left": 3,
-          "top": 9,
+          "left": 4,
+          "top": 10,
           "right": 316,
-          "bottom": 681
+          "bottom": 680
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 317,
+          "bottom": 682
         },
         {
           "left": 2,
-          "top": 7,
-          "right": 317,
-          "bottom": 683
+          "top": 6,
+          "right": 318,
+          "bottom": 684
         },
         {
           "left": 2,
@@ -119,7 +142,7 @@
         },
         {
           "left": 1,
-          "top": 3,
+          "top": 4,
           "right": 319,
           "bottom": 687
         },
@@ -130,6 +153,18 @@
           "bottom": 688
         },
         {
+          "left": 1,
+          "top": 2,
+          "right": 319,
+          "bottom": 688
+        },
+        {
+          "left": 0,
+          "top": 1,
+          "right": 320,
+          "bottom": 689
+        },
+        {
           "left": 0,
           "top": 1,
           "right": 320,
@@ -159,7 +194,6 @@
       "name": "corner_radii",
       "type": "cornerRadii",
       "data_points": [
-        null,
         {
           "top_left_x": 10,
           "top_left_y": 10,
@@ -171,184 +205,244 @@
           "bottom_left_y": 20
         },
         {
-          "top_left_x": 9.762664,
-          "top_left_y": 9.762664,
-          "top_right_x": 9.762664,
-          "top_right_y": 9.762664,
-          "bottom_right_x": 19.525328,
-          "bottom_right_y": 19.525328,
-          "bottom_left_x": 19.525328,
-          "bottom_left_y": 19.525328
+          "top_left_x": 9.865689,
+          "top_left_y": 9.865689,
+          "top_right_x": 9.865689,
+          "top_right_y": 9.865689,
+          "bottom_right_x": 19.731379,
+          "bottom_right_y": 19.731379,
+          "bottom_left_x": 19.731379,
+          "bottom_left_y": 19.731379
         },
         {
-          "top_left_x": 8.969244,
-          "top_left_y": 8.969244,
-          "top_right_x": 8.969244,
-          "top_right_y": 8.969244,
-          "bottom_right_x": 17.938488,
-          "bottom_right_y": 17.938488,
-          "bottom_left_x": 17.938488,
-          "bottom_left_y": 17.938488
+          "top_left_x": 9.419104,
+          "top_left_y": 9.419104,
+          "top_right_x": 9.419104,
+          "top_right_y": 9.419104,
+          "bottom_right_x": 18.838207,
+          "bottom_right_y": 18.838207,
+          "bottom_left_x": 18.838207,
+          "bottom_left_y": 18.838207
         },
         {
-          "top_left_x": 6.8709626,
-          "top_left_y": 6.8709626,
-          "top_right_x": 6.8709626,
-          "top_right_y": 6.8709626,
-          "bottom_right_x": 13.741925,
-          "bottom_right_y": 13.741925,
-          "bottom_left_x": 13.741925,
-          "bottom_left_y": 13.741925
+          "top_left_x": 8.533693,
+          "top_left_y": 8.533693,
+          "top_right_x": 8.533693,
+          "top_right_y": 8.533693,
+          "bottom_right_x": 17.067387,
+          "bottom_right_y": 17.067387,
+          "bottom_left_x": 17.067387,
+          "bottom_left_y": 17.067387
         },
         {
-          "top_left_x": 3.260561,
-          "top_left_y": 3.260561,
-          "top_right_x": 3.260561,
-          "top_right_y": 3.260561,
-          "bottom_right_x": 6.521122,
-          "bottom_right_y": 6.521122,
-          "bottom_left_x": 6.521122,
-          "bottom_left_y": 6.521122
+          "top_left_x": 6.5919456,
+          "top_left_y": 6.5919456,
+          "top_right_x": 6.5919456,
+          "top_right_y": 6.5919456,
+          "bottom_right_x": 13.183891,
+          "bottom_right_y": 13.183891,
+          "bottom_left_x": 13.183891,
+          "bottom_left_y": 13.183891
         },
         {
-          "top_left_x": 2.0915751,
-          "top_left_y": 2.0915751,
-          "top_right_x": 2.0915751,
-          "top_right_y": 2.0915751,
-          "bottom_right_x": 4.1831503,
-          "bottom_right_y": 4.1831503,
-          "bottom_left_x": 4.1831503,
-          "bottom_left_y": 4.1831503
+          "top_left_x": 3.6674318,
+          "top_left_y": 3.6674318,
+          "top_right_x": 3.6674318,
+          "top_right_y": 3.6674318,
+          "bottom_right_x": 7.3348637,
+          "bottom_right_y": 7.3348637,
+          "bottom_left_x": 7.3348637,
+          "bottom_left_y": 7.3348637
         },
         {
-          "top_left_x": 1.4640827,
-          "top_left_y": 1.4640827,
-          "top_right_x": 1.4640827,
-          "top_right_y": 1.4640827,
-          "bottom_right_x": 2.9281654,
-          "bottom_right_y": 2.9281654,
-          "bottom_left_x": 2.9281654,
-          "bottom_left_y": 2.9281654
+          "top_left_x": 2.4832253,
+          "top_left_y": 2.4832253,
+          "top_right_x": 2.4832253,
+          "top_right_y": 2.4832253,
+          "bottom_right_x": 4.9664507,
+          "bottom_right_y": 4.9664507,
+          "bottom_left_x": 4.9664507,
+          "bottom_left_y": 4.9664507
         },
         {
-          "top_left_x": 1.057313,
-          "top_left_y": 1.057313,
-          "top_right_x": 1.057313,
-          "top_right_y": 1.057313,
-          "bottom_right_x": 2.114626,
-          "bottom_right_y": 2.114626,
-          "bottom_left_x": 2.114626,
-          "bottom_left_y": 2.114626
+          "top_left_x": 1.8252907,
+          "top_left_y": 1.8252907,
+          "top_right_x": 1.8252907,
+          "top_right_y": 1.8252907,
+          "bottom_right_x": 3.6505814,
+          "bottom_right_y": 3.6505814,
+          "bottom_left_x": 3.6505814,
+          "bottom_left_y": 3.6505814
         },
         {
-          "top_left_x": 0.7824335,
-          "top_left_y": 0.7824335,
-          "top_right_x": 0.7824335,
-          "top_right_y": 0.7824335,
-          "bottom_right_x": 1.564867,
-          "bottom_right_y": 1.564867,
-          "bottom_left_x": 1.564867,
-          "bottom_left_y": 1.564867
+          "top_left_x": 1.4077549,
+          "top_left_y": 1.4077549,
+          "top_right_x": 1.4077549,
+          "top_right_y": 1.4077549,
+          "bottom_right_x": 2.8155098,
+          "bottom_right_y": 2.8155098,
+          "bottom_left_x": 2.8155098,
+          "bottom_left_y": 2.8155098
         },
         {
-          "top_left_x": 0.5863056,
-          "top_left_y": 0.5863056,
-          "top_right_x": 0.5863056,
-          "top_right_y": 0.5863056,
-          "bottom_right_x": 1.1726112,
-          "bottom_right_y": 1.1726112,
-          "bottom_left_x": 1.1726112,
-          "bottom_left_y": 1.1726112
+          "top_left_x": 1.1067667,
+          "top_left_y": 1.1067667,
+          "top_right_x": 1.1067667,
+          "top_right_y": 1.1067667,
+          "bottom_right_x": 2.2135334,
+          "bottom_right_y": 2.2135334,
+          "bottom_left_x": 2.2135334,
+          "bottom_left_y": 2.2135334
         },
         {
-          "top_left_x": 0.4332962,
-          "top_left_y": 0.4332962,
-          "top_right_x": 0.4332962,
-          "top_right_y": 0.4332962,
-          "bottom_right_x": 0.8665924,
-          "bottom_right_y": 0.8665924,
-          "bottom_left_x": 0.8665924,
-          "bottom_left_y": 0.8665924
+          "top_left_x": 0.88593864,
+          "top_left_y": 0.88593864,
+          "top_right_x": 0.88593864,
+          "top_right_y": 0.88593864,
+          "bottom_right_x": 1.7718773,
+          "bottom_right_y": 1.7718773,
+          "bottom_left_x": 1.7718773,
+          "bottom_left_y": 1.7718773
         },
         {
-          "top_left_x": 0.3145876,
-          "top_left_y": 0.3145876,
-          "top_right_x": 0.3145876,
-          "top_right_y": 0.3145876,
-          "bottom_right_x": 0.6291752,
-          "bottom_right_y": 0.6291752,
-          "bottom_left_x": 0.6291752,
-          "bottom_left_y": 0.6291752
+          "top_left_x": 0.7069988,
+          "top_left_y": 0.7069988,
+          "top_right_x": 0.7069988,
+          "top_right_y": 0.7069988,
+          "bottom_right_x": 1.4139977,
+          "bottom_right_y": 1.4139977,
+          "bottom_left_x": 1.4139977,
+          "bottom_left_y": 1.4139977
         },
         {
-          "top_left_x": 0.22506618,
-          "top_left_y": 0.22506618,
-          "top_right_x": 0.22506618,
-          "top_right_y": 0.22506618,
-          "bottom_right_x": 0.45013237,
-          "bottom_right_y": 0.45013237,
-          "bottom_left_x": 0.45013237,
-          "bottom_left_y": 0.45013237
+          "top_left_x": 0.55613136,
+          "top_left_y": 0.55613136,
+          "top_right_x": 0.55613136,
+          "top_right_y": 0.55613136,
+          "bottom_right_x": 1.1122627,
+          "bottom_right_y": 1.1122627,
+          "bottom_left_x": 1.1122627,
+          "bottom_left_y": 1.1122627
         },
         {
-          "top_left_x": 0.15591621,
-          "top_left_y": 0.15591621,
-          "top_right_x": 0.15591621,
-          "top_right_y": 0.15591621,
-          "bottom_right_x": 0.31183243,
-          "bottom_right_y": 0.31183243,
-          "bottom_left_x": 0.31183243,
-          "bottom_left_y": 0.31183243
+          "top_left_x": 0.44889355,
+          "top_left_y": 0.44889355,
+          "top_right_x": 0.44889355,
+          "top_right_y": 0.44889355,
+          "bottom_right_x": 0.8977871,
+          "bottom_right_y": 0.8977871,
+          "bottom_left_x": 0.8977871,
+          "bottom_left_y": 0.8977871
         },
         {
-          "top_left_x": 0.100948334,
-          "top_left_y": 0.100948334,
-          "top_right_x": 0.100948334,
-          "top_right_y": 0.100948334,
-          "bottom_right_x": 0.20189667,
-          "bottom_right_y": 0.20189667,
-          "bottom_left_x": 0.20189667,
-          "bottom_left_y": 0.20189667
+          "top_left_x": 0.34557533,
+          "top_left_y": 0.34557533,
+          "top_right_x": 0.34557533,
+          "top_right_y": 0.34557533,
+          "bottom_right_x": 0.69115067,
+          "bottom_right_y": 0.69115067,
+          "bottom_left_x": 0.69115067,
+          "bottom_left_y": 0.69115067
         },
         {
-          "top_left_x": 0.06496239,
-          "top_left_y": 0.06496239,
-          "top_right_x": 0.06496239,
-          "top_right_y": 0.06496239,
-          "bottom_right_x": 0.12992477,
-          "bottom_right_y": 0.12992477,
-          "bottom_left_x": 0.12992477,
-          "bottom_left_y": 0.12992477
+          "top_left_x": 0.27671337,
+          "top_left_y": 0.27671337,
+          "top_right_x": 0.27671337,
+          "top_right_y": 0.27671337,
+          "bottom_right_x": 0.55342674,
+          "bottom_right_y": 0.55342674,
+          "bottom_left_x": 0.55342674,
+          "bottom_left_y": 0.55342674
         },
         {
-          "top_left_x": 0.03526497,
-          "top_left_y": 0.03526497,
-          "top_right_x": 0.03526497,
-          "top_right_y": 0.03526497,
-          "bottom_right_x": 0.07052994,
-          "bottom_right_y": 0.07052994,
-          "bottom_left_x": 0.07052994,
-          "bottom_left_y": 0.07052994
+          "top_left_x": 0.20785141,
+          "top_left_y": 0.20785141,
+          "top_right_x": 0.20785141,
+          "top_right_y": 0.20785141,
+          "bottom_right_x": 0.41570282,
+          "bottom_right_y": 0.41570282,
+          "bottom_left_x": 0.41570282,
+          "bottom_left_y": 0.41570282
         },
         {
-          "top_left_x": 0.014661789,
-          "top_left_y": 0.014661789,
-          "top_right_x": 0.014661789,
-          "top_right_y": 0.014661789,
-          "bottom_right_x": 0.029323578,
-          "bottom_right_y": 0.029323578,
-          "bottom_left_x": 0.029323578,
-          "bottom_left_y": 0.029323578
+          "top_left_x": 0.1601448,
+          "top_left_y": 0.1601448,
+          "top_right_x": 0.1601448,
+          "top_right_y": 0.1601448,
+          "bottom_right_x": 0.3202896,
+          "bottom_right_y": 0.3202896,
+          "bottom_left_x": 0.3202896,
+          "bottom_left_y": 0.3202896
         },
         {
-          "top_left_x": 0.0041856766,
-          "top_left_y": 0.0041856766,
-          "top_right_x": 0.0041856766,
-          "top_right_y": 0.0041856766,
-          "bottom_right_x": 0.008371353,
-          "bottom_right_y": 0.008371353,
-          "bottom_left_x": 0.008371353,
-          "bottom_left_y": 0.008371353
+          "top_left_x": 0.117860794,
+          "top_left_y": 0.117860794,
+          "top_right_x": 0.117860794,
+          "top_right_y": 0.117860794,
+          "bottom_right_x": 0.23572159,
+          "bottom_right_y": 0.23572159,
+          "bottom_left_x": 0.23572159,
+          "bottom_left_y": 0.23572159
+        },
+        {
+          "top_left_x": 0.08036041,
+          "top_left_y": 0.08036041,
+          "top_right_x": 0.08036041,
+          "top_right_y": 0.08036041,
+          "bottom_right_x": 0.16072083,
+          "bottom_right_y": 0.16072083,
+          "bottom_left_x": 0.16072083,
+          "bottom_left_y": 0.16072083
+        },
+        {
+          "top_left_x": 0.05836296,
+          "top_left_y": 0.05836296,
+          "top_right_x": 0.05836296,
+          "top_right_y": 0.05836296,
+          "bottom_right_x": 0.11672592,
+          "bottom_right_y": 0.11672592,
+          "bottom_left_x": 0.11672592,
+          "bottom_left_y": 0.11672592
+        },
+        {
+          "top_left_x": 0.03636551,
+          "top_left_y": 0.03636551,
+          "top_right_x": 0.03636551,
+          "top_right_y": 0.03636551,
+          "bottom_right_x": 0.07273102,
+          "bottom_right_y": 0.07273102,
+          "bottom_left_x": 0.07273102,
+          "bottom_left_y": 0.07273102
+        },
+        {
+          "top_left_x": 0.018137932,
+          "top_left_y": 0.018137932,
+          "top_right_x": 0.018137932,
+          "top_right_y": 0.018137932,
+          "bottom_right_x": 0.036275864,
+          "bottom_right_y": 0.036275864,
+          "bottom_left_x": 0.036275864,
+          "bottom_left_y": 0.036275864
+        },
+        {
+          "top_left_x": 0.0082063675,
+          "top_left_y": 0.0082063675,
+          "top_right_x": 0.0082063675,
+          "top_right_y": 0.0082063675,
+          "bottom_right_x": 0.016412735,
+          "bottom_right_y": 0.016412735,
+          "bottom_left_x": 0.016412735,
+          "bottom_left_y": 0.016412735
+        },
+        {
+          "top_left_x": 0.0031013489,
+          "top_left_y": 0.0031013489,
+          "top_right_x": 0.0031013489,
+          "top_right_y": 0.0031013489,
+          "bottom_right_x": 0.0062026978,
+          "bottom_right_y": 0.0062026978,
+          "bottom_left_x": 0.0062026978,
+          "bottom_left_y": 0.0062026978
         },
         {
           "top_left_x": 0,
@@ -366,20 +460,25 @@
       "name": "alpha",
       "type": "int",
       "data_points": [
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        233,
+        191,
+        153,
+        117,
+        85,
+        57,
+        33,
+        14,
+        3,
         0,
-        255,
-        255,
-        255,
-        255,
-        255,
-        255,
-        239,
-        183,
-        135,
-        91,
-        53,
-        23,
-        5,
+        0,
         0,
         0,
         0,
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json
new file mode 100644
index 0000000..18eedd4
--- /dev/null
+++ b/packages/SystemUI/tests/goldens/backgroundAnimationWithoutFade_whenReturning_withSpring.json
@@ -0,0 +1,375 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304
+  ],
+  "features": [
+    {
+      "name": "bounds",
+      "type": "rect",
+      "data_points": [
+        {
+          "left": 0,
+          "top": 0,
+          "right": 0,
+          "bottom": 0
+        },
+        {
+          "left": 94,
+          "top": 284,
+          "right": 206,
+          "bottom": 414
+        },
+        {
+          "left": 83,
+          "top": 251,
+          "right": 219,
+          "bottom": 447
+        },
+        {
+          "left": 70,
+          "top": 212,
+          "right": 234,
+          "bottom": 485
+        },
+        {
+          "left": 57,
+          "top": 173,
+          "right": 250,
+          "bottom": 522
+        },
+        {
+          "left": 46,
+          "top": 139,
+          "right": 264,
+          "bottom": 555
+        },
+        {
+          "left": 36,
+          "top": 109,
+          "right": 276,
+          "bottom": 584
+        },
+        {
+          "left": 28,
+          "top": 84,
+          "right": 285,
+          "bottom": 608
+        },
+        {
+          "left": 21,
+          "top": 65,
+          "right": 293,
+          "bottom": 627
+        },
+        {
+          "left": 16,
+          "top": 49,
+          "right": 300,
+          "bottom": 642
+        },
+        {
+          "left": 12,
+          "top": 36,
+          "right": 305,
+          "bottom": 653
+        },
+        {
+          "left": 9,
+          "top": 27,
+          "right": 308,
+          "bottom": 662
+        },
+        {
+          "left": 7,
+          "top": 20,
+          "right": 312,
+          "bottom": 669
+        },
+        {
+          "left": 5,
+          "top": 14,
+          "right": 314,
+          "bottom": 675
+        },
+        {
+          "left": 4,
+          "top": 11,
+          "right": 315,
+          "bottom": 678
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 316,
+          "bottom": 681
+        },
+        {
+          "left": 2,
+          "top": 5,
+          "right": 317,
+          "bottom": 684
+        },
+        {
+          "left": 1,
+          "top": 4,
+          "right": 318,
+          "bottom": 685
+        },
+        {
+          "left": 1,
+          "top": 3,
+          "right": 318,
+          "bottom": 686
+        },
+        {
+          "left": 0,
+          "top": 2,
+          "right": 319,
+          "bottom": 687
+        }
+      ]
+    },
+    {
+      "name": "corner_radii",
+      "type": "cornerRadii",
+      "data_points": [
+        null,
+        {
+          "top_left_x": 9.492916,
+          "top_left_y": 9.492916,
+          "top_right_x": 9.492916,
+          "top_right_y": 9.492916,
+          "bottom_right_x": 18.985832,
+          "bottom_right_y": 18.985832,
+          "bottom_left_x": 18.985832,
+          "bottom_left_y": 18.985832
+        },
+        {
+          "top_left_x": 8.381761,
+          "top_left_y": 8.381761,
+          "top_right_x": 8.381761,
+          "top_right_y": 8.381761,
+          "bottom_right_x": 16.763521,
+          "bottom_right_y": 16.763521,
+          "bottom_left_x": 16.763521,
+          "bottom_left_y": 16.763521
+        },
+        {
+          "top_left_x": 7.07397,
+          "top_left_y": 7.07397,
+          "top_right_x": 7.07397,
+          "top_right_y": 7.07397,
+          "bottom_right_x": 14.14794,
+          "bottom_right_y": 14.14794,
+          "bottom_left_x": 14.14794,
+          "bottom_left_y": 14.14794
+        },
+        {
+          "top_left_x": 5.7880254,
+          "top_left_y": 5.7880254,
+          "top_right_x": 5.7880254,
+          "top_right_y": 5.7880254,
+          "bottom_right_x": 11.576051,
+          "bottom_right_y": 11.576051,
+          "bottom_left_x": 11.576051,
+          "bottom_left_y": 11.576051
+        },
+        {
+          "top_left_x": 4.6295347,
+          "top_left_y": 4.6295347,
+          "top_right_x": 4.6295347,
+          "top_right_y": 4.6295347,
+          "bottom_right_x": 9.259069,
+          "bottom_right_y": 9.259069,
+          "bottom_left_x": 9.259069,
+          "bottom_left_y": 9.259069
+        },
+        {
+          "top_left_x": 3.638935,
+          "top_left_y": 3.638935,
+          "top_right_x": 3.638935,
+          "top_right_y": 3.638935,
+          "bottom_right_x": 7.27787,
+          "bottom_right_y": 7.27787,
+          "bottom_left_x": 7.27787,
+          "bottom_left_y": 7.27787
+        },
+        {
+          "top_left_x": 2.8209057,
+          "top_left_y": 2.8209057,
+          "top_right_x": 2.8209057,
+          "top_right_y": 2.8209057,
+          "bottom_right_x": 5.6418114,
+          "bottom_right_y": 5.6418114,
+          "bottom_left_x": 5.6418114,
+          "bottom_left_y": 5.6418114
+        },
+        {
+          "top_left_x": 2.1620893,
+          "top_left_y": 2.1620893,
+          "top_right_x": 2.1620893,
+          "top_right_y": 2.1620893,
+          "bottom_right_x": 4.3241787,
+          "bottom_right_y": 4.3241787,
+          "bottom_left_x": 4.3241787,
+          "bottom_left_y": 4.3241787
+        },
+        {
+          "top_left_x": 1.6414614,
+          "top_left_y": 1.6414614,
+          "top_right_x": 1.6414614,
+          "top_right_y": 1.6414614,
+          "bottom_right_x": 3.2829227,
+          "bottom_right_y": 3.2829227,
+          "bottom_left_x": 3.2829227,
+          "bottom_left_y": 3.2829227
+        },
+        {
+          "top_left_x": 1.2361269,
+          "top_left_y": 1.2361269,
+          "top_right_x": 1.2361269,
+          "top_right_y": 1.2361269,
+          "bottom_right_x": 2.4722538,
+          "bottom_right_y": 2.4722538,
+          "bottom_left_x": 2.4722538,
+          "bottom_left_y": 2.4722538
+        },
+        {
+          "top_left_x": 0.92435074,
+          "top_left_y": 0.92435074,
+          "top_right_x": 0.92435074,
+          "top_right_y": 0.92435074,
+          "bottom_right_x": 1.8487015,
+          "bottom_right_y": 1.8487015,
+          "bottom_left_x": 1.8487015,
+          "bottom_left_y": 1.8487015
+        },
+        {
+          "top_left_x": 0.68693924,
+          "top_left_y": 0.68693924,
+          "top_right_x": 0.68693924,
+          "top_right_y": 0.68693924,
+          "bottom_right_x": 1.3738785,
+          "bottom_right_y": 1.3738785,
+          "bottom_left_x": 1.3738785,
+          "bottom_left_y": 1.3738785
+        },
+        {
+          "top_left_x": 0.5076904,
+          "top_left_y": 0.5076904,
+          "top_right_x": 0.5076904,
+          "top_right_y": 0.5076904,
+          "bottom_right_x": 1.0153809,
+          "bottom_right_y": 1.0153809,
+          "bottom_left_x": 1.0153809,
+          "bottom_left_y": 1.0153809
+        },
+        {
+          "top_left_x": 0.3733511,
+          "top_left_y": 0.3733511,
+          "top_right_x": 0.3733511,
+          "top_right_y": 0.3733511,
+          "bottom_right_x": 0.7467022,
+          "bottom_right_y": 0.7467022,
+          "bottom_left_x": 0.7467022,
+          "bottom_left_y": 0.7467022
+        },
+        {
+          "top_left_x": 0.27331638,
+          "top_left_y": 0.27331638,
+          "top_right_x": 0.27331638,
+          "top_right_y": 0.27331638,
+          "bottom_right_x": 0.54663277,
+          "bottom_right_y": 0.54663277,
+          "bottom_left_x": 0.54663277,
+          "bottom_left_y": 0.54663277
+        },
+        {
+          "top_left_x": 0.19925308,
+          "top_left_y": 0.19925308,
+          "top_right_x": 0.19925308,
+          "top_right_y": 0.19925308,
+          "bottom_right_x": 0.39850616,
+          "bottom_right_y": 0.39850616,
+          "bottom_left_x": 0.39850616,
+          "bottom_left_y": 0.39850616
+        },
+        {
+          "top_left_x": 0.14470005,
+          "top_left_y": 0.14470005,
+          "top_right_x": 0.14470005,
+          "top_right_y": 0.14470005,
+          "bottom_right_x": 0.2894001,
+          "bottom_right_y": 0.2894001,
+          "bottom_left_x": 0.2894001,
+          "bottom_left_y": 0.2894001
+        },
+        {
+          "top_left_x": 0.10470486,
+          "top_left_y": 0.10470486,
+          "top_right_x": 0.10470486,
+          "top_right_y": 0.10470486,
+          "bottom_right_x": 0.20940971,
+          "bottom_right_y": 0.20940971,
+          "bottom_left_x": 0.20940971,
+          "bottom_left_y": 0.20940971
+        },
+        {
+          "top_left_x": 0.07550812,
+          "top_left_y": 0.07550812,
+          "top_right_x": 0.07550812,
+          "top_right_y": 0.07550812,
+          "bottom_right_x": 0.15101624,
+          "bottom_right_y": 0.15101624,
+          "bottom_left_x": 0.15101624,
+          "bottom_left_y": 0.15101624
+        }
+      ]
+    },
+    {
+      "name": "alpha",
+      "type": "int",
+      "data_points": [
+        0,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        255,
+        249,
+        226,
+        192,
+        153,
+        112,
+        72,
+        34,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json b/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json
index 608e633..aa80445 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching.json
@@ -1,25 +1,30 @@
 {
   "frame_ids": [
-    "before",
     0,
-    26,
-    52,
-    78,
-    105,
-    131,
-    157,
-    184,
-    210,
-    236,
-    263,
-    289,
-    315,
-    342,
-    368,
-    394,
-    421,
-    447,
-    473,
+    20,
+    40,
+    60,
+    80,
+    100,
+    120,
+    140,
+    160,
+    180,
+    200,
+    220,
+    240,
+    260,
+    280,
+    300,
+    320,
+    340,
+    360,
+    380,
+    400,
+    420,
+    440,
+    460,
+    480,
     500
   ],
   "features": [
@@ -28,70 +33,82 @@
       "type": "rect",
       "data_points": [
         {
-          "left": 0,
-          "top": 0,
-          "right": 0,
-          "bottom": 0
-        },
-        {
           "left": 100,
           "top": 300,
           "right": 200,
           "bottom": 400
         },
         {
-          "left": 98,
-          "top": 293,
-          "right": 203,
-          "bottom": 407
+          "left": 99,
+          "top": 296,
+          "right": 202,
+          "bottom": 404
         },
         {
-          "left": 91,
-          "top": 269,
-          "right": 213,
-          "bottom": 430
+          "left": 95,
+          "top": 283,
+          "right": 207,
+          "bottom": 417
         },
         {
-          "left": 71,
-          "top": 206,
-          "right": 240,
-          "bottom": 491
+          "left": 86,
+          "top": 256,
+          "right": 219,
+          "bottom": 443
         },
         {
-          "left": 34,
-          "top": 98,
-          "right": 283,
-          "bottom": 595
+          "left": 68,
+          "top": 198,
+          "right": 243,
+          "bottom": 499
         },
         {
-          "left": 22,
-          "top": 63,
-          "right": 296,
-          "bottom": 629
+          "left": 39,
+          "top": 110,
+          "right": 278,
+          "bottom": 584
+        },
+        {
+          "left": 26,
+          "top": 74,
+          "right": 292,
+          "bottom": 618
+        },
+        {
+          "left": 19,
+          "top": 55,
+          "right": 299,
+          "bottom": 637
         },
         {
           "left": 15,
-          "top": 44,
-          "right": 303,
-          "bottom": 648
+          "top": 42,
+          "right": 304,
+          "bottom": 649
         },
         {
-          "left": 11,
-          "top": 32,
-          "right": 308,
-          "bottom": 659
+          "left": 12,
+          "top": 33,
+          "right": 307,
+          "bottom": 658
         },
         {
-          "left": 8,
-          "top": 23,
-          "right": 311,
-          "bottom": 667
+          "left": 9,
+          "top": 27,
+          "right": 310,
+          "bottom": 664
+        },
+        {
+          "left": 7,
+          "top": 21,
+          "right": 312,
+          "bottom": 669
         },
         {
           "left": 6,
-          "top": 18,
-          "right": 313,
-          "bottom": 673
+          "top": 17,
+          "right": 314,
+          "bottom": 674
         },
         {
           "left": 5,
@@ -100,16 +117,22 @@
           "bottom": 677
         },
         {
-          "left": 3,
-          "top": 9,
+          "left": 4,
+          "top": 10,
           "right": 316,
-          "bottom": 681
+          "bottom": 680
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 317,
+          "bottom": 682
         },
         {
           "left": 2,
-          "top": 7,
-          "right": 317,
-          "bottom": 683
+          "top": 6,
+          "right": 318,
+          "bottom": 684
         },
         {
           "left": 2,
@@ -119,7 +142,7 @@
         },
         {
           "left": 1,
-          "top": 3,
+          "top": 4,
           "right": 319,
           "bottom": 687
         },
@@ -130,6 +153,18 @@
           "bottom": 688
         },
         {
+          "left": 1,
+          "top": 2,
+          "right": 319,
+          "bottom": 688
+        },
+        {
+          "left": 0,
+          "top": 1,
+          "right": 320,
+          "bottom": 689
+        },
+        {
           "left": 0,
           "top": 1,
           "right": 320,
@@ -159,7 +194,6 @@
       "name": "corner_radii",
       "type": "cornerRadii",
       "data_points": [
-        null,
         {
           "top_left_x": 10,
           "top_left_y": 10,
@@ -171,184 +205,244 @@
           "bottom_left_y": 20
         },
         {
-          "top_left_x": 9.762664,
-          "top_left_y": 9.762664,
-          "top_right_x": 9.762664,
-          "top_right_y": 9.762664,
-          "bottom_right_x": 19.525328,
-          "bottom_right_y": 19.525328,
-          "bottom_left_x": 19.525328,
-          "bottom_left_y": 19.525328
+          "top_left_x": 9.865689,
+          "top_left_y": 9.865689,
+          "top_right_x": 9.865689,
+          "top_right_y": 9.865689,
+          "bottom_right_x": 19.731379,
+          "bottom_right_y": 19.731379,
+          "bottom_left_x": 19.731379,
+          "bottom_left_y": 19.731379
         },
         {
-          "top_left_x": 8.969244,
-          "top_left_y": 8.969244,
-          "top_right_x": 8.969244,
-          "top_right_y": 8.969244,
-          "bottom_right_x": 17.938488,
-          "bottom_right_y": 17.938488,
-          "bottom_left_x": 17.938488,
-          "bottom_left_y": 17.938488
+          "top_left_x": 9.419104,
+          "top_left_y": 9.419104,
+          "top_right_x": 9.419104,
+          "top_right_y": 9.419104,
+          "bottom_right_x": 18.838207,
+          "bottom_right_y": 18.838207,
+          "bottom_left_x": 18.838207,
+          "bottom_left_y": 18.838207
         },
         {
-          "top_left_x": 6.8709626,
-          "top_left_y": 6.8709626,
-          "top_right_x": 6.8709626,
-          "top_right_y": 6.8709626,
-          "bottom_right_x": 13.741925,
-          "bottom_right_y": 13.741925,
-          "bottom_left_x": 13.741925,
-          "bottom_left_y": 13.741925
+          "top_left_x": 8.533693,
+          "top_left_y": 8.533693,
+          "top_right_x": 8.533693,
+          "top_right_y": 8.533693,
+          "bottom_right_x": 17.067387,
+          "bottom_right_y": 17.067387,
+          "bottom_left_x": 17.067387,
+          "bottom_left_y": 17.067387
         },
         {
-          "top_left_x": 3.260561,
-          "top_left_y": 3.260561,
-          "top_right_x": 3.260561,
-          "top_right_y": 3.260561,
-          "bottom_right_x": 6.521122,
-          "bottom_right_y": 6.521122,
-          "bottom_left_x": 6.521122,
-          "bottom_left_y": 6.521122
+          "top_left_x": 6.5919456,
+          "top_left_y": 6.5919456,
+          "top_right_x": 6.5919456,
+          "top_right_y": 6.5919456,
+          "bottom_right_x": 13.183891,
+          "bottom_right_y": 13.183891,
+          "bottom_left_x": 13.183891,
+          "bottom_left_y": 13.183891
         },
         {
-          "top_left_x": 2.0915751,
-          "top_left_y": 2.0915751,
-          "top_right_x": 2.0915751,
-          "top_right_y": 2.0915751,
-          "bottom_right_x": 4.1831503,
-          "bottom_right_y": 4.1831503,
-          "bottom_left_x": 4.1831503,
-          "bottom_left_y": 4.1831503
+          "top_left_x": 3.6674318,
+          "top_left_y": 3.6674318,
+          "top_right_x": 3.6674318,
+          "top_right_y": 3.6674318,
+          "bottom_right_x": 7.3348637,
+          "bottom_right_y": 7.3348637,
+          "bottom_left_x": 7.3348637,
+          "bottom_left_y": 7.3348637
         },
         {
-          "top_left_x": 1.4640827,
-          "top_left_y": 1.4640827,
-          "top_right_x": 1.4640827,
-          "top_right_y": 1.4640827,
-          "bottom_right_x": 2.9281654,
-          "bottom_right_y": 2.9281654,
-          "bottom_left_x": 2.9281654,
-          "bottom_left_y": 2.9281654
+          "top_left_x": 2.4832253,
+          "top_left_y": 2.4832253,
+          "top_right_x": 2.4832253,
+          "top_right_y": 2.4832253,
+          "bottom_right_x": 4.9664507,
+          "bottom_right_y": 4.9664507,
+          "bottom_left_x": 4.9664507,
+          "bottom_left_y": 4.9664507
         },
         {
-          "top_left_x": 1.057313,
-          "top_left_y": 1.057313,
-          "top_right_x": 1.057313,
-          "top_right_y": 1.057313,
-          "bottom_right_x": 2.114626,
-          "bottom_right_y": 2.114626,
-          "bottom_left_x": 2.114626,
-          "bottom_left_y": 2.114626
+          "top_left_x": 1.8252907,
+          "top_left_y": 1.8252907,
+          "top_right_x": 1.8252907,
+          "top_right_y": 1.8252907,
+          "bottom_right_x": 3.6505814,
+          "bottom_right_y": 3.6505814,
+          "bottom_left_x": 3.6505814,
+          "bottom_left_y": 3.6505814
         },
         {
-          "top_left_x": 0.7824335,
-          "top_left_y": 0.7824335,
-          "top_right_x": 0.7824335,
-          "top_right_y": 0.7824335,
-          "bottom_right_x": 1.564867,
-          "bottom_right_y": 1.564867,
-          "bottom_left_x": 1.564867,
-          "bottom_left_y": 1.564867
+          "top_left_x": 1.4077549,
+          "top_left_y": 1.4077549,
+          "top_right_x": 1.4077549,
+          "top_right_y": 1.4077549,
+          "bottom_right_x": 2.8155098,
+          "bottom_right_y": 2.8155098,
+          "bottom_left_x": 2.8155098,
+          "bottom_left_y": 2.8155098
         },
         {
-          "top_left_x": 0.5863056,
-          "top_left_y": 0.5863056,
-          "top_right_x": 0.5863056,
-          "top_right_y": 0.5863056,
-          "bottom_right_x": 1.1726112,
-          "bottom_right_y": 1.1726112,
-          "bottom_left_x": 1.1726112,
-          "bottom_left_y": 1.1726112
+          "top_left_x": 1.1067667,
+          "top_left_y": 1.1067667,
+          "top_right_x": 1.1067667,
+          "top_right_y": 1.1067667,
+          "bottom_right_x": 2.2135334,
+          "bottom_right_y": 2.2135334,
+          "bottom_left_x": 2.2135334,
+          "bottom_left_y": 2.2135334
         },
         {
-          "top_left_x": 0.4332962,
-          "top_left_y": 0.4332962,
-          "top_right_x": 0.4332962,
-          "top_right_y": 0.4332962,
-          "bottom_right_x": 0.8665924,
-          "bottom_right_y": 0.8665924,
-          "bottom_left_x": 0.8665924,
-          "bottom_left_y": 0.8665924
+          "top_left_x": 0.88593864,
+          "top_left_y": 0.88593864,
+          "top_right_x": 0.88593864,
+          "top_right_y": 0.88593864,
+          "bottom_right_x": 1.7718773,
+          "bottom_right_y": 1.7718773,
+          "bottom_left_x": 1.7718773,
+          "bottom_left_y": 1.7718773
         },
         {
-          "top_left_x": 0.3145876,
-          "top_left_y": 0.3145876,
-          "top_right_x": 0.3145876,
-          "top_right_y": 0.3145876,
-          "bottom_right_x": 0.6291752,
-          "bottom_right_y": 0.6291752,
-          "bottom_left_x": 0.6291752,
-          "bottom_left_y": 0.6291752
+          "top_left_x": 0.7069988,
+          "top_left_y": 0.7069988,
+          "top_right_x": 0.7069988,
+          "top_right_y": 0.7069988,
+          "bottom_right_x": 1.4139977,
+          "bottom_right_y": 1.4139977,
+          "bottom_left_x": 1.4139977,
+          "bottom_left_y": 1.4139977
         },
         {
-          "top_left_x": 0.22506618,
-          "top_left_y": 0.22506618,
-          "top_right_x": 0.22506618,
-          "top_right_y": 0.22506618,
-          "bottom_right_x": 0.45013237,
-          "bottom_right_y": 0.45013237,
-          "bottom_left_x": 0.45013237,
-          "bottom_left_y": 0.45013237
+          "top_left_x": 0.55613136,
+          "top_left_y": 0.55613136,
+          "top_right_x": 0.55613136,
+          "top_right_y": 0.55613136,
+          "bottom_right_x": 1.1122627,
+          "bottom_right_y": 1.1122627,
+          "bottom_left_x": 1.1122627,
+          "bottom_left_y": 1.1122627
         },
         {
-          "top_left_x": 0.15591621,
-          "top_left_y": 0.15591621,
-          "top_right_x": 0.15591621,
-          "top_right_y": 0.15591621,
-          "bottom_right_x": 0.31183243,
-          "bottom_right_y": 0.31183243,
-          "bottom_left_x": 0.31183243,
-          "bottom_left_y": 0.31183243
+          "top_left_x": 0.44889355,
+          "top_left_y": 0.44889355,
+          "top_right_x": 0.44889355,
+          "top_right_y": 0.44889355,
+          "bottom_right_x": 0.8977871,
+          "bottom_right_y": 0.8977871,
+          "bottom_left_x": 0.8977871,
+          "bottom_left_y": 0.8977871
         },
         {
-          "top_left_x": 0.100948334,
-          "top_left_y": 0.100948334,
-          "top_right_x": 0.100948334,
-          "top_right_y": 0.100948334,
-          "bottom_right_x": 0.20189667,
-          "bottom_right_y": 0.20189667,
-          "bottom_left_x": 0.20189667,
-          "bottom_left_y": 0.20189667
+          "top_left_x": 0.34557533,
+          "top_left_y": 0.34557533,
+          "top_right_x": 0.34557533,
+          "top_right_y": 0.34557533,
+          "bottom_right_x": 0.69115067,
+          "bottom_right_y": 0.69115067,
+          "bottom_left_x": 0.69115067,
+          "bottom_left_y": 0.69115067
         },
         {
-          "top_left_x": 0.06496239,
-          "top_left_y": 0.06496239,
-          "top_right_x": 0.06496239,
-          "top_right_y": 0.06496239,
-          "bottom_right_x": 0.12992477,
-          "bottom_right_y": 0.12992477,
-          "bottom_left_x": 0.12992477,
-          "bottom_left_y": 0.12992477
+          "top_left_x": 0.27671337,
+          "top_left_y": 0.27671337,
+          "top_right_x": 0.27671337,
+          "top_right_y": 0.27671337,
+          "bottom_right_x": 0.55342674,
+          "bottom_right_y": 0.55342674,
+          "bottom_left_x": 0.55342674,
+          "bottom_left_y": 0.55342674
         },
         {
-          "top_left_x": 0.03526497,
-          "top_left_y": 0.03526497,
-          "top_right_x": 0.03526497,
-          "top_right_y": 0.03526497,
-          "bottom_right_x": 0.07052994,
-          "bottom_right_y": 0.07052994,
-          "bottom_left_x": 0.07052994,
-          "bottom_left_y": 0.07052994
+          "top_left_x": 0.20785141,
+          "top_left_y": 0.20785141,
+          "top_right_x": 0.20785141,
+          "top_right_y": 0.20785141,
+          "bottom_right_x": 0.41570282,
+          "bottom_right_y": 0.41570282,
+          "bottom_left_x": 0.41570282,
+          "bottom_left_y": 0.41570282
         },
         {
-          "top_left_x": 0.014661789,
-          "top_left_y": 0.014661789,
-          "top_right_x": 0.014661789,
-          "top_right_y": 0.014661789,
-          "bottom_right_x": 0.029323578,
-          "bottom_right_y": 0.029323578,
-          "bottom_left_x": 0.029323578,
-          "bottom_left_y": 0.029323578
+          "top_left_x": 0.1601448,
+          "top_left_y": 0.1601448,
+          "top_right_x": 0.1601448,
+          "top_right_y": 0.1601448,
+          "bottom_right_x": 0.3202896,
+          "bottom_right_y": 0.3202896,
+          "bottom_left_x": 0.3202896,
+          "bottom_left_y": 0.3202896
         },
         {
-          "top_left_x": 0.0041856766,
-          "top_left_y": 0.0041856766,
-          "top_right_x": 0.0041856766,
-          "top_right_y": 0.0041856766,
-          "bottom_right_x": 0.008371353,
-          "bottom_right_y": 0.008371353,
-          "bottom_left_x": 0.008371353,
-          "bottom_left_y": 0.008371353
+          "top_left_x": 0.117860794,
+          "top_left_y": 0.117860794,
+          "top_right_x": 0.117860794,
+          "top_right_y": 0.117860794,
+          "bottom_right_x": 0.23572159,
+          "bottom_right_y": 0.23572159,
+          "bottom_left_x": 0.23572159,
+          "bottom_left_y": 0.23572159
+        },
+        {
+          "top_left_x": 0.08036041,
+          "top_left_y": 0.08036041,
+          "top_right_x": 0.08036041,
+          "top_right_y": 0.08036041,
+          "bottom_right_x": 0.16072083,
+          "bottom_right_y": 0.16072083,
+          "bottom_left_x": 0.16072083,
+          "bottom_left_y": 0.16072083
+        },
+        {
+          "top_left_x": 0.05836296,
+          "top_left_y": 0.05836296,
+          "top_right_x": 0.05836296,
+          "top_right_y": 0.05836296,
+          "bottom_right_x": 0.11672592,
+          "bottom_right_y": 0.11672592,
+          "bottom_left_x": 0.11672592,
+          "bottom_left_y": 0.11672592
+        },
+        {
+          "top_left_x": 0.03636551,
+          "top_left_y": 0.03636551,
+          "top_right_x": 0.03636551,
+          "top_right_y": 0.03636551,
+          "bottom_right_x": 0.07273102,
+          "bottom_right_y": 0.07273102,
+          "bottom_left_x": 0.07273102,
+          "bottom_left_y": 0.07273102
+        },
+        {
+          "top_left_x": 0.018137932,
+          "top_left_y": 0.018137932,
+          "top_right_x": 0.018137932,
+          "top_right_y": 0.018137932,
+          "bottom_right_x": 0.036275864,
+          "bottom_right_y": 0.036275864,
+          "bottom_left_x": 0.036275864,
+          "bottom_left_y": 0.036275864
+        },
+        {
+          "top_left_x": 0.0082063675,
+          "top_left_y": 0.0082063675,
+          "top_right_x": 0.0082063675,
+          "top_right_y": 0.0082063675,
+          "bottom_right_x": 0.016412735,
+          "bottom_right_y": 0.016412735,
+          "bottom_left_x": 0.016412735,
+          "bottom_left_y": 0.016412735
+        },
+        {
+          "top_left_x": 0.0031013489,
+          "top_left_y": 0.0031013489,
+          "top_right_x": 0.0031013489,
+          "top_right_y": 0.0031013489,
+          "bottom_right_x": 0.0062026978,
+          "bottom_right_y": 0.0062026978,
+          "bottom_left_x": 0.0062026978,
+          "bottom_left_y": 0.0062026978
         },
         {
           "top_left_x": 0,
@@ -367,19 +461,24 @@
       "type": "int",
       "data_points": [
         0,
+        96,
+        153,
+        192,
+        220,
+        238,
+        249,
+        254,
+        233,
+        191,
+        153,
+        117,
+        85,
+        57,
+        33,
+        14,
+        3,
         0,
-        115,
-        178,
-        217,
-        241,
-        253,
-        239,
-        183,
-        135,
-        91,
-        53,
-        23,
-        5,
+        0,
         0,
         0,
         0,
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json
new file mode 100644
index 0000000..a840d3c
--- /dev/null
+++ b/packages/SystemUI/tests/goldens/backgroundAnimation_whenLaunching_withSpring.json
@@ -0,0 +1,375 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304
+  ],
+  "features": [
+    {
+      "name": "bounds",
+      "type": "rect",
+      "data_points": [
+        {
+          "left": 0,
+          "top": 0,
+          "right": 0,
+          "bottom": 0
+        },
+        {
+          "left": 94,
+          "top": 284,
+          "right": 206,
+          "bottom": 414
+        },
+        {
+          "left": 83,
+          "top": 251,
+          "right": 219,
+          "bottom": 447
+        },
+        {
+          "left": 70,
+          "top": 212,
+          "right": 234,
+          "bottom": 485
+        },
+        {
+          "left": 57,
+          "top": 173,
+          "right": 250,
+          "bottom": 522
+        },
+        {
+          "left": 46,
+          "top": 139,
+          "right": 264,
+          "bottom": 555
+        },
+        {
+          "left": 36,
+          "top": 109,
+          "right": 276,
+          "bottom": 584
+        },
+        {
+          "left": 28,
+          "top": 84,
+          "right": 285,
+          "bottom": 608
+        },
+        {
+          "left": 21,
+          "top": 65,
+          "right": 293,
+          "bottom": 627
+        },
+        {
+          "left": 16,
+          "top": 49,
+          "right": 300,
+          "bottom": 642
+        },
+        {
+          "left": 12,
+          "top": 36,
+          "right": 305,
+          "bottom": 653
+        },
+        {
+          "left": 9,
+          "top": 27,
+          "right": 308,
+          "bottom": 662
+        },
+        {
+          "left": 7,
+          "top": 20,
+          "right": 312,
+          "bottom": 669
+        },
+        {
+          "left": 5,
+          "top": 14,
+          "right": 314,
+          "bottom": 675
+        },
+        {
+          "left": 4,
+          "top": 11,
+          "right": 315,
+          "bottom": 678
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 316,
+          "bottom": 681
+        },
+        {
+          "left": 2,
+          "top": 5,
+          "right": 317,
+          "bottom": 684
+        },
+        {
+          "left": 1,
+          "top": 4,
+          "right": 318,
+          "bottom": 685
+        },
+        {
+          "left": 1,
+          "top": 3,
+          "right": 318,
+          "bottom": 686
+        },
+        {
+          "left": 0,
+          "top": 2,
+          "right": 319,
+          "bottom": 687
+        }
+      ]
+    },
+    {
+      "name": "corner_radii",
+      "type": "cornerRadii",
+      "data_points": [
+        null,
+        {
+          "top_left_x": 9.492916,
+          "top_left_y": 9.492916,
+          "top_right_x": 9.492916,
+          "top_right_y": 9.492916,
+          "bottom_right_x": 18.985832,
+          "bottom_right_y": 18.985832,
+          "bottom_left_x": 18.985832,
+          "bottom_left_y": 18.985832
+        },
+        {
+          "top_left_x": 8.381761,
+          "top_left_y": 8.381761,
+          "top_right_x": 8.381761,
+          "top_right_y": 8.381761,
+          "bottom_right_x": 16.763521,
+          "bottom_right_y": 16.763521,
+          "bottom_left_x": 16.763521,
+          "bottom_left_y": 16.763521
+        },
+        {
+          "top_left_x": 7.07397,
+          "top_left_y": 7.07397,
+          "top_right_x": 7.07397,
+          "top_right_y": 7.07397,
+          "bottom_right_x": 14.14794,
+          "bottom_right_y": 14.14794,
+          "bottom_left_x": 14.14794,
+          "bottom_left_y": 14.14794
+        },
+        {
+          "top_left_x": 5.7880254,
+          "top_left_y": 5.7880254,
+          "top_right_x": 5.7880254,
+          "top_right_y": 5.7880254,
+          "bottom_right_x": 11.576051,
+          "bottom_right_y": 11.576051,
+          "bottom_left_x": 11.576051,
+          "bottom_left_y": 11.576051
+        },
+        {
+          "top_left_x": 4.6295347,
+          "top_left_y": 4.6295347,
+          "top_right_x": 4.6295347,
+          "top_right_y": 4.6295347,
+          "bottom_right_x": 9.259069,
+          "bottom_right_y": 9.259069,
+          "bottom_left_x": 9.259069,
+          "bottom_left_y": 9.259069
+        },
+        {
+          "top_left_x": 3.638935,
+          "top_left_y": 3.638935,
+          "top_right_x": 3.638935,
+          "top_right_y": 3.638935,
+          "bottom_right_x": 7.27787,
+          "bottom_right_y": 7.27787,
+          "bottom_left_x": 7.27787,
+          "bottom_left_y": 7.27787
+        },
+        {
+          "top_left_x": 2.8209057,
+          "top_left_y": 2.8209057,
+          "top_right_x": 2.8209057,
+          "top_right_y": 2.8209057,
+          "bottom_right_x": 5.6418114,
+          "bottom_right_y": 5.6418114,
+          "bottom_left_x": 5.6418114,
+          "bottom_left_y": 5.6418114
+        },
+        {
+          "top_left_x": 2.1620893,
+          "top_left_y": 2.1620893,
+          "top_right_x": 2.1620893,
+          "top_right_y": 2.1620893,
+          "bottom_right_x": 4.3241787,
+          "bottom_right_y": 4.3241787,
+          "bottom_left_x": 4.3241787,
+          "bottom_left_y": 4.3241787
+        },
+        {
+          "top_left_x": 1.6414614,
+          "top_left_y": 1.6414614,
+          "top_right_x": 1.6414614,
+          "top_right_y": 1.6414614,
+          "bottom_right_x": 3.2829227,
+          "bottom_right_y": 3.2829227,
+          "bottom_left_x": 3.2829227,
+          "bottom_left_y": 3.2829227
+        },
+        {
+          "top_left_x": 1.2361269,
+          "top_left_y": 1.2361269,
+          "top_right_x": 1.2361269,
+          "top_right_y": 1.2361269,
+          "bottom_right_x": 2.4722538,
+          "bottom_right_y": 2.4722538,
+          "bottom_left_x": 2.4722538,
+          "bottom_left_y": 2.4722538
+        },
+        {
+          "top_left_x": 0.92435074,
+          "top_left_y": 0.92435074,
+          "top_right_x": 0.92435074,
+          "top_right_y": 0.92435074,
+          "bottom_right_x": 1.8487015,
+          "bottom_right_y": 1.8487015,
+          "bottom_left_x": 1.8487015,
+          "bottom_left_y": 1.8487015
+        },
+        {
+          "top_left_x": 0.68693924,
+          "top_left_y": 0.68693924,
+          "top_right_x": 0.68693924,
+          "top_right_y": 0.68693924,
+          "bottom_right_x": 1.3738785,
+          "bottom_right_y": 1.3738785,
+          "bottom_left_x": 1.3738785,
+          "bottom_left_y": 1.3738785
+        },
+        {
+          "top_left_x": 0.5076904,
+          "top_left_y": 0.5076904,
+          "top_right_x": 0.5076904,
+          "top_right_y": 0.5076904,
+          "bottom_right_x": 1.0153809,
+          "bottom_right_y": 1.0153809,
+          "bottom_left_x": 1.0153809,
+          "bottom_left_y": 1.0153809
+        },
+        {
+          "top_left_x": 0.3733511,
+          "top_left_y": 0.3733511,
+          "top_right_x": 0.3733511,
+          "top_right_y": 0.3733511,
+          "bottom_right_x": 0.7467022,
+          "bottom_right_y": 0.7467022,
+          "bottom_left_x": 0.7467022,
+          "bottom_left_y": 0.7467022
+        },
+        {
+          "top_left_x": 0.27331638,
+          "top_left_y": 0.27331638,
+          "top_right_x": 0.27331638,
+          "top_right_y": 0.27331638,
+          "bottom_right_x": 0.54663277,
+          "bottom_right_y": 0.54663277,
+          "bottom_left_x": 0.54663277,
+          "bottom_left_y": 0.54663277
+        },
+        {
+          "top_left_x": 0.19925308,
+          "top_left_y": 0.19925308,
+          "top_right_x": 0.19925308,
+          "top_right_y": 0.19925308,
+          "bottom_right_x": 0.39850616,
+          "bottom_right_y": 0.39850616,
+          "bottom_left_x": 0.39850616,
+          "bottom_left_y": 0.39850616
+        },
+        {
+          "top_left_x": 0.14470005,
+          "top_left_y": 0.14470005,
+          "top_right_x": 0.14470005,
+          "top_right_y": 0.14470005,
+          "bottom_right_x": 0.2894001,
+          "bottom_right_y": 0.2894001,
+          "bottom_left_x": 0.2894001,
+          "bottom_left_y": 0.2894001
+        },
+        {
+          "top_left_x": 0.10470486,
+          "top_left_y": 0.10470486,
+          "top_right_x": 0.10470486,
+          "top_right_y": 0.10470486,
+          "bottom_right_x": 0.20940971,
+          "bottom_right_y": 0.20940971,
+          "bottom_left_x": 0.20940971,
+          "bottom_left_y": 0.20940971
+        },
+        {
+          "top_left_x": 0.07550812,
+          "top_left_y": 0.07550812,
+          "top_right_x": 0.07550812,
+          "top_right_y": 0.07550812,
+          "bottom_right_x": 0.15101624,
+          "bottom_right_y": 0.15101624,
+          "bottom_left_x": 0.15101624,
+          "bottom_left_y": 0.15101624
+        }
+      ]
+    },
+    {
+      "name": "alpha",
+      "type": "int",
+      "data_points": [
+        0,
+        45,
+        126,
+        190,
+        228,
+        246,
+        253,
+        255,
+        255,
+        255,
+        249,
+        226,
+        192,
+        153,
+        112,
+        72,
+        34,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json b/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json
index 608e633..aa80445 100644
--- a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json
+++ b/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning.json
@@ -1,25 +1,30 @@
 {
   "frame_ids": [
-    "before",
     0,
-    26,
-    52,
-    78,
-    105,
-    131,
-    157,
-    184,
-    210,
-    236,
-    263,
-    289,
-    315,
-    342,
-    368,
-    394,
-    421,
-    447,
-    473,
+    20,
+    40,
+    60,
+    80,
+    100,
+    120,
+    140,
+    160,
+    180,
+    200,
+    220,
+    240,
+    260,
+    280,
+    300,
+    320,
+    340,
+    360,
+    380,
+    400,
+    420,
+    440,
+    460,
+    480,
     500
   ],
   "features": [
@@ -28,70 +33,82 @@
       "type": "rect",
       "data_points": [
         {
-          "left": 0,
-          "top": 0,
-          "right": 0,
-          "bottom": 0
-        },
-        {
           "left": 100,
           "top": 300,
           "right": 200,
           "bottom": 400
         },
         {
-          "left": 98,
-          "top": 293,
-          "right": 203,
-          "bottom": 407
+          "left": 99,
+          "top": 296,
+          "right": 202,
+          "bottom": 404
         },
         {
-          "left": 91,
-          "top": 269,
-          "right": 213,
-          "bottom": 430
+          "left": 95,
+          "top": 283,
+          "right": 207,
+          "bottom": 417
         },
         {
-          "left": 71,
-          "top": 206,
-          "right": 240,
-          "bottom": 491
+          "left": 86,
+          "top": 256,
+          "right": 219,
+          "bottom": 443
         },
         {
-          "left": 34,
-          "top": 98,
-          "right": 283,
-          "bottom": 595
+          "left": 68,
+          "top": 198,
+          "right": 243,
+          "bottom": 499
         },
         {
-          "left": 22,
-          "top": 63,
-          "right": 296,
-          "bottom": 629
+          "left": 39,
+          "top": 110,
+          "right": 278,
+          "bottom": 584
+        },
+        {
+          "left": 26,
+          "top": 74,
+          "right": 292,
+          "bottom": 618
+        },
+        {
+          "left": 19,
+          "top": 55,
+          "right": 299,
+          "bottom": 637
         },
         {
           "left": 15,
-          "top": 44,
-          "right": 303,
-          "bottom": 648
+          "top": 42,
+          "right": 304,
+          "bottom": 649
         },
         {
-          "left": 11,
-          "top": 32,
-          "right": 308,
-          "bottom": 659
+          "left": 12,
+          "top": 33,
+          "right": 307,
+          "bottom": 658
         },
         {
-          "left": 8,
-          "top": 23,
-          "right": 311,
-          "bottom": 667
+          "left": 9,
+          "top": 27,
+          "right": 310,
+          "bottom": 664
+        },
+        {
+          "left": 7,
+          "top": 21,
+          "right": 312,
+          "bottom": 669
         },
         {
           "left": 6,
-          "top": 18,
-          "right": 313,
-          "bottom": 673
+          "top": 17,
+          "right": 314,
+          "bottom": 674
         },
         {
           "left": 5,
@@ -100,16 +117,22 @@
           "bottom": 677
         },
         {
-          "left": 3,
-          "top": 9,
+          "left": 4,
+          "top": 10,
           "right": 316,
-          "bottom": 681
+          "bottom": 680
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 317,
+          "bottom": 682
         },
         {
           "left": 2,
-          "top": 7,
-          "right": 317,
-          "bottom": 683
+          "top": 6,
+          "right": 318,
+          "bottom": 684
         },
         {
           "left": 2,
@@ -119,7 +142,7 @@
         },
         {
           "left": 1,
-          "top": 3,
+          "top": 4,
           "right": 319,
           "bottom": 687
         },
@@ -130,6 +153,18 @@
           "bottom": 688
         },
         {
+          "left": 1,
+          "top": 2,
+          "right": 319,
+          "bottom": 688
+        },
+        {
+          "left": 0,
+          "top": 1,
+          "right": 320,
+          "bottom": 689
+        },
+        {
           "left": 0,
           "top": 1,
           "right": 320,
@@ -159,7 +194,6 @@
       "name": "corner_radii",
       "type": "cornerRadii",
       "data_points": [
-        null,
         {
           "top_left_x": 10,
           "top_left_y": 10,
@@ -171,184 +205,244 @@
           "bottom_left_y": 20
         },
         {
-          "top_left_x": 9.762664,
-          "top_left_y": 9.762664,
-          "top_right_x": 9.762664,
-          "top_right_y": 9.762664,
-          "bottom_right_x": 19.525328,
-          "bottom_right_y": 19.525328,
-          "bottom_left_x": 19.525328,
-          "bottom_left_y": 19.525328
+          "top_left_x": 9.865689,
+          "top_left_y": 9.865689,
+          "top_right_x": 9.865689,
+          "top_right_y": 9.865689,
+          "bottom_right_x": 19.731379,
+          "bottom_right_y": 19.731379,
+          "bottom_left_x": 19.731379,
+          "bottom_left_y": 19.731379
         },
         {
-          "top_left_x": 8.969244,
-          "top_left_y": 8.969244,
-          "top_right_x": 8.969244,
-          "top_right_y": 8.969244,
-          "bottom_right_x": 17.938488,
-          "bottom_right_y": 17.938488,
-          "bottom_left_x": 17.938488,
-          "bottom_left_y": 17.938488
+          "top_left_x": 9.419104,
+          "top_left_y": 9.419104,
+          "top_right_x": 9.419104,
+          "top_right_y": 9.419104,
+          "bottom_right_x": 18.838207,
+          "bottom_right_y": 18.838207,
+          "bottom_left_x": 18.838207,
+          "bottom_left_y": 18.838207
         },
         {
-          "top_left_x": 6.8709626,
-          "top_left_y": 6.8709626,
-          "top_right_x": 6.8709626,
-          "top_right_y": 6.8709626,
-          "bottom_right_x": 13.741925,
-          "bottom_right_y": 13.741925,
-          "bottom_left_x": 13.741925,
-          "bottom_left_y": 13.741925
+          "top_left_x": 8.533693,
+          "top_left_y": 8.533693,
+          "top_right_x": 8.533693,
+          "top_right_y": 8.533693,
+          "bottom_right_x": 17.067387,
+          "bottom_right_y": 17.067387,
+          "bottom_left_x": 17.067387,
+          "bottom_left_y": 17.067387
         },
         {
-          "top_left_x": 3.260561,
-          "top_left_y": 3.260561,
-          "top_right_x": 3.260561,
-          "top_right_y": 3.260561,
-          "bottom_right_x": 6.521122,
-          "bottom_right_y": 6.521122,
-          "bottom_left_x": 6.521122,
-          "bottom_left_y": 6.521122
+          "top_left_x": 6.5919456,
+          "top_left_y": 6.5919456,
+          "top_right_x": 6.5919456,
+          "top_right_y": 6.5919456,
+          "bottom_right_x": 13.183891,
+          "bottom_right_y": 13.183891,
+          "bottom_left_x": 13.183891,
+          "bottom_left_y": 13.183891
         },
         {
-          "top_left_x": 2.0915751,
-          "top_left_y": 2.0915751,
-          "top_right_x": 2.0915751,
-          "top_right_y": 2.0915751,
-          "bottom_right_x": 4.1831503,
-          "bottom_right_y": 4.1831503,
-          "bottom_left_x": 4.1831503,
-          "bottom_left_y": 4.1831503
+          "top_left_x": 3.6674318,
+          "top_left_y": 3.6674318,
+          "top_right_x": 3.6674318,
+          "top_right_y": 3.6674318,
+          "bottom_right_x": 7.3348637,
+          "bottom_right_y": 7.3348637,
+          "bottom_left_x": 7.3348637,
+          "bottom_left_y": 7.3348637
         },
         {
-          "top_left_x": 1.4640827,
-          "top_left_y": 1.4640827,
-          "top_right_x": 1.4640827,
-          "top_right_y": 1.4640827,
-          "bottom_right_x": 2.9281654,
-          "bottom_right_y": 2.9281654,
-          "bottom_left_x": 2.9281654,
-          "bottom_left_y": 2.9281654
+          "top_left_x": 2.4832253,
+          "top_left_y": 2.4832253,
+          "top_right_x": 2.4832253,
+          "top_right_y": 2.4832253,
+          "bottom_right_x": 4.9664507,
+          "bottom_right_y": 4.9664507,
+          "bottom_left_x": 4.9664507,
+          "bottom_left_y": 4.9664507
         },
         {
-          "top_left_x": 1.057313,
-          "top_left_y": 1.057313,
-          "top_right_x": 1.057313,
-          "top_right_y": 1.057313,
-          "bottom_right_x": 2.114626,
-          "bottom_right_y": 2.114626,
-          "bottom_left_x": 2.114626,
-          "bottom_left_y": 2.114626
+          "top_left_x": 1.8252907,
+          "top_left_y": 1.8252907,
+          "top_right_x": 1.8252907,
+          "top_right_y": 1.8252907,
+          "bottom_right_x": 3.6505814,
+          "bottom_right_y": 3.6505814,
+          "bottom_left_x": 3.6505814,
+          "bottom_left_y": 3.6505814
         },
         {
-          "top_left_x": 0.7824335,
-          "top_left_y": 0.7824335,
-          "top_right_x": 0.7824335,
-          "top_right_y": 0.7824335,
-          "bottom_right_x": 1.564867,
-          "bottom_right_y": 1.564867,
-          "bottom_left_x": 1.564867,
-          "bottom_left_y": 1.564867
+          "top_left_x": 1.4077549,
+          "top_left_y": 1.4077549,
+          "top_right_x": 1.4077549,
+          "top_right_y": 1.4077549,
+          "bottom_right_x": 2.8155098,
+          "bottom_right_y": 2.8155098,
+          "bottom_left_x": 2.8155098,
+          "bottom_left_y": 2.8155098
         },
         {
-          "top_left_x": 0.5863056,
-          "top_left_y": 0.5863056,
-          "top_right_x": 0.5863056,
-          "top_right_y": 0.5863056,
-          "bottom_right_x": 1.1726112,
-          "bottom_right_y": 1.1726112,
-          "bottom_left_x": 1.1726112,
-          "bottom_left_y": 1.1726112
+          "top_left_x": 1.1067667,
+          "top_left_y": 1.1067667,
+          "top_right_x": 1.1067667,
+          "top_right_y": 1.1067667,
+          "bottom_right_x": 2.2135334,
+          "bottom_right_y": 2.2135334,
+          "bottom_left_x": 2.2135334,
+          "bottom_left_y": 2.2135334
         },
         {
-          "top_left_x": 0.4332962,
-          "top_left_y": 0.4332962,
-          "top_right_x": 0.4332962,
-          "top_right_y": 0.4332962,
-          "bottom_right_x": 0.8665924,
-          "bottom_right_y": 0.8665924,
-          "bottom_left_x": 0.8665924,
-          "bottom_left_y": 0.8665924
+          "top_left_x": 0.88593864,
+          "top_left_y": 0.88593864,
+          "top_right_x": 0.88593864,
+          "top_right_y": 0.88593864,
+          "bottom_right_x": 1.7718773,
+          "bottom_right_y": 1.7718773,
+          "bottom_left_x": 1.7718773,
+          "bottom_left_y": 1.7718773
         },
         {
-          "top_left_x": 0.3145876,
-          "top_left_y": 0.3145876,
-          "top_right_x": 0.3145876,
-          "top_right_y": 0.3145876,
-          "bottom_right_x": 0.6291752,
-          "bottom_right_y": 0.6291752,
-          "bottom_left_x": 0.6291752,
-          "bottom_left_y": 0.6291752
+          "top_left_x": 0.7069988,
+          "top_left_y": 0.7069988,
+          "top_right_x": 0.7069988,
+          "top_right_y": 0.7069988,
+          "bottom_right_x": 1.4139977,
+          "bottom_right_y": 1.4139977,
+          "bottom_left_x": 1.4139977,
+          "bottom_left_y": 1.4139977
         },
         {
-          "top_left_x": 0.22506618,
-          "top_left_y": 0.22506618,
-          "top_right_x": 0.22506618,
-          "top_right_y": 0.22506618,
-          "bottom_right_x": 0.45013237,
-          "bottom_right_y": 0.45013237,
-          "bottom_left_x": 0.45013237,
-          "bottom_left_y": 0.45013237
+          "top_left_x": 0.55613136,
+          "top_left_y": 0.55613136,
+          "top_right_x": 0.55613136,
+          "top_right_y": 0.55613136,
+          "bottom_right_x": 1.1122627,
+          "bottom_right_y": 1.1122627,
+          "bottom_left_x": 1.1122627,
+          "bottom_left_y": 1.1122627
         },
         {
-          "top_left_x": 0.15591621,
-          "top_left_y": 0.15591621,
-          "top_right_x": 0.15591621,
-          "top_right_y": 0.15591621,
-          "bottom_right_x": 0.31183243,
-          "bottom_right_y": 0.31183243,
-          "bottom_left_x": 0.31183243,
-          "bottom_left_y": 0.31183243
+          "top_left_x": 0.44889355,
+          "top_left_y": 0.44889355,
+          "top_right_x": 0.44889355,
+          "top_right_y": 0.44889355,
+          "bottom_right_x": 0.8977871,
+          "bottom_right_y": 0.8977871,
+          "bottom_left_x": 0.8977871,
+          "bottom_left_y": 0.8977871
         },
         {
-          "top_left_x": 0.100948334,
-          "top_left_y": 0.100948334,
-          "top_right_x": 0.100948334,
-          "top_right_y": 0.100948334,
-          "bottom_right_x": 0.20189667,
-          "bottom_right_y": 0.20189667,
-          "bottom_left_x": 0.20189667,
-          "bottom_left_y": 0.20189667
+          "top_left_x": 0.34557533,
+          "top_left_y": 0.34557533,
+          "top_right_x": 0.34557533,
+          "top_right_y": 0.34557533,
+          "bottom_right_x": 0.69115067,
+          "bottom_right_y": 0.69115067,
+          "bottom_left_x": 0.69115067,
+          "bottom_left_y": 0.69115067
         },
         {
-          "top_left_x": 0.06496239,
-          "top_left_y": 0.06496239,
-          "top_right_x": 0.06496239,
-          "top_right_y": 0.06496239,
-          "bottom_right_x": 0.12992477,
-          "bottom_right_y": 0.12992477,
-          "bottom_left_x": 0.12992477,
-          "bottom_left_y": 0.12992477
+          "top_left_x": 0.27671337,
+          "top_left_y": 0.27671337,
+          "top_right_x": 0.27671337,
+          "top_right_y": 0.27671337,
+          "bottom_right_x": 0.55342674,
+          "bottom_right_y": 0.55342674,
+          "bottom_left_x": 0.55342674,
+          "bottom_left_y": 0.55342674
         },
         {
-          "top_left_x": 0.03526497,
-          "top_left_y": 0.03526497,
-          "top_right_x": 0.03526497,
-          "top_right_y": 0.03526497,
-          "bottom_right_x": 0.07052994,
-          "bottom_right_y": 0.07052994,
-          "bottom_left_x": 0.07052994,
-          "bottom_left_y": 0.07052994
+          "top_left_x": 0.20785141,
+          "top_left_y": 0.20785141,
+          "top_right_x": 0.20785141,
+          "top_right_y": 0.20785141,
+          "bottom_right_x": 0.41570282,
+          "bottom_right_y": 0.41570282,
+          "bottom_left_x": 0.41570282,
+          "bottom_left_y": 0.41570282
         },
         {
-          "top_left_x": 0.014661789,
-          "top_left_y": 0.014661789,
-          "top_right_x": 0.014661789,
-          "top_right_y": 0.014661789,
-          "bottom_right_x": 0.029323578,
-          "bottom_right_y": 0.029323578,
-          "bottom_left_x": 0.029323578,
-          "bottom_left_y": 0.029323578
+          "top_left_x": 0.1601448,
+          "top_left_y": 0.1601448,
+          "top_right_x": 0.1601448,
+          "top_right_y": 0.1601448,
+          "bottom_right_x": 0.3202896,
+          "bottom_right_y": 0.3202896,
+          "bottom_left_x": 0.3202896,
+          "bottom_left_y": 0.3202896
         },
         {
-          "top_left_x": 0.0041856766,
-          "top_left_y": 0.0041856766,
-          "top_right_x": 0.0041856766,
-          "top_right_y": 0.0041856766,
-          "bottom_right_x": 0.008371353,
-          "bottom_right_y": 0.008371353,
-          "bottom_left_x": 0.008371353,
-          "bottom_left_y": 0.008371353
+          "top_left_x": 0.117860794,
+          "top_left_y": 0.117860794,
+          "top_right_x": 0.117860794,
+          "top_right_y": 0.117860794,
+          "bottom_right_x": 0.23572159,
+          "bottom_right_y": 0.23572159,
+          "bottom_left_x": 0.23572159,
+          "bottom_left_y": 0.23572159
+        },
+        {
+          "top_left_x": 0.08036041,
+          "top_left_y": 0.08036041,
+          "top_right_x": 0.08036041,
+          "top_right_y": 0.08036041,
+          "bottom_right_x": 0.16072083,
+          "bottom_right_y": 0.16072083,
+          "bottom_left_x": 0.16072083,
+          "bottom_left_y": 0.16072083
+        },
+        {
+          "top_left_x": 0.05836296,
+          "top_left_y": 0.05836296,
+          "top_right_x": 0.05836296,
+          "top_right_y": 0.05836296,
+          "bottom_right_x": 0.11672592,
+          "bottom_right_y": 0.11672592,
+          "bottom_left_x": 0.11672592,
+          "bottom_left_y": 0.11672592
+        },
+        {
+          "top_left_x": 0.03636551,
+          "top_left_y": 0.03636551,
+          "top_right_x": 0.03636551,
+          "top_right_y": 0.03636551,
+          "bottom_right_x": 0.07273102,
+          "bottom_right_y": 0.07273102,
+          "bottom_left_x": 0.07273102,
+          "bottom_left_y": 0.07273102
+        },
+        {
+          "top_left_x": 0.018137932,
+          "top_left_y": 0.018137932,
+          "top_right_x": 0.018137932,
+          "top_right_y": 0.018137932,
+          "bottom_right_x": 0.036275864,
+          "bottom_right_y": 0.036275864,
+          "bottom_left_x": 0.036275864,
+          "bottom_left_y": 0.036275864
+        },
+        {
+          "top_left_x": 0.0082063675,
+          "top_left_y": 0.0082063675,
+          "top_right_x": 0.0082063675,
+          "top_right_y": 0.0082063675,
+          "bottom_right_x": 0.016412735,
+          "bottom_right_y": 0.016412735,
+          "bottom_left_x": 0.016412735,
+          "bottom_left_y": 0.016412735
+        },
+        {
+          "top_left_x": 0.0031013489,
+          "top_left_y": 0.0031013489,
+          "top_right_x": 0.0031013489,
+          "top_right_y": 0.0031013489,
+          "bottom_right_x": 0.0062026978,
+          "bottom_right_y": 0.0062026978,
+          "bottom_left_x": 0.0062026978,
+          "bottom_left_y": 0.0062026978
         },
         {
           "top_left_x": 0,
@@ -367,19 +461,24 @@
       "type": "int",
       "data_points": [
         0,
+        96,
+        153,
+        192,
+        220,
+        238,
+        249,
+        254,
+        233,
+        191,
+        153,
+        117,
+        85,
+        57,
+        33,
+        14,
+        3,
         0,
-        115,
-        178,
-        217,
-        241,
-        253,
-        239,
-        183,
-        135,
-        91,
-        53,
-        23,
-        5,
+        0,
         0,
         0,
         0,
diff --git a/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json b/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json
new file mode 100644
index 0000000..a840d3c
--- /dev/null
+++ b/packages/SystemUI/tests/goldens/backgroundAnimation_whenReturning_withSpring.json
@@ -0,0 +1,375 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304
+  ],
+  "features": [
+    {
+      "name": "bounds",
+      "type": "rect",
+      "data_points": [
+        {
+          "left": 0,
+          "top": 0,
+          "right": 0,
+          "bottom": 0
+        },
+        {
+          "left": 94,
+          "top": 284,
+          "right": 206,
+          "bottom": 414
+        },
+        {
+          "left": 83,
+          "top": 251,
+          "right": 219,
+          "bottom": 447
+        },
+        {
+          "left": 70,
+          "top": 212,
+          "right": 234,
+          "bottom": 485
+        },
+        {
+          "left": 57,
+          "top": 173,
+          "right": 250,
+          "bottom": 522
+        },
+        {
+          "left": 46,
+          "top": 139,
+          "right": 264,
+          "bottom": 555
+        },
+        {
+          "left": 36,
+          "top": 109,
+          "right": 276,
+          "bottom": 584
+        },
+        {
+          "left": 28,
+          "top": 84,
+          "right": 285,
+          "bottom": 608
+        },
+        {
+          "left": 21,
+          "top": 65,
+          "right": 293,
+          "bottom": 627
+        },
+        {
+          "left": 16,
+          "top": 49,
+          "right": 300,
+          "bottom": 642
+        },
+        {
+          "left": 12,
+          "top": 36,
+          "right": 305,
+          "bottom": 653
+        },
+        {
+          "left": 9,
+          "top": 27,
+          "right": 308,
+          "bottom": 662
+        },
+        {
+          "left": 7,
+          "top": 20,
+          "right": 312,
+          "bottom": 669
+        },
+        {
+          "left": 5,
+          "top": 14,
+          "right": 314,
+          "bottom": 675
+        },
+        {
+          "left": 4,
+          "top": 11,
+          "right": 315,
+          "bottom": 678
+        },
+        {
+          "left": 3,
+          "top": 8,
+          "right": 316,
+          "bottom": 681
+        },
+        {
+          "left": 2,
+          "top": 5,
+          "right": 317,
+          "bottom": 684
+        },
+        {
+          "left": 1,
+          "top": 4,
+          "right": 318,
+          "bottom": 685
+        },
+        {
+          "left": 1,
+          "top": 3,
+          "right": 318,
+          "bottom": 686
+        },
+        {
+          "left": 0,
+          "top": 2,
+          "right": 319,
+          "bottom": 687
+        }
+      ]
+    },
+    {
+      "name": "corner_radii",
+      "type": "cornerRadii",
+      "data_points": [
+        null,
+        {
+          "top_left_x": 9.492916,
+          "top_left_y": 9.492916,
+          "top_right_x": 9.492916,
+          "top_right_y": 9.492916,
+          "bottom_right_x": 18.985832,
+          "bottom_right_y": 18.985832,
+          "bottom_left_x": 18.985832,
+          "bottom_left_y": 18.985832
+        },
+        {
+          "top_left_x": 8.381761,
+          "top_left_y": 8.381761,
+          "top_right_x": 8.381761,
+          "top_right_y": 8.381761,
+          "bottom_right_x": 16.763521,
+          "bottom_right_y": 16.763521,
+          "bottom_left_x": 16.763521,
+          "bottom_left_y": 16.763521
+        },
+        {
+          "top_left_x": 7.07397,
+          "top_left_y": 7.07397,
+          "top_right_x": 7.07397,
+          "top_right_y": 7.07397,
+          "bottom_right_x": 14.14794,
+          "bottom_right_y": 14.14794,
+          "bottom_left_x": 14.14794,
+          "bottom_left_y": 14.14794
+        },
+        {
+          "top_left_x": 5.7880254,
+          "top_left_y": 5.7880254,
+          "top_right_x": 5.7880254,
+          "top_right_y": 5.7880254,
+          "bottom_right_x": 11.576051,
+          "bottom_right_y": 11.576051,
+          "bottom_left_x": 11.576051,
+          "bottom_left_y": 11.576051
+        },
+        {
+          "top_left_x": 4.6295347,
+          "top_left_y": 4.6295347,
+          "top_right_x": 4.6295347,
+          "top_right_y": 4.6295347,
+          "bottom_right_x": 9.259069,
+          "bottom_right_y": 9.259069,
+          "bottom_left_x": 9.259069,
+          "bottom_left_y": 9.259069
+        },
+        {
+          "top_left_x": 3.638935,
+          "top_left_y": 3.638935,
+          "top_right_x": 3.638935,
+          "top_right_y": 3.638935,
+          "bottom_right_x": 7.27787,
+          "bottom_right_y": 7.27787,
+          "bottom_left_x": 7.27787,
+          "bottom_left_y": 7.27787
+        },
+        {
+          "top_left_x": 2.8209057,
+          "top_left_y": 2.8209057,
+          "top_right_x": 2.8209057,
+          "top_right_y": 2.8209057,
+          "bottom_right_x": 5.6418114,
+          "bottom_right_y": 5.6418114,
+          "bottom_left_x": 5.6418114,
+          "bottom_left_y": 5.6418114
+        },
+        {
+          "top_left_x": 2.1620893,
+          "top_left_y": 2.1620893,
+          "top_right_x": 2.1620893,
+          "top_right_y": 2.1620893,
+          "bottom_right_x": 4.3241787,
+          "bottom_right_y": 4.3241787,
+          "bottom_left_x": 4.3241787,
+          "bottom_left_y": 4.3241787
+        },
+        {
+          "top_left_x": 1.6414614,
+          "top_left_y": 1.6414614,
+          "top_right_x": 1.6414614,
+          "top_right_y": 1.6414614,
+          "bottom_right_x": 3.2829227,
+          "bottom_right_y": 3.2829227,
+          "bottom_left_x": 3.2829227,
+          "bottom_left_y": 3.2829227
+        },
+        {
+          "top_left_x": 1.2361269,
+          "top_left_y": 1.2361269,
+          "top_right_x": 1.2361269,
+          "top_right_y": 1.2361269,
+          "bottom_right_x": 2.4722538,
+          "bottom_right_y": 2.4722538,
+          "bottom_left_x": 2.4722538,
+          "bottom_left_y": 2.4722538
+        },
+        {
+          "top_left_x": 0.92435074,
+          "top_left_y": 0.92435074,
+          "top_right_x": 0.92435074,
+          "top_right_y": 0.92435074,
+          "bottom_right_x": 1.8487015,
+          "bottom_right_y": 1.8487015,
+          "bottom_left_x": 1.8487015,
+          "bottom_left_y": 1.8487015
+        },
+        {
+          "top_left_x": 0.68693924,
+          "top_left_y": 0.68693924,
+          "top_right_x": 0.68693924,
+          "top_right_y": 0.68693924,
+          "bottom_right_x": 1.3738785,
+          "bottom_right_y": 1.3738785,
+          "bottom_left_x": 1.3738785,
+          "bottom_left_y": 1.3738785
+        },
+        {
+          "top_left_x": 0.5076904,
+          "top_left_y": 0.5076904,
+          "top_right_x": 0.5076904,
+          "top_right_y": 0.5076904,
+          "bottom_right_x": 1.0153809,
+          "bottom_right_y": 1.0153809,
+          "bottom_left_x": 1.0153809,
+          "bottom_left_y": 1.0153809
+        },
+        {
+          "top_left_x": 0.3733511,
+          "top_left_y": 0.3733511,
+          "top_right_x": 0.3733511,
+          "top_right_y": 0.3733511,
+          "bottom_right_x": 0.7467022,
+          "bottom_right_y": 0.7467022,
+          "bottom_left_x": 0.7467022,
+          "bottom_left_y": 0.7467022
+        },
+        {
+          "top_left_x": 0.27331638,
+          "top_left_y": 0.27331638,
+          "top_right_x": 0.27331638,
+          "top_right_y": 0.27331638,
+          "bottom_right_x": 0.54663277,
+          "bottom_right_y": 0.54663277,
+          "bottom_left_x": 0.54663277,
+          "bottom_left_y": 0.54663277
+        },
+        {
+          "top_left_x": 0.19925308,
+          "top_left_y": 0.19925308,
+          "top_right_x": 0.19925308,
+          "top_right_y": 0.19925308,
+          "bottom_right_x": 0.39850616,
+          "bottom_right_y": 0.39850616,
+          "bottom_left_x": 0.39850616,
+          "bottom_left_y": 0.39850616
+        },
+        {
+          "top_left_x": 0.14470005,
+          "top_left_y": 0.14470005,
+          "top_right_x": 0.14470005,
+          "top_right_y": 0.14470005,
+          "bottom_right_x": 0.2894001,
+          "bottom_right_y": 0.2894001,
+          "bottom_left_x": 0.2894001,
+          "bottom_left_y": 0.2894001
+        },
+        {
+          "top_left_x": 0.10470486,
+          "top_left_y": 0.10470486,
+          "top_right_x": 0.10470486,
+          "top_right_y": 0.10470486,
+          "bottom_right_x": 0.20940971,
+          "bottom_right_y": 0.20940971,
+          "bottom_left_x": 0.20940971,
+          "bottom_left_y": 0.20940971
+        },
+        {
+          "top_left_x": 0.07550812,
+          "top_left_y": 0.07550812,
+          "top_right_x": 0.07550812,
+          "top_right_y": 0.07550812,
+          "bottom_right_x": 0.15101624,
+          "bottom_right_y": 0.15101624,
+          "bottom_left_x": 0.15101624,
+          "bottom_left_y": 0.15101624
+        }
+      ]
+    },
+    {
+      "name": "alpha",
+      "type": "int",
+      "data_points": [
+        0,
+        45,
+        126,
+        190,
+        228,
+        246,
+        253,
+        255,
+        255,
+        255,
+        249,
+        226,
+        192,
+        153,
+        112,
+        72,
+        34,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml
new file mode 100644
index 0000000..d9ea0b9
--- /dev/null
+++ b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:drawable="@drawable/ic_brightness"/>
+    <item android:drawable="@drawable/ic_brightness"/>
+</layer-list>
diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml
new file mode 100644
index 0000000..796de8f
--- /dev/null
+++ b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <!-- dessert_flan is a PNG while ic_brightness is a level-list. -->
+    <item android:drawable="@drawable/dessert_flan"/>
+    <item android:drawable="@drawable/ic_brightness"/>
+</layer-list>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
deleted file mode 100644
index c51aa04..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.AnimatedStateListDrawable;
-import android.util.Pair;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ImageView;
-
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
-
-public class LegacyLockIconViewControllerBaseTest extends SysuiTestCase {
-    protected static final String UNLOCKED_LABEL = "unlocked";
-    protected static final String LOCKED_LABEL = "locked";
-    protected static final int PADDING = 10;
-
-    protected MockitoSession mStaticMockSession;
-
-    protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
-    protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
-    protected @Mock LockIconView mLockIconView;
-    protected @Mock ImageView mLockIcon;
-    protected @Mock AnimatedStateListDrawable mIconDrawable;
-    protected @Mock Context mContext;
-    protected @Mock Resources mResources;
-    protected @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager;
-    protected @Mock StatusBarStateController mStatusBarStateController;
-    protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    protected @Mock KeyguardViewController mKeyguardViewController;
-    protected @Mock KeyguardStateController mKeyguardStateController;
-    protected @Mock FalsingManager mFalsingManager;
-    protected @Mock AuthController mAuthController;
-    protected @Mock DumpManager mDumpManager;
-    protected @Mock AccessibilityManager mAccessibilityManager;
-    protected @Mock ConfigurationController mConfigurationController;
-    protected @Mock VibratorHelper mVibrator;
-    protected @Mock AuthRippleController mAuthRippleController;
-    protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
-    protected FakeFeatureFlags mFeatureFlags;
-
-    protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-
-    protected LegacyLockIconViewController mUnderTest;
-
-    // Capture listeners so that they can be used to send events
-    @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
-            ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
-    @Captor protected ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor =
-            ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
-    protected KeyguardStateController.Callback mKeyguardStateCallback;
-
-    @Captor protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor =
-            ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-    protected StatusBarStateController.StateListener mStatusBarStateListener;
-
-    @Captor protected ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor;
-    protected AuthController.Callback mAuthControllerCallback;
-
-    @Captor protected ArgumentCaptor<KeyguardUpdateMonitorCallback>
-            mKeyguardUpdateMonitorCallbackCaptor =
-            ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-    protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
-
-    @Captor protected ArgumentCaptor<Point> mPointCaptor;
-
-    @Before
-    public void setUp() throws Exception {
-        mStaticMockSession = mockitoSession()
-                .mockStatic(BurnInHelperKt.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-        MockitoAnnotations.initMocks(this);
-
-        setupLockIconViewMocks();
-        when(mContext.getResources()).thenReturn(mResources);
-        when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
-        Rect windowBounds = new Rect(0, 0, 800, 1200);
-        when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds);
-        when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL);
-        when(mResources.getString(R.string.accessibility_lock_icon)).thenReturn(LOCKED_LABEL);
-        when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable);
-        when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING);
-        when(mAuthController.getScaleFactor()).thenReturn(1f);
-
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
-        when(mStatusBarStateController.isDozing()).thenReturn(false);
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-
-        if (!SceneContainerFlag.isEnabled()) {
-            mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
-            //TODO move this to use @DisableFlags annotation if needed
-            mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
-        }
-
-        mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
-        mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
-
-        mUnderTest = new LegacyLockIconViewController(
-                mStatusBarStateController,
-                mKeyguardUpdateMonitor,
-                mKeyguardViewController,
-                mKeyguardStateController,
-                mFalsingManager,
-                mAuthController,
-                mDumpManager,
-                mAccessibilityManager,
-                mConfigurationController,
-                mDelayableExecutor,
-                mVibrator,
-                mAuthRippleController,
-                mResources,
-                mKosmos.getKeyguardTransitionInteractor(),
-                KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
-                mFeatureFlags,
-                mPrimaryBouncerInteractor,
-                mContext,
-                () -> mDeviceEntryInteractor
-        );
-    }
-
-    @After
-    public void tearDown() {
-        mStaticMockSession.finishMocking();
-    }
-
-    protected Pair<Float, Point> setupUdfps() {
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
-        final Point udfpsLocation = new Point(50, 75);
-        final float radius = 33f;
-        when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
-        when(mAuthController.getUdfpsRadius()).thenReturn(radius);
-
-        return new Pair(radius, udfpsLocation);
-    }
-
-    protected void setupShowLockIcon() {
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
-        when(mStatusBarStateController.isDozing()).thenReturn(false);
-        when(mStatusBarStateController.getDozeAmount()).thenReturn(0f);
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
-    }
-
-    protected void captureAuthControllerCallback() {
-        verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture());
-        mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue();
-    }
-
-    protected void captureKeyguardStateCallback() {
-        verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture());
-        mKeyguardStateCallback = mKeyguardStateCaptor.getValue();
-    }
-
-    protected void captureStatusBarStateListener() {
-        verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture());
-        mStatusBarStateListener = mStatusBarStateCaptor.getValue();
-    }
-
-    protected void captureKeyguardUpdateMonitorCallback() {
-        verify(mKeyguardUpdateMonitor).registerCallback(
-                mKeyguardUpdateMonitorCallbackCaptor.capture());
-        mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue();
-    }
-
-    protected void setupLockIconViewMocks() {
-        when(mLockIconView.getResources()).thenReturn(mResources);
-        when(mLockIconView.getContext()).thenReturn(mContext);
-        when(mLockIconView.getLockIcon()).thenReturn(mLockIcon);
-    }
-
-    protected void resetLockIconView() {
-        reset(mLockIconView);
-        setupLockIconViewMocks();
-    }
-
-    protected void init(boolean useDozeMigrationFlag) {
-        mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag);
-        mUnderTest.setLockIconView(mLockIconView);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
deleted file mode 100644
index c1ba39e..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.graphics.Point;
-import android.hardware.biometrics.BiometricSourceType;
-import android.testing.TestableLooper;
-import android.util.Pair;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class LegacyLockIconViewControllerTest extends LegacyLockIconViewControllerBaseTest {
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        when(mLockIconView.isAttachedToWindow()).thenReturn(true);
-    }
-
-    @Test
-    public void testUpdateFingerprintLocationOnInit() {
-        // GIVEN fp sensor location is available pre-attached
-        Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
-
-        // WHEN lock icon view controller is initialized and attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN lock icon view location is updated to the udfps location with UDFPS radius
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING));
-    }
-
-    @Test
-    public void testUpdatePaddingBasedOnResolutionScale() {
-        // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5
-        Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
-        when(mAuthController.getScaleFactor()).thenReturn(5f);
-
-        // WHEN lock icon view controller is initialized and attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN lock icon view location is updated with the scaled radius
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING * 5));
-    }
-
-    @Test
-    public void testUpdateLockIconLocationOnAuthenticatorsRegistered() {
-        // GIVEN fp sensor location is not available pre-init
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-        when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
-        init(/* useMigrationFlag= */false);
-        resetLockIconView(); // reset any method call counts for when we verify method calls later
-
-        // GIVEN fp sensor location is available post-attached
-        captureAuthControllerCallback();
-        Pair<Float, Point> udfps = setupUdfps();
-
-        // WHEN all authenticators are registered
-        mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
-        mDelayableExecutor.runAllReady();
-
-        // THEN lock icon view location is updated with the same coordinates as auth controller vals
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING));
-    }
-
-    @Test
-    public void testUpdateLockIconLocationOnUdfpsLocationChanged() {
-        // GIVEN fp sensor location is not available pre-init
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-        when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
-        init(/* useMigrationFlag= */false);
-        resetLockIconView(); // reset any method call counts for when we verify method calls later
-
-        // GIVEN fp sensor location is available post-attached
-        captureAuthControllerCallback();
-        Pair<Float, Point> udfps = setupUdfps();
-
-        // WHEN udfps location changes
-        mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams());
-        mDelayableExecutor.runAllReady();
-
-        // THEN lock icon view location is updated with the same coordinates as auth controller vals
-        verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
-                eq(PADDING));
-    }
-
-    @Test
-    public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
-        // GIVEN Udpfs sensor location is available
-        setupUdfps();
-
-        // WHEN the view is attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN the lock icon view background should be enabled
-        verify(mLockIconView).setUseBackground(true);
-    }
-
-    @Test
-    public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
-        // GIVEN Udfps sensor location is not supported
-        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-
-        // WHEN the view is attached
-        init(/* useMigrationFlag= */false);
-
-        // THEN the lock icon view background should be disabled
-        verify(mLockIconView).setUseBackground(false);
-    }
-
-    @Test
-    public void testLockIconStartState() {
-        // GIVEN lock icon state
-        setupShowLockIcon();
-
-        // WHEN lock icon controller is initialized
-        init(/* useMigrationFlag= */false);
-
-        // THEN the lock icon should show
-        verify(mLockIconView).updateIcon(ICON_LOCK, false);
-    }
-
-    @Test
-    public void testLockIcon_updateToUnlock() {
-        // GIVEN starting state for the lock icon
-        setupShowLockIcon();
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        reset(mLockIconView);
-
-        // WHEN the unlocked state changes to canDismissLockScreen=true
-        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the unlock should show
-        verify(mLockIconView).updateIcon(ICON_UNLOCK, false);
-    }
-
-    @Test
-    public void testLockIcon_clearsIconWhenUnlocked() {
-        // GIVEN udfps not enrolled
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false);
-
-        // GIVEN starting state for the lock icon
-        setupShowLockIcon();
-        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureStatusBarStateListener();
-        reset(mLockIconView);
-
-        // WHEN the dozing state changes
-        mStatusBarStateListener.onDozingChanged(false /* isDozing */);
-
-        // THEN the icon is cleared
-        verify(mLockIconView).clearIcon();
-    }
-
-    @Test
-    public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() {
-        // GIVEN udfps enrolled
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
-        // GIVEN starting state for the lock icon
-        setupShowLockIcon();
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureStatusBarStateListener();
-        reset(mLockIconView);
-
-        // WHEN the dozing state changes
-        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
-
-        // THEN the AOD lock icon should show
-        verify(mLockIconView).updateIcon(ICON_LOCK, true);
-    }
-
-    @Test
-    public void testBurnInOffsetsUpdated_onDozeAmountChanged() {
-        // GIVEN udfps enrolled
-        setupUdfps();
-        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
-        // GIVEN burn-in offset = 5
-        int burnInOffset = 5;
-        when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset);
-
-        // GIVEN starting state for the lock icon (keyguard)
-        setupShowLockIcon();
-        init(/* useMigrationFlag= */false);
-        captureStatusBarStateListener();
-        reset(mLockIconView);
-
-        // WHEN dozing updates
-        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
-        mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
-
-        // THEN the view's translation is updated to use the AoD burn-in offsets
-        verify(mLockIconView).setTranslationY(burnInOffset);
-        verify(mLockIconView).setTranslationX(burnInOffset);
-        reset(mLockIconView);
-
-        // WHEN the device is no longer dozing
-        mStatusBarStateListener.onDozingChanged(false /* isDozing */);
-        mStatusBarStateListener.onDozeAmountChanged(0f, 0f);
-
-        // THEN the view is updated to NO translation (no burn-in offsets anymore)
-        verify(mLockIconView).setTranslationY(0);
-        verify(mLockIconView).setTranslationX(0);
-    }
-
-    @Test
-    public void lockIconShows_afterUnlockStateChanges() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setContentDescription(LOCKED_LABEL);
-    }
-
-    @Test
-    public void lockIconAccessibility_notVisibleToUser() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-        when(mLockIconView.isVisibleToUser()).thenReturn(false);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-    }
-
-    @Test
-    public void lockIconAccessibility_bouncerAnimatingAway() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-        when(mLockIconView.isVisibleToUser()).thenReturn(true);
-        when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-    }
-
-    @Test
-    public void lockIconAccessibility_bouncerNotAnimatingAway_viewVisible() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardStateCallback();
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-        when(mLockIconView.isVisibleToUser()).thenReturn(true);
-        when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(false);
-
-        // WHEN the unlocked state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardStateCallback.onUnlockedChanged();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-    }
-
-    @Test
-    public void playHaptic_onTouchExploration_performHapticFeedback() {
-       // WHEN request to vibrate on touch exploration
-        mUnderTest.vibrateOnTouchExploration();
-
-        // THEN performHapticFeedback is used
-        verify(mVibrator).performHapticFeedback(any(), eq(HapticFeedbackConstants.CONTEXT_CLICK));
-    }
-
-    @Test
-    public void playHaptic_onLongPress_performHapticFeedback() {
-        // WHEN request to vibrate on long press
-        mUnderTest.vibrateOnLongPress();
-
-        // THEN uses perform haptic feedback
-        verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
-    }
-
-    @Test
-    public void longPress_showBouncer_sceneContainerNotEnabled() {
-        init(/* useMigrationFlag= */ false);
-        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
-        // WHEN longPress
-        mUnderTest.onLongPress();
-
-        // THEN show primary bouncer via keyguard view controller, not scene container
-        verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
-        verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
-    }
-
-    @Test
-    @EnableSceneContainer
-    public void longPress_showBouncer() {
-        init(/* useMigrationFlag= */ false);
-        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
-        // WHEN longPress
-        mUnderTest.onLongPress();
-
-        // THEN show primary bouncer
-        verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
-        verify(mDeviceEntryInteractor).attemptDeviceEntry();
-    }
-
-    @Test
-    @EnableSceneContainer
-    public void longPress_falsingTriggered_doesNotShowBouncer() {
-        init(/* useMigrationFlag= */ false);
-        when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
-
-        // WHEN longPress
-        mUnderTest.onLongPress();
-
-        // THEN don't show primary bouncer
-        verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
-        verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
deleted file mode 100644
index 2fd3cb0..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard
-
-import android.view.View
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.keyguard.LockIconView.ICON_LOCK
-import com.android.systemui.doze.util.getBurnInOffset
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControllerBaseTest() {
-
-    /** After migration, replaces LockIconViewControllerTest version */
-    @Test
-    fun testLockIcon_clearsIconWhenUnlocked() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN udfps not enrolled
-            setupUdfps()
-            whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false)
-
-            // GIVEN starting state for the lock icon
-            setupShowLockIcon()
-            whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE)
-
-            // GIVEN lock icon controller is initialized and view is attached
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN the dozing state changes
-            mUnderTest.mIsDozingCallback.accept(false)
-            // THEN the icon is cleared
-            verify(mLockIconView).clearIcon()
-        }
-
-    /** After migration, replaces LockIconViewControllerTest version */
-    @Test
-    fun testLockIcon_updateToAodLock_whenUdfpsEnrolled() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN udfps enrolled
-            setupUdfps()
-            whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
-            // GIVEN starting state for the lock icon
-            setupShowLockIcon()
-
-            // GIVEN lock icon controller is initialized and view is attached
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN the dozing state changes
-            mUnderTest.mIsDozingCallback.accept(true)
-
-            // THEN the AOD lock icon should show
-            verify(mLockIconView).updateIcon(ICON_LOCK, true)
-        }
-
-    /** After migration, replaces LockIconViewControllerTest version */
-    @Test
-    fun testBurnInOffsetsUpdated_onDozeAmountChanged() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN udfps enrolled
-            setupUdfps()
-            whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
-            // GIVEN burn-in offset = 5
-            val burnInOffset = 5
-            whenever(getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset)
-
-            // GIVEN starting state for the lock icon (keyguard)
-            setupShowLockIcon()
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN dozing updates
-            mUnderTest.mIsDozingCallback.accept(true)
-            mUnderTest.mDozeTransitionCallback.accept(1f)
-
-            // THEN the view's translation is updated to use the AoD burn-in offsets
-            verify(mLockIconView).setTranslationY(burnInOffset.toFloat())
-            verify(mLockIconView).setTranslationX(burnInOffset.toFloat())
-            reset(mLockIconView)
-
-            // WHEN the device is no longer dozing
-            mUnderTest.mIsDozingCallback.accept(false)
-            mUnderTest.mDozeTransitionCallback.accept(0f)
-
-            // THEN the view is updated to NO translation (no burn-in offsets anymore)
-            verify(mLockIconView).setTranslationY(0f)
-            verify(mLockIconView).setTranslationX(0f)
-        }
-
-    @Test
-    fun testHideLockIconView_onLockscreenHostedDreamStateChanged() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN starting state for the lock icon (keyguard) and wallpaper dream enabled
-            mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true)
-            setupShowLockIcon()
-            init(/* useMigrationFlag= */ true)
-            reset(mLockIconView)
-
-            // WHEN dream starts
-            mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
-                true /* isActiveDreamLockscreenHosted */
-            )
-
-            // THEN the lock icon is hidden
-            verify(mLockIconView).visibility = View.INVISIBLE
-            reset(mLockIconView)
-
-            // WHEN the device is no longer dreaming
-            mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
-                false /* isActiveDreamLockscreenHosted */
-            )
-
-            // THEN lock icon is visible
-            verify(mLockIconView).visibility = View.VISIBLE
-        }
-
-    companion object {
-        private val IMMEDIATE = Dispatchers.Main.immediate
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 5e37d4c..51a7b5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -338,6 +338,25 @@
         assertThat(mController.mFloatingMenu).isInstanceOf(MenuViewLayerController.class);
     }
 
+    @Test
+    public void onUserInitializationComplete_destroysOldWidget() {
+        enableAccessibilityFloatingMenuConfig();
+        mController = setUpController();
+
+        captureKeyguardUpdateMonitorCallback();
+        mKeyguardCallback.onUserUnlocked();
+        mKeyguardCallback.onKeyguardVisibilityChanged(false);
+
+        IAccessibilityFloatingMenu floatingMenu = mController.mFloatingMenu;
+
+        mController.mUserInitializationCompleteCallback
+                .onUserInitializationComplete(mContext.getUserId());
+        mTestableLooper.processAllMessages();
+
+        assertThat(mController.mFloatingMenu).isNotNull();
+        assertThat(mController.mFloatingMenu).isNotSameInstanceAs(floatingMenu);
+    }
+
     private AccessibilityFloatingMenuController setUpController() {
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
         final ViewCaptureAwareWindowManager viewCaptureAwareWindowManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index 6c42662..071acfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -16,50 +16,44 @@
 
 package com.android.systemui.animation
 
-import android.animation.AnimatorSet
+import android.animation.AnimatorRuleRecordingSpec
+import android.animation.AnimatorTestRuleToolkit
+import android.animation.MotionControl
+import android.animation.recordMotion
 import android.graphics.drawable.GradientDrawable
 import android.platform.test.annotations.MotionTest
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.test.ext.junit.rules.ActivityScenarioRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.EmptyTestActivity
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import kotlin.test.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import platform.test.motion.MotionTestRule
 import platform.test.motion.RecordedMotion
-import platform.test.motion.view.AnimationSampling.Companion.evenlySampled
 import platform.test.motion.view.DrawableFeatureCaptures
-import platform.test.motion.view.ViewRecordingSpec.Companion.captureWithoutScreenshot
-import platform.test.motion.view.ViewToolkit
-import platform.test.motion.view.record
-import platform.test.screenshot.DeviceEmulationRule
-import platform.test.screenshot.DeviceEmulationSpec
-import platform.test.screenshot.DisplaySpec
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 import platform.test.screenshot.GoldenPathManager
 import platform.test.screenshot.PathConfig
 
 @SmallTest
 @MotionTest
-@RunWith(AndroidJUnit4::class)
-class TransitionAnimatorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
     companion object {
         private const val GOLDENS_PATH = "frameworks/base/packages/SystemUI/tests/goldens"
 
-        private val emulationSpec =
-            DeviceEmulationSpec(
-                DisplaySpec(
-                    "phone",
-                    width = 320,
-                    height = 690,
-                    densityDpi = 160,
-                )
-            )
+        @get:Parameters(name = "{0}")
+        @JvmStatic
+        val useSpringValues = booleanArrayOf(false, true).toList()
     }
 
     private val kosmos = Kosmos()
@@ -68,32 +62,51 @@
         TransitionAnimator(
             kosmos.fakeExecutor,
             ActivityTransitionAnimator.TIMINGS,
-            ActivityTransitionAnimator.INTERPOLATORS
+            ActivityTransitionAnimator.INTERPOLATORS,
+            ActivityTransitionAnimator.SPRING_TIMINGS,
+            ActivityTransitionAnimator.SPRING_INTERPOLATORS,
         )
+    private val withSpring =
+        if (useSpring) {
+            "_withSpring"
+        } else {
+            ""
+        }
 
-    @get:Rule(order = 0) val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
     @get:Rule(order = 1) val activityRule = ActivityScenarioRule(EmptyTestActivity::class.java)
-    @get:Rule(order = 2)
-    val motionRule = MotionTestRule(ViewToolkit { activityRule.scenario }, pathManager)
+    @get:Rule(order = 2) val animatorTestRule = android.animation.AnimatorTestRule(this)
+    @get:Rule(order = 3)
+    val motionRule =
+        MotionTestRule(AnimatorTestRuleToolkit(animatorTestRule, kosmos.testScope), pathManager)
 
     @Test
     fun backgroundAnimation_whenLaunching() {
         val backgroundLayer = GradientDrawable().apply { alpha = 0 }
-        val animator = setUpTest(backgroundLayer, isLaunching = true)
+        val animator =
+            setUpTest(backgroundLayer, isLaunching = true).apply {
+                getInstrumentation().runOnMainSync { start() }
+            }
 
         val recordedMotion = recordMotion(backgroundLayer, animator)
 
-        motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden()
+        motionRule
+            .assertThat(recordedMotion)
+            .timeSeriesMatchesGolden("backgroundAnimation_whenLaunching$withSpring")
     }
 
     @Test
     fun backgroundAnimation_whenReturning() {
         val backgroundLayer = GradientDrawable().apply { alpha = 0 }
-        val animator = setUpTest(backgroundLayer, isLaunching = false)
+        val animator =
+            setUpTest(backgroundLayer, isLaunching = false).apply {
+                getInstrumentation().runOnMainSync { start() }
+            }
 
         val recordedMotion = recordMotion(backgroundLayer, animator)
 
-        motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden()
+        motionRule
+            .assertThat(recordedMotion)
+            .timeSeriesMatchesGolden("backgroundAnimation_whenReturning$withSpring")
     }
 
     @Test
@@ -101,10 +114,13 @@
         val backgroundLayer = GradientDrawable().apply { alpha = 0 }
         val animator =
             setUpTest(backgroundLayer, isLaunching = true, fadeWindowBackgroundLayer = false)
+                .apply { getInstrumentation().runOnMainSync { start() } }
 
         val recordedMotion = recordMotion(backgroundLayer, animator)
 
-        motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden()
+        motionRule
+            .assertThat(recordedMotion)
+            .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenLaunching$withSpring")
     }
 
     @Test
@@ -112,17 +128,20 @@
         val backgroundLayer = GradientDrawable().apply { alpha = 0 }
         val animator =
             setUpTest(backgroundLayer, isLaunching = false, fadeWindowBackgroundLayer = false)
+                .apply { getInstrumentation().runOnMainSync { start() } }
 
         val recordedMotion = recordMotion(backgroundLayer, animator)
 
-        motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden()
+        motionRule
+            .assertThat(recordedMotion)
+            .timeSeriesMatchesGolden("backgroundAnimationWithoutFade_whenReturning$withSpring")
     }
 
     private fun setUpTest(
         backgroundLayer: GradientDrawable,
         isLaunching: Boolean,
         fadeWindowBackgroundLayer: Boolean = true,
-    ): AnimatorSet {
+    ): TransitionAnimator.Animation {
         lateinit var transitionContainer: ViewGroup
         activityRule.scenario.onActivity { activity ->
             transitionContainer = FrameLayout(activity).apply { setBackgroundColor(0x00FF00) }
@@ -131,17 +150,14 @@
         waitForIdleSync()
 
         val controller = TestController(transitionContainer, isLaunching)
-        val animator =
-            transitionAnimator.createAnimator(
-                controller,
-                createEndState(transitionContainer),
-                backgroundLayer,
-                fadeWindowBackgroundLayer
-            )
-        return AnimatorSet().apply {
-            duration = animator.duration
-            play(animator)
-        }
+        return transitionAnimator.createAnimation(
+            controller,
+            controller.createAnimatorState(),
+            createEndState(transitionContainer),
+            backgroundLayer,
+            fadeWindowBackgroundLayer,
+            useSpring = useSpring,
+        )
     }
 
     private fun createEndState(container: ViewGroup): TransitionAnimator.State {
@@ -150,25 +166,44 @@
         return TransitionAnimator.State(
             left = containerLocation[0],
             top = containerLocation[1],
-            right = containerLocation[0] + emulationSpec.display.width,
-            bottom = containerLocation[1] + emulationSpec.display.height,
+            right = containerLocation[0] + 320,
+            bottom = containerLocation[1] + 690,
             topCornerRadius = 0f,
-            bottomCornerRadius = 0f
+            bottomCornerRadius = 0f,
         )
     }
 
     private fun recordMotion(
         backgroundLayer: GradientDrawable,
-        animator: AnimatorSet
+        animation: TransitionAnimator.Animation,
     ): RecordedMotion {
-        return motionRule.record(
-            animator,
-            backgroundLayer.captureWithoutScreenshot(evenlySampled(20)) {
-                feature(DrawableFeatureCaptures.bounds, "bounds")
-                feature(DrawableFeatureCaptures.cornerRadii, "corner_radii")
-                feature(DrawableFeatureCaptures.alpha, "alpha")
+        fun record(motionControl: MotionControl, sampleIntervalMs: Long): RecordedMotion {
+            return motionRule.recordMotion(
+                AnimatorRuleRecordingSpec(backgroundLayer, motionControl, sampleIntervalMs) {
+                    feature(DrawableFeatureCaptures.bounds, "bounds")
+                    feature(DrawableFeatureCaptures.cornerRadii, "corner_radii")
+                    feature(DrawableFeatureCaptures.alpha, "alpha")
+                }
+            )
+        }
+
+        val motionControl: MotionControl
+        val sampleIntervalMs: Long
+        if (useSpring) {
+            assertTrue { animation is TransitionAnimator.MultiSpringAnimation }
+            motionControl = MotionControl {
+                awaitCondition { (animation as TransitionAnimator.MultiSpringAnimation).isDone }
             }
-        )
+            sampleIntervalMs = 16L
+        } else {
+            assertTrue { animation is TransitionAnimator.InterpolatedAnimation }
+            motionControl = MotionControl { awaitFrames(count = 26) }
+            sampleIntervalMs = 20L
+        }
+
+        var recording: RecordedMotion? = null
+        getInstrumentation().runOnMainSync { recording = record(motionControl, sampleIntervalMs) }
+        return recording!!
     }
 }
 
@@ -178,7 +213,7 @@
  */
 private class TestController(
     override var transitionContainer: ViewGroup,
-    override val isLaunching: Boolean
+    override val isLaunching: Boolean,
 ) : TransitionAnimator.Controller {
     override fun createAnimatorState(): TransitionAnimator.State {
         val containerLocation = IntArray(2)
@@ -189,7 +224,7 @@
             right = containerLocation[0] + 200,
             bottom = containerLocation[1] + 400,
             topCornerRadius = 10f,
-            bottomCornerRadius = 20f
+            bottomCornerRadius = 20f,
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 476d6e3..7c08928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -103,11 +103,10 @@
     @Mock()
     private BroadcastDispatcher mDispatcher;
     @Mock(stubOnly = true)
-    private AudioManager.AudioRecordingCallback mRecordingCallback;
-    @Mock(stubOnly = true)
     private AudioRecordingConfiguration mPausedMockRecording;
 
     private AppOpsControllerImpl mController;
+    private AudioManager.AudioRecordingCallback mRecordingCallback;
     private TestableLooper mTestableLooper;
     private final FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
 
@@ -975,6 +974,7 @@
     }
 
     private void verifyUnPausedSentActive(int micOpCode) {
+        // Setup stubs the initial active recording list with a single silenced client
         mController.addCallback(new int[]{micOpCode}, mCallback);
         mBgExecutor.runAllReady();
         mTestableLooper.processAllMessages();
@@ -982,11 +982,20 @@
                 TEST_PACKAGE_NAME, true);
 
         mTestableLooper.processAllMessages();
-        mRecordingCallback.onRecordingConfigChanged(Collections.emptyList());
+
+        // Update with multiple recording configs, of which one is unsilenced
+        var mockARCUnsilenced = mock(AudioRecordingConfiguration.class);
+        when(mockARCUnsilenced.getClientUid()).thenReturn(TEST_UID);
+        when(mockARCUnsilenced.isClientSilenced()).thenReturn(false);
+
+        mRecordingCallback.onRecordingConfigChanged(List.of(
+                    mockARCUnsilenced, mPausedMockRecording));
 
         mTestableLooper.processAllMessages();
 
         verify(mCallback).onActiveStateChanged(micOpCode, TEST_UID, TEST_PACKAGE_NAME, true);
+        // For consistency since this runs in a loop
+        mController.removeCallback(new int[]{micOpCode}, mCallback);
     }
 
     private void verifyAudioPausedSentInactive(int micOpCode) {
@@ -997,11 +1006,16 @@
                 TEST_PACKAGE_NAME, true);
         mTestableLooper.processAllMessages();
 
-        AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class);
-        when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER);
-        when(mockARC.isClientSilenced()).thenReturn(true);
+        // Multiple recording configs, which are all silenced
+        AudioRecordingConfiguration mockARCOne = mock(AudioRecordingConfiguration.class);
+        when(mockARCOne.getClientUid()).thenReturn(TEST_UID_OTHER);
+        when(mockARCOne.isClientSilenced()).thenReturn(true);
 
-        mRecordingCallback.onRecordingConfigChanged(List.of(mockARC));
+        AudioRecordingConfiguration mockARCTwo = mock(AudioRecordingConfiguration.class);
+        when(mockARCTwo.getClientUid()).thenReturn(TEST_UID_OTHER);
+        when(mockARCTwo.isClientSilenced()).thenReturn(true);
+
+        mRecordingCallback.onRecordingConfigChanged(List.of(mockARCOne, mockARCTwo));
         mTestableLooper.processAllMessages();
 
         InOrder inOrder = inOrder(mCallback);
@@ -1009,6 +1023,8 @@
                 micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
         inOrder.verify(mCallback).onActiveStateChanged(
                 micOpCode, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
+        // For consistency since this runs in a loop
+        mController.removeCallback(new int[]{micOpCode}, mCallback);
     }
 
     private void verifySingleActiveOps(int op) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
deleted file mode 100644
index 6dc4b10..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.graphics.Point
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.testing.TestableLooper.RunWithLooper
-import android.util.DisplayMetrics
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.keyguard.logging.KeyguardLogger
-import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
-import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.leak.RotationUtils
-import com.android.systemui.util.mockito.any
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.After
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import javax.inject.Provider
-
-
-@ExperimentalCoroutinesApi
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class AuthRippleControllerTest : SysuiTestCase() {
-    private lateinit var staticMockSession: MockitoSession
-
-    private lateinit var controller: AuthRippleController
-    @Mock private lateinit var rippleView: AuthRippleView
-    @Mock private lateinit var commandRegistry: CommandRegistry
-    @Mock private lateinit var configurationController: ConfigurationController
-    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var authController: AuthController
-    @Mock private lateinit var authRippleInteractor: AuthRippleInteractor
-    @Mock private lateinit var keyguardStateController: KeyguardStateController
-    @Mock
-    private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
-    @Mock
-    private lateinit var notificationShadeWindowController: NotificationShadeWindowController
-    @Mock
-    private lateinit var biometricUnlockController: BiometricUnlockController
-    @Mock
-    private lateinit var udfpsControllerProvider: Provider<UdfpsController>
-    @Mock
-    private lateinit var udfpsController: UdfpsController
-    @Mock
-    private lateinit var statusBarStateController: StatusBarStateController
-    @Mock
-    private lateinit var lightRevealScrim: LightRevealScrim
-    @Mock
-    private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
-
-    private val facePropertyRepository = FakeFacePropertyRepository()
-    private val displayMetrics = DisplayMetrics()
-
-    @Captor
-    private lateinit var biometricUnlockListener:
-            ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
-
-    @Before
-    fun setUp() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        MockitoAnnotations.initMocks(this)
-        staticMockSession = mockitoSession()
-                .mockStatic(RotationUtils::class.java)
-                .strictness(Strictness.LENIENT)
-                .startMocking()
-
-        `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
-        `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp))
-        `when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
-
-        controller = AuthRippleController(
-            context,
-            authController,
-            configurationController,
-            keyguardUpdateMonitor,
-            keyguardStateController,
-            wakefulnessLifecycle,
-            commandRegistry,
-            notificationShadeWindowController,
-            udfpsControllerProvider,
-            statusBarStateController,
-            displayMetrics,
-            KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
-            biometricUnlockController,
-            lightRevealScrim,
-            authRippleInteractor,
-            facePropertyRepository,
-            rippleView,
-        )
-        controller.init()
-    }
-
-    @After
-    fun tearDown() {
-        staticMockSession.finishMocking()
-    }
-
-    @Test
-    fun testFingerprintTrigger_KeyguardShowing_Ripple() {
-        // GIVEN fp exists, keyguard is showing, unlocking with fp allowed
-        val fpsLocation = Point(5, 5)
-        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
-        // WHEN fingerprint authenticated
-        verify(biometricUnlockController).addListener(biometricUnlockListener.capture())
-        biometricUnlockListener.value
-                .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT)
-
-        // THEN update sensor location and show ripple
-        verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
-        verify(rippleView).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() {
-        // GIVEN fp exists & unlocking with fp allowed
-        val fpsLocation = Point(5, 5)
-        `when`(authController.udfpsLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
-        // WHEN keyguard is NOT showing & fingerprint authenticated
-        `when`(keyguardStateController.isShowing).thenReturn(false)
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */)
-
-        // THEN no ripple
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() {
-        // GIVEN fp exists & keyguard is showing
-        val fpsLocation = Point(5, 5)
-        `when`(authController.udfpsLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-
-        // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FINGERPRINT))).thenReturn(false)
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */)
-
-        // THEN no ripple
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testNullFaceSensorLocationDoesNothing() {
-        facePropertyRepository.setSensorLocation(null)
-        controller.onViewAttached()
-
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FACE /* type */,
-            false /* isStrongBiometric */)
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun testNullFingerprintSensorLocationDoesNothing() {
-        `when`(authController.fingerprintSensorLocation).thenReturn(null)
-        controller.onViewAttached()
-
-        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
-        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
-        captor.value.onBiometricAuthenticated(
-            0 /* userId */,
-            BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */)
-        verify(rippleView, never()).startUnlockedRipple(any())
-    }
-
-    @Test
-    fun registersAndDeregisters() {
-        controller.onViewAttached()
-        val captor = ArgumentCaptor
-            .forClass(KeyguardStateController.Callback::class.java)
-        verify(keyguardStateController).addCallback(captor.capture())
-        val captor2 = ArgumentCaptor
-            .forClass(WakefulnessLifecycle.Observer::class.java)
-        verify(wakefulnessLifecycle).addObserver(captor2.capture())
-        controller.onViewDetached()
-        verify(keyguardStateController).removeCallback(any())
-        verify(wakefulnessLifecycle).removeObserver(any())
-    }
-
-    @Test
-    @RunWithLooper(setAsMainLooper = true)
-    fun testAnimatorRunWhenWakeAndUnlock_fingerprint() {
-        mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
-        val fpsLocation = Point(5, 5)
-        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                BiometricSourceType.FINGERPRINT)).thenReturn(true)
-        `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-
-        controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
-        assertTrue("reveal didn't start on keyguardFadingAway",
-            controller.startLightRevealScrimOnKeyguardFadingAway)
-        `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
-        controller.onKeyguardFadingAwayChanged()
-        assertFalse("reveal triggers multiple times",
-            controller.startLightRevealScrimOnKeyguardFadingAway)
-    }
-
-    @Test
-    @RunWithLooper(setAsMainLooper = true)
-    fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() {
-        mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
-        val faceLocation = Point(5, 5)
-        facePropertyRepository.setSensorLocation(faceLocation)
-        controller.onViewAttached()
-        `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-        `when`(authController.isUdfpsFingerDown).thenReturn(true)
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                eq(BiometricSourceType.FACE))).thenReturn(true)
-
-        controller.showUnlockRipple(BiometricSourceType.FACE)
-        assertTrue("reveal didn't start on keyguardFadingAway",
-                controller.startLightRevealScrimOnKeyguardFadingAway)
-        `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
-        controller.onKeyguardFadingAwayChanged()
-        assertFalse("reveal triggers multiple times",
-                controller.startLightRevealScrimOnKeyguardFadingAway)
-    }
-
-    @Test
-    fun testUpdateRippleColor() {
-        controller.onViewAttached()
-        val captor = ArgumentCaptor
-            .forClass(ConfigurationController.ConfigurationListener::class.java)
-        verify(configurationController).addCallback(captor.capture())
-
-        reset(rippleView)
-        captor.value.onThemeChanged()
-        verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
-
-        reset(rippleView)
-        captor.value.onUiModeChanged()
-        verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
-    }
-
-    @Test
-    fun testUdfps_onFingerDown_runningForDeviceEntry_showDwellRipple() {
-        // GIVEN fingerprint detection is running on keyguard
-        `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true)
-
-        // GIVEN view is already attached
-        controller.onViewAttached()
-        val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
-        verify(udfpsController).addCallback(captor.capture())
-
-        // GIVEN fp is updated to Point(5, 5)
-        val fpsLocation = Point(5, 5)
-        `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-
-        // WHEN finger is down
-        captor.value.onFingerDown()
-
-        // THEN update sensor location and show ripple
-        verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
-        verify(rippleView).startDwellRipple(false)
-    }
-
-    @Test
-    fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() {
-        // GIVEN fingerprint detection is NOT running on keyguard
-        `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false)
-
-        // GIVEN view is already attached
-        controller.onViewAttached()
-        val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
-        verify(udfpsController).addCallback(captor.capture())
-
-        // WHEN finger is down
-        captor.value.onFingerDown()
-
-        // THEN doesn't show dwell ripple
-        verify(rippleView, never()).startDwellRipple(false)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index e2a6a55..4baca71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -16,21 +16,14 @@
 
 package com.android.systemui.biometrics
 
-import android.graphics.Rect
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
 import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
 import android.hardware.biometrics.BiometricRequestConstants.RequestReason
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
 import android.testing.TestableLooper.RunWithLooper
 import android.view.LayoutInflater
 import android.view.MotionEvent
-import android.view.Surface
-import android.view.Surface.Rotation
 import android.view.View
-import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -38,7 +31,6 @@
 import com.android.app.viewcapture.ViewCapture
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -61,9 +53,7 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -86,7 +76,6 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
@@ -118,7 +107,6 @@
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var dialogManager: SystemUIDialogManager
     @Mock private lateinit var dumpManager: DumpManager
-    @Mock private lateinit var transitionController: LockscreenShadeTransitionController
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock
@@ -126,8 +114,6 @@
     @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
     @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
     @Mock private lateinit var udfpsController: UdfpsController
-    @Mock private lateinit var udfpsView: UdfpsView
-    @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy
     @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
     @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
     @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@@ -147,7 +133,7 @@
     private lateinit var powerInteractor: PowerInteractor
     private lateinit var testScope: TestScope
 
-    private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
+    private val onTouch = { _: View, _: MotionEvent -> true }
     private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
     private lateinit var controllerOverlay: UdfpsControllerOverlay
 
@@ -158,53 +144,37 @@
         powerInteractor = kosmos.powerInteractor
         keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
         keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
-        whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView)
-        whenever(inflater.inflate(R.layout.udfps_bp_view, null))
-            .thenReturn(mock(UdfpsBpView::class.java))
-        whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
-            .thenReturn(mUdfpsKeyguardViewLegacy)
-        whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null))
-            .thenReturn(mock(UdfpsFpmEmptyView::class.java))
     }
 
     private suspend fun withReasonSuspend(
         @RequestReason reason: Int,
         isDebuggable: Boolean = false,
-        enableDeviceEntryUdfpsRefactor: Boolean = false,
         block: suspend () -> Unit,
     ) {
-        withReason(
-            reason,
-            isDebuggable,
-            enableDeviceEntryUdfpsRefactor,
-        )
+        withReason(reason, isDebuggable)
         block()
     }
 
     private fun withReason(
         @RequestReason reason: Int,
         isDebuggable: Boolean = false,
-        enableDeviceEntryUdfpsRefactor: Boolean = false,
         block: () -> Unit = {},
     ) {
-        if (enableDeviceEntryUdfpsRefactor) {
-            mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        } else {
-            mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        }
         controllerOverlay =
             UdfpsControllerOverlay(
                 context,
                 inflater,
-                ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
-                        isViewCaptureEnabled = false),
+                ViewCaptureAwareWindowManager(
+                    windowManager,
+                    lazyViewCapture,
+                    isViewCaptureEnabled = false,
+                ),
                 accessibilityManager,
                 statusBarStateController,
                 statusBarKeyguardViewManager,
                 keyguardUpdateMonitor,
                 dialogManager,
                 dumpManager,
-                transitionController,
                 configurationController,
                 keyguardStateController,
                 unlockedScreenOffAnimationController,
@@ -230,117 +200,6 @@
         block()
     }
 
-    @Test fun showUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { showUdfpsOverlay() }
-
-    @Test
-    fun showUdfpsOverlay_keyguard() =
-        withReason(REASON_AUTH_KEYGUARD) {
-            showUdfpsOverlay()
-            verify(mUdfpsKeyguardViewLegacy).updateSensorLocation(eq(overlayParams.sensorBounds))
-        }
-
-    @Test fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() }
-
-    private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
-        // Sensor that's in the top left corner of the display in natural orientation.
-        val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
-        val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
-        overlayParams =
-            UdfpsOverlayParams(
-                sensorBounds,
-                overlayBounds,
-                DISPLAY_WIDTH,
-                DISPLAY_HEIGHT,
-                scaleFactor = 1f,
-                rotation
-            )
-        block()
-    }
-
-    @Test
-    fun showUdfpsOverlay_withRotation0() =
-        withRotation(Surface.ROTATION_0) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
-                assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_withRotation180() =
-        withRotation(Surface.ROTATION_180) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // ROTATION_180 is not supported. Sensor should stay in the top left corner.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
-                assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_withRotation90() =
-        withRotation(Surface.ROTATION_90) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // Sensor should be in the bottom left corner in ROTATION_90.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
-                assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_withRotation270() =
-        withRotation(Surface.ROTATION_270) {
-            withReason(REASON_AUTH_BP) {
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // Sensor should be in the top right corner in ROTATION_270.
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.x).isEqualTo(0)
-                assertThat(lp.y).isEqualTo(0)
-                assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
-                assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
-            }
-        }
-
-    @Test
-    fun showUdfpsOverlay_awake() =
-        testScope.runTest {
-            withReason(REASON_AUTH_KEYGUARD) {
-                powerRepository.updateWakefulness(
-                    rawState = WakefulnessState.AWAKE,
-                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
-                    lastSleepReason = WakeSleepReason.OTHER,
-                )
-                runCurrent()
-                controllerOverlay.show(udfpsController, overlayParams)
-                runCurrent()
-                verify(windowManager).addView(any(), any())
-            }
-        }
-
     @Test
     fun showUdfpsOverlay_whileGoingToSleep() =
         testScope.runTest {
@@ -412,91 +271,9 @@
         }
 
     @Test
-    fun showUdfpsOverlay_afterFinishedTransitioningToAOD() =
-        testScope.runTest {
-            withReasonSuspend(REASON_AUTH_KEYGUARD) {
-                keyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.OFF,
-                    to = KeyguardState.GONE,
-                    testScope = this,
-                )
-                powerRepository.updateWakefulness(
-                    rawState = WakefulnessState.STARTING_TO_SLEEP,
-                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
-                    lastSleepReason = WakeSleepReason.OTHER,
-                )
-                runCurrent()
-
-                // WHEN a request comes to show the view
-                controllerOverlay.show(udfpsController, overlayParams)
-                runCurrent()
-
-                // THEN the view does not get added immediately
-                verify(windowManager, never()).addView(any(), any())
-
-                // WHEN the device finishes transitioning to AOD
-                keyguardTransitionRepository.sendTransitionSteps(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    testScope = this,
-                )
-                runCurrent()
-
-                // THEN the view gets added
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-            }
-        }
-
-    private fun showUdfpsOverlay() {
-        val didShow = controllerOverlay.show(udfpsController, overlayParams)
-
-        verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any())
-        verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
-        verify(udfpsView).animationViewController = any()
-        verify(udfpsView).addView(any())
-
-        assertThat(didShow).isTrue()
-        assertThat(controllerOverlay.isShowing).isTrue()
-        assertThat(controllerOverlay.isHiding).isFalse()
-        assertThat(controllerOverlay.getTouchOverlay()).isNotNull()
-    }
-
-    @Test fun hideUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { hideUdfpsOverlay() }
-
-    @Test fun hideUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) { hideUdfpsOverlay() }
-
-    @Test fun hideUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { hideUdfpsOverlay() }
-
-    @Test fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() }
-
-    private fun hideUdfpsOverlay() {
-        val didShow = controllerOverlay.show(udfpsController, overlayParams)
-        val view = controllerOverlay.getTouchOverlay()
-        view?.let { whenever(view.parent).thenReturn(mock(ViewGroup::class.java)) }
-        val didHide = controllerOverlay.hide()
-
-        verify(windowManager).removeView(eq(view))
-
-        assertThat(didShow).isTrue()
-        assertThat(didHide).isTrue()
-        assertThat(controllerOverlay.getTouchOverlay()).isNull()
-        assertThat(controllerOverlay.animationViewController).isNull()
-        assertThat(controllerOverlay.isShowing).isFalse()
-        assertThat(controllerOverlay.isHiding).isTrue()
-    }
-
-    @Test
     fun canNotHide() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.hide()).isFalse() }
 
     @Test
-    fun canNotReshow() =
-        withReason(REASON_AUTH_BP) {
-            assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue()
-            assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse()
-        }
-
-    @Test
     fun cancels() =
         withReason(REASON_AUTH_BP) {
             controllerOverlay.cancel()
@@ -504,16 +281,6 @@
         }
 
     @Test
-    fun unconfigureDisplayOnHide() =
-        withReason(REASON_AUTH_BP) {
-            whenever(udfpsView.isDisplayConfigured).thenReturn(true)
-
-            controllerOverlay.show(udfpsController, overlayParams)
-            controllerOverlay.hide()
-            verify(udfpsView).unconfigureDisplay()
-        }
-
-    @Test
     fun matchesRequestIds() =
         withReason(REASON_AUTH_BP) {
             assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue()
@@ -521,29 +288,9 @@
         }
 
     @Test
-    fun smallOverlayOnEnrollmentWithA11y() =
-        withRotation(Surface.ROTATION_0) {
-            withReason(REASON_ENROLL_ENROLLING) {
-                // When a11y enabled during enrollment
-                whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true)
-
-                controllerOverlay.show(udfpsController, overlayParams)
-                verify(windowManager)
-                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
-                // Layout params should use sensor bounds
-                val lp = layoutParamsCaptor.value
-                assertThat(lp.width).isEqualTo(overlayParams.sensorBounds.width())
-                assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
-            }
-        }
-
-    @Test
     fun addViewPending_layoutIsNotUpdated() =
         testScope.runTest {
             withReasonSuspend(REASON_AUTH_KEYGUARD) {
-                mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-
                 // GIVEN going to sleep
                 keyguardTransitionRepository.sendTransitionSteps(
                     from = KeyguardState.OFF,
@@ -574,26 +321,4 @@
                 controllerOverlay.hide()
             }
         }
-
-    @Test
-    fun updateOverlayParams_viewLayoutUpdated() =
-        testScope.runTest {
-            withReasonSuspend(REASON_AUTH_KEYGUARD) {
-                powerRepository.updateWakefulness(
-                    rawState = WakefulnessState.AWAKE,
-                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
-                    lastSleepReason = WakeSleepReason.OTHER,
-                )
-                runCurrent()
-                controllerOverlay.show(udfpsController, overlayParams)
-                runCurrent()
-                verify(windowManager).addView(any(), any())
-
-                // WHEN updateOverlayParams gets called
-                controllerOverlay.updateOverlayParams(overlayParams)
-
-                // THEN the view layout is updated
-                verify(windowManager).updateViewLayout(any(), any())
-            }
-        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt
new file mode 100644
index 0000000..655b2cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AudioSharingButtonViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val bluetoothState = MutableStateFlow(false)
+    private val deviceItemUpdate: MutableSharedFlow<List<DeviceItem>> = MutableSharedFlow()
+    @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
+    @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+    @Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
+    @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
+    @Mock private lateinit var deviceItem: DeviceItem
+    private lateinit var mockitoSession: StaticMockitoSession
+    private lateinit var audioSharingButtonViewModel: AudioSharingButtonViewModel
+
+    @Before
+    fun setUp() {
+        mockitoSession =
+            mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
+        whenever(bluetoothStateInteractor.bluetoothStateUpdate).thenReturn(bluetoothState)
+        whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(deviceItemUpdate)
+        audioSharingButtonViewModel =
+            AudioSharingButtonViewModel(
+                localBluetoothManager,
+                kosmos.audioSharingInteractor,
+                bluetoothStateInteractor,
+                deviceItemInteractor,
+            )
+        audioSharingButtonViewModel.activateIn(testScope)
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
+    @Test
+    fun testButtonStateUpdate_bluetoothOff_returnGone() {
+        testScope.runTest {
+            val actual by
+                collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
+            kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+
+            runCurrent()
+
+            assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+        }
+    }
+
+    @Test
+    fun testButtonStateUpdate_noDevice_returnGone() {
+        testScope.runTest {
+            val actual by
+                collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
+            kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+            bluetoothState.value = true
+            runCurrent()
+
+            assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+        }
+    }
+
+    @Test
+    fun testButtonStateUpdate_isBroadcasting_returnSharingAudio() {
+        testScope.runTest {
+            val actual by
+                collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
+            kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+            bluetoothState.value = true
+            runCurrent()
+            deviceItemUpdate.emit(listOf())
+            runCurrent()
+            kosmos.bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
+            runCurrent()
+
+            assertThat(actual)
+                .isEqualTo(
+                    AudioSharingButtonState.Visible(
+                        R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
+                        isActive = true,
+                    )
+                )
+        }
+    }
+
+    @Test
+    fun testButtonStateUpdate_hasSource_returnGone() {
+        testScope.runTest {
+            val actual by
+                collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
+            kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+            whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
+            whenever(
+                    BluetoothUtils.hasConnectedBroadcastSource(
+                        cachedBluetoothDevice,
+                        localBluetoothManager,
+                    )
+                )
+                .thenReturn(true)
+            bluetoothState.value = true
+            runCurrent()
+            deviceItemUpdate.emit(listOf(deviceItem))
+            runCurrent()
+
+            assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+        }
+    }
+
+    @Test
+    fun testButtonStateUpdate_hasActiveDevice_returnAudioSharing() {
+        testScope.runTest {
+            val actual by
+                collectLastValue(audioSharingButtonViewModel.audioSharingButtonStateUpdate)
+            kosmos.bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+            whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
+            whenever(
+                    BluetoothUtils.hasConnectedBroadcastSource(
+                        cachedBluetoothDevice,
+                        localBluetoothManager,
+                    )
+                )
+                .thenReturn(false)
+            whenever(BluetoothUtils.isActiveLeAudioDevice(cachedBluetoothDevice)).thenReturn(true)
+            bluetoothState.value = true
+            runCurrent()
+            deviceItemUpdate.emit(listOf(deviceItem))
+            runCurrent()
+
+            assertThat(actual)
+                .isEqualTo(
+                    AudioSharingButtonState.Visible(
+                        R.string.quick_settings_bluetooth_audio_sharing_button,
+                        isActive = false,
+                    )
+                )
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt
new file mode 100644
index 0000000..ce37eee
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bluetooth.qsdialog
+
+import android.bluetooth.BluetoothDevice
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.flags.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
+class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+    private lateinit var actionInteractorImpl: DeviceItemActionInteractor
+    private lateinit var mockitoSession: StaticMockitoSession
+    private lateinit var connectedAudioSharingMediaDeviceItem: DeviceItem
+    private lateinit var connectedMediaDeviceItem: DeviceItem
+    @Mock private lateinit var dialog: SystemUIDialog
+    @Mock private lateinit var leAudioProfile: LeAudioProfile
+    @Mock private lateinit var bluetoothDevice: BluetoothDevice
+
+    @Before
+    fun setUp() {
+        mockitoSession =
+            mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
+        connectedMediaDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null,
+            )
+        connectedAudioSharingMediaDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null,
+            )
+        actionInteractorImpl = kosmos.audioSharingDeviceItemActionInteractorImpl
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT)
+    fun testOnClick_connectedAudioSharingMediaDevice_flagOn_createDialog() {
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog)
+                verify(dialogTransitionAnimator)
+                    .showFromDialog(any(), any(), eq(null), anyBoolean())
+            }
+        }
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT)
+    fun testOnClick_connectedAudioSharingMediaDevice_flagOff_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any(),
+                    )
+                verify(dialogTransitionAnimator, never())
+                    .showFromDialog(any(), any(), eq(null), anyBoolean())
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                whenever(cachedBluetoothDevice.uiAccessibleProfiles)
+                    .thenReturn(listOf(leAudioProfile))
+                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+                whenever(
+                        BluetoothUtils.hasConnectedBroadcastSource(
+                            ArgumentMatchers.any(),
+                            ArgumentMatchers.any(),
+                        )
+                    )
+                    .thenReturn(true)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any(),
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                whenever(cachedBluetoothDevice.uiAccessibleProfiles)
+                    .thenReturn(listOf(leAudioProfile))
+
+                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+                whenever(
+                        BluetoothUtils.hasConnectedBroadcastSource(
+                            ArgumentMatchers.any(),
+                            ArgumentMatchers.any(),
+                        )
+                    )
+                    .thenReturn(false)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any(),
+                    )
+            }
+        }
+    }
+
+    private companion object {
+        const val DEVICE_NAME = "device"
+        const val DEVICE_CONNECTION_SUMMARY = "active"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt
new file mode 100644
index 0000000..25b85b5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.widget.Button
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+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.junit.MockitoRule
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
+class AudioSharingDialogDelegateTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos()
+    private val updateFlow = MutableSharedFlow<Unit>()
+    private lateinit var underTest: AudioSharingDialogDelegate
+
+    @Before
+    fun setUp() {
+        with(kosmos) {
+            // TODO(b/364515243): use real object instead of mock
+            whenever(deviceItemInteractor.deviceItemUpdateRequest).thenReturn(updateFlow)
+            whenever(deviceItemInteractor.deviceItemUpdate)
+                .thenReturn(MutableStateFlow(emptyList()))
+            underTest = audioSharingDialogDelegate
+        }
+    }
+
+    @Test
+    fun testCreateDialog() =
+        kosmos.testScope.runTest {
+            val dialog = underTest.createDialog()
+            assertThat(dialog).isInstanceOf(SystemUIDialog::class.java)
+        }
+
+    @Test
+    fun testCreateDialog_showState() =
+        with(kosmos) {
+            testScope.runTest {
+                val availableDeviceName = "name"
+                whenever(cachedBluetoothDevice.name).thenReturn(availableDeviceName)
+                val dialog = spy(underTest.createDialog())
+                dialog.show()
+                runCurrent()
+                val subtitleTextView = dialog.findViewById<TextView>(R.id.subtitle)
+                val switchActiveButton = dialog.findViewById<Button>(R.id.switch_active_button)
+                val shareAudioButton = dialog.findViewById<Button>(R.id.share_audio_button)
+                val subtitle =
+                    context.getString(
+                        R.string.quick_settings_bluetooth_audio_sharing_dialog_subtitle,
+                        availableDeviceName,
+                        ""
+                    )
+                val switchButtonText =
+                    context.getString(
+                        R.string.quick_settings_bluetooth_audio_sharing_dialog_switch_to_button,
+                        availableDeviceName
+                    )
+                assertThat(subtitleTextView.text).isEqualTo(subtitle)
+                assertThat(switchActiveButton.text).isEqualTo(switchButtonText)
+                assertThat(switchActiveButton.hasOnClickListeners()).isTrue()
+                assertThat(shareAudioButton.hasOnClickListeners()).isTrue()
+
+                switchActiveButton.performClick()
+                verify(dialog).dismiss()
+            }
+        }
+
+    @Test
+    fun testCreateDialog_hideState() =
+        with(kosmos) {
+            testScope.runTest {
+                val dialog = spy(underTest.createDialog())
+                dialog.show()
+                runCurrent()
+                updateFlow.emit(Unit)
+                runCurrent()
+                verify(dialog).dismiss()
+            }
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt
new file mode 100644
index 0000000..beb816c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import android.bluetooth.BluetoothDevice
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bluetooth.cachedBluetoothDeviceManager
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
+class AudioSharingDialogViewModelTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+    @Mock private lateinit var profileManager: LocalBluetoothProfileManager
+    @Mock private lateinit var leAudioProfile: LeAudioProfile
+    private val updateFlow = MutableSharedFlow<Unit>()
+    private lateinit var underTest: AudioSharingDialogViewModel
+
+    @Before
+    fun setUp() {
+        with(kosmos) {
+            // TODO(b/364515243): use real object instead of mock
+            whenever(deviceItemInteractor.deviceItemUpdateRequest).thenReturn(updateFlow)
+            whenever(deviceItemInteractor.deviceItemUpdate)
+                .thenReturn(MutableStateFlow(emptyList()))
+            underTest = audioSharingDialogViewModel
+        }
+    }
+
+    @Test
+    fun testDialogState_show() =
+        with(kosmos) {
+            testScope.runTest {
+                val deviceName = "name"
+                whenever(cachedBluetoothDevice.name).thenReturn(deviceName)
+                val actual by collectLastValue(underTest.dialogState)
+                runCurrent()
+                assertThat(actual)
+                    .isEqualTo(
+                        AudioSharingDialogState.Show(
+                            context.getString(
+                                R.string.quick_settings_bluetooth_audio_sharing_dialog_subtitle,
+                                deviceName,
+                                ""
+                            ),
+                            context.getString(
+                                R.string
+                                    .quick_settings_bluetooth_audio_sharing_dialog_switch_to_button,
+                                deviceName
+                            )
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun testDialogState_showWithActiveDeviceName() =
+        with(kosmos) {
+            testScope.runTest {
+                val deviceName = "name"
+                whenever(cachedBluetoothDevice.name).thenReturn(deviceName)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(localBluetoothManager.cachedDeviceManager)
+                    .thenReturn(cachedBluetoothDeviceManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(leAudioProfile.activeDevices).thenReturn(listOf(mock<BluetoothDevice>()))
+                whenever(cachedBluetoothDeviceManager.findDevice(any()))
+                    .thenReturn(cachedBluetoothDevice)
+                val actual by collectLastValue(underTest.dialogState)
+                runCurrent()
+                assertThat(actual)
+                    .isEqualTo(
+                        AudioSharingDialogState.Show(
+                            context.getString(
+                                R.string.quick_settings_bluetooth_audio_sharing_dialog_subtitle,
+                                deviceName,
+                                deviceName
+                            ),
+                            context.getString(
+                                R.string
+                                    .quick_settings_bluetooth_audio_sharing_dialog_switch_to_button,
+                                deviceName
+                            )
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun testDialogState_hide() =
+        with(kosmos) {
+            testScope.runTest {
+                val actual by collectLastValue(underTest.dialogState)
+                runCurrent()
+                updateFlow.emit(Unit)
+                assertThat(actual).isEqualTo(AudioSharingDialogState.Hide)
+            }
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
index 2c53fd6..25f9565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
@@ -16,158 +16,197 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
+import android.bluetooth.BluetoothLeBroadcast
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.dx.mockito.inline.extended.StaticMockitoSession
-import com.android.settingslib.bluetooth.BluetoothUtils
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.res.R
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
 
-@ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
 class AudioSharingInteractorTest : SysuiTestCase() {
-    private val testDispatcher = UnconfinedTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-    private val bluetoothState = MutableStateFlow(false)
-    private val deviceItemUpdate: MutableSharedFlow<List<DeviceItem>> = MutableSharedFlow()
-    @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
-    @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
-    @Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
-    @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
-    @Mock private lateinit var deviceItem: DeviceItem
-    private lateinit var mockitoSession: StaticMockitoSession
-    private lateinit var audioSharingInteractor: AudioSharingInteractor
+    @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos()
+    @Mock private lateinit var localBluetoothLeBroadcast: LocalBluetoothLeBroadcast
+    @Captor private lateinit var callbackCaptor: ArgumentCaptor<BluetoothLeBroadcast.Callback>
+    private lateinit var underTest: AudioSharingInteractor
 
     @Before
     fun setUp() {
-        mockitoSession =
-            mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
-        whenever(bluetoothStateInteractor.bluetoothStateUpdate).thenReturn(bluetoothState)
-        whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(deviceItemUpdate)
-        audioSharingInteractor =
-            AudioSharingInteractor(
-                localBluetoothManager,
-                bluetoothStateInteractor,
-                deviceItemInteractor,
-                testScope.backgroundScope,
-                testDispatcher,
-            )
-    }
-
-    @After
-    fun tearDown() {
-        mockitoSession.finishMocking()
+        with(kosmos) { underTest = audioSharingInteractor }
     }
 
     @Test
-    fun testButtonStateUpdate_bluetoothOff_returnGone() {
-        testScope.runTest {
-            val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
+    fun testIsAudioSharingOn_flagOff_false() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(false)
+                val value by collectLastValue(underTest.isAudioSharingOn)
+                runCurrent()
 
-            assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+                assertThat(value).isFalse()
+            }
         }
-    }
 
     @Test
-    fun testButtonStateUpdate_noDevice_returnGone() {
-        testScope.runTest {
-            val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
-            bluetoothState.value = true
-            runCurrent()
+    fun testIsAudioSharingOn_flagOn_notInAudioSharing_false() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
+                val value by collectLastValue(underTest.isAudioSharingOn)
+                runCurrent()
 
-            assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+                assertThat(value).isFalse()
+            }
         }
-    }
 
     @Test
-    fun testButtonStateUpdate_isBroadcasting_returnSharingAudio() {
-        testScope.runTest {
-            whenever(BluetoothUtils.isBroadcasting(localBluetoothManager)).thenReturn(true)
+    fun testIsAudioSharingOn_flagOn_inAudioSharing_true() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
+                val value by collectLastValue(underTest.isAudioSharingOn)
+                runCurrent()
 
-            val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
-            bluetoothState.value = true
-            deviceItemUpdate.emit(listOf())
-            runCurrent()
+                assertThat(value).isTrue()
+            }
+        }
 
-            assertThat(actual)
-                .isEqualTo(
-                    AudioSharingButtonState.Visible(
-                        R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
-                        isActive = true
-                    )
+    @Test
+    fun testAudioSourceStateUpdate_notInAudioSharing_returnEmpty() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
+                val value by collectLastValue(underTest.audioSourceStateUpdate)
+                runCurrent()
+
+                assertThat(value).isNull()
+            }
+        }
+
+    @Test
+    fun testAudioSourceStateUpdate_inAudioSharing_returnUnit() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
+                val value by collectLastValue(underTest.audioSourceStateUpdate)
+                runCurrent()
+                bluetoothTileDialogAudioSharingRepository.emitAudioSourceStateUpdate()
+                runCurrent()
+
+                assertThat(value).isNull()
+            }
+        }
+
+    @Test
+    fun testHandleAudioSourceWhenReady_flagOff_sourceNotAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(false)
+                val job = launch { underTest.handleAudioSourceWhenReady() }
+                runCurrent()
+
+                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
+                job.cancel()
+            }
+        }
+
+    @Test
+    fun testHandleAudioSourceWhenReady_noProfile_sourceNotAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(null)
+                val job = launch { underTest.handleAudioSourceWhenReady() }
+                runCurrent()
+
+                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
+                job.cancel()
+            }
+        }
+
+    @Test
+    fun testHandleAudioSourceWhenReady_hasProfileButAudioSharingOff_sourceNotAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(false)
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
+                    localBluetoothLeBroadcast
                 )
+                val job = launch { underTest.handleAudioSourceWhenReady() }
+                runCurrent()
+
+                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
+                job.cancel()
+            }
         }
-    }
 
     @Test
-    fun testButtonStateUpdate_hasSource_returnGone() {
-        testScope.runTest {
-            whenever(BluetoothUtils.isBroadcasting(localBluetoothManager)).thenReturn(false)
-            whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
-            whenever(
-                    BluetoothUtils.hasConnectedBroadcastSource(
-                        cachedBluetoothDevice,
-                        localBluetoothManager
-                    )
+    fun testHandleAudioSourceWhenReady_audioSharingOnButNoPlayback_sourceNotAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
+                    localBluetoothLeBroadcast
                 )
-                .thenReturn(true)
+                val job = launch { underTest.handleAudioSourceWhenReady() }
+                runCurrent()
+                verify(localBluetoothLeBroadcast)
+                    .registerServiceCallBack(any(), callbackCaptor.capture())
+                runCurrent()
 
-            val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
-            bluetoothState.value = true
-            deviceItemUpdate.emit(listOf(deviceItem))
-            runCurrent()
-
-            assertThat(actual).isEqualTo(AudioSharingButtonState.Gone)
+                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isFalse()
+                job.cancel()
+            }
         }
-    }
 
     @Test
-    fun testButtonStateUpdate_hasActiveDevice_returnAudioSharing() {
-        testScope.runTest {
-            whenever(BluetoothUtils.isBroadcasting(localBluetoothManager)).thenReturn(false)
-            whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
-            whenever(
-                    BluetoothUtils.hasConnectedBroadcastSource(
-                        cachedBluetoothDevice,
-                        localBluetoothManager
-                    )
+    fun testHandleAudioSourceWhenReady_audioSharingOnAndPlaybackStarts_sourceAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                bluetoothTileDialogAudioSharingRepository.setInAudioSharing(true)
+                bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true)
+                bluetoothTileDialogAudioSharingRepository.setLeAudioBroadcastProfile(
+                    localBluetoothLeBroadcast
                 )
-                .thenReturn(false)
-            whenever(BluetoothUtils.isActiveLeAudioDevice(cachedBluetoothDevice)).thenReturn(true)
+                val job = launch { underTest.handleAudioSourceWhenReady() }
+                runCurrent()
+                verify(localBluetoothLeBroadcast)
+                    .registerServiceCallBack(any(), callbackCaptor.capture())
+                runCurrent()
+                callbackCaptor.value.onPlaybackStarted(0, 0)
+                runCurrent()
 
-            val actual by collectLastValue(audioSharingInteractor.audioSharingButtonStateUpdate)
-            bluetoothState.value = true
-            deviceItemUpdate.emit(listOf(deviceItem))
-            runCurrent()
-
-            assertThat(actual)
-                .isEqualTo(
-                    AudioSharingButtonState.Visible(
-                        R.string.quick_settings_bluetooth_audio_sharing_button,
-                        isActive = false
-                    )
-                )
+                assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isTrue()
+                job.cancel()
+            }
         }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt
new file mode 100644
index 0000000..c9e8813
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryTest.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bluetooth.qsdialog
+
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.audioSharingRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AudioSharingRepositoryTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var profileManager: LocalBluetoothProfileManager
+    @Mock private lateinit var leAudioBroadcastProfile: LocalBluetoothLeBroadcast
+    @Mock private lateinit var leAudioBroadcastAssistant: LocalBluetoothLeBroadcastAssistant
+    @Mock private lateinit var metadata: BluetoothLeBroadcastMetadata
+    @Mock private lateinit var bluetoothDevice: BluetoothDevice
+    private val kosmos = testKosmos()
+    private lateinit var underTest: AudioSharingRepository
+
+    @Before
+    fun setUp() {
+        underTest =
+            AudioSharingRepositoryImpl(
+                kosmos.localBluetoothManager,
+                kosmos.audioSharingRepository,
+                kosmos.testDispatcher,
+            )
+    }
+
+    @Test
+    fun testSwitchActive() =
+        with(kosmos) {
+            testScope.runTest {
+                audioSharingRepository.setAudioSharingAvailable(true)
+                underTest.setActive(cachedBluetoothDevice)
+                verify(cachedBluetoothDevice).setActive()
+            }
+        }
+
+    @Test
+    fun testSwitchActive_flagOff_doNothing() =
+        with(kosmos) {
+            testScope.runTest {
+                audioSharingRepository.setAudioSharingAvailable(false)
+                underTest.setActive(cachedBluetoothDevice)
+                verify(cachedBluetoothDevice, never()).setActive()
+            }
+        }
+
+    @Test
+    fun testStartAudioSharing() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioBroadcastProfile).thenReturn(leAudioBroadcastProfile)
+                audioSharingRepository.setAudioSharingAvailable(true)
+                underTest.startAudioSharing()
+                verify(leAudioBroadcastProfile).startPrivateBroadcast()
+            }
+        }
+
+    @Test
+    fun testStartAudioSharing_flagOff_doNothing() =
+        with(kosmos) {
+            testScope.runTest {
+                audioSharingRepository.setAudioSharingAvailable(false)
+                underTest.startAudioSharing()
+                verify(leAudioBroadcastProfile, never()).startPrivateBroadcast()
+            }
+        }
+
+    @Test
+    fun testAddSource_flagOff_doesNothing() =
+        with(kosmos) {
+            testScope.runTest {
+                audioSharingRepository.setAudioSharingAvailable(false)
+
+                underTest.addSource()
+                runCurrent()
+
+                verify(leAudioBroadcastAssistant, never()).allConnectedDevices
+            }
+        }
+
+    @Test
+    fun testAddSource_noMetadata_doesNothing() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioBroadcastProfile).thenReturn(leAudioBroadcastProfile)
+                audioSharingRepository.setAudioSharingAvailable(true)
+                whenever(leAudioBroadcastProfile.latestBluetoothLeBroadcastMetadata)
+                    .thenReturn(null)
+
+                underTest.addSource()
+                runCurrent()
+
+                verify(leAudioBroadcastAssistant, never()).allConnectedDevices
+            }
+        }
+
+    @Test
+    fun testAddSource_noConnectedDevice_doesNothing() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioBroadcastProfile).thenReturn(leAudioBroadcastProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(leAudioBroadcastAssistant)
+                audioSharingRepository.setAudioSharingAvailable(true)
+                whenever(leAudioBroadcastProfile.latestBluetoothLeBroadcastMetadata)
+                    .thenReturn(metadata)
+                whenever(leAudioBroadcastAssistant.allConnectedDevices).thenReturn(emptyList())
+
+                underTest.addSource()
+                runCurrent()
+
+                verify(leAudioBroadcastAssistant, never()).addSource(any(), any(), anyBoolean())
+            }
+        }
+
+    @Test
+    fun testAddSource_hasConnectedDeviceAndMetadata_addSource() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioBroadcastProfile).thenReturn(leAudioBroadcastProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(leAudioBroadcastAssistant)
+                audioSharingRepository.setAudioSharingAvailable(true)
+                whenever(leAudioBroadcastProfile.latestBluetoothLeBroadcastMetadata)
+                    .thenReturn(metadata)
+                whenever(leAudioBroadcastAssistant.allConnectedDevices)
+                    .thenReturn(listOf(bluetoothDevice))
+
+                underTest.addSource()
+                runCurrent()
+
+                verify(leAudioBroadcastAssistant).addSource(bluetoothDevice, metadata, false)
+            }
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index d7bea66..a56c2cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -31,8 +31,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.kotlin.getMutableStateFlow
@@ -42,12 +45,12 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.test.TestCoroutineScheduler
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Rule
@@ -64,10 +67,12 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
 @EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
 class BluetoothTileDialogViewModelTest : SysuiTestCase() {
 
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos()
     private val fakeSystemClock = FakeSystemClock()
     private val backgroundExecutor = FakeExecutor(fakeSystemClock)
 
@@ -75,8 +80,6 @@
 
     @Mock private lateinit var bluetoothStateInteractor: BluetoothStateInteractor
 
-    @Mock private lateinit var audioSharingInteractor: AudioSharingInteractor
-
     @Mock private lateinit var bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor
 
     @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
@@ -111,15 +114,15 @@
 
     private val sharedPreferences = FakeSharedPreferences()
 
-    private lateinit var scheduler: TestCoroutineScheduler
     private lateinit var dispatcher: CoroutineDispatcher
     private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
-        scheduler = TestCoroutineScheduler()
-        dispatcher = UnconfinedTestDispatcher(scheduler)
-        testScope = TestScope(dispatcher)
+        dispatcher = kosmos.testDispatcher
+        testScope = kosmos.testScope
+        // TODO(b/364515243): use real object instead of mock
+        whenever(kosmos.deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow())
         bluetoothTileDialogViewModel =
             BluetoothTileDialogViewModel(
                 deviceItemInteractor,
@@ -139,11 +142,13 @@
                         dispatcher
                     )
                 ),
-                audioSharingInteractor,
+                kosmos.audioSharingInteractor,
+                kosmos.audioSharingButtonViewModelFactory,
                 bluetoothDeviceMetadataInteractor,
                 mDialogTransitionAnimator,
                 activityStarter,
                 uiEventLogger,
+                bluetoothTileDialogLogger,
                 testScope.backgroundScope,
                 dispatcher,
                 dispatcher,
@@ -161,13 +166,10 @@
         whenever(sysuiDialog.context).thenReturn(mContext)
         whenever(bluetoothTileDialogDelegate.bluetoothStateToggle)
             .thenReturn(getMutableStateFlow(false))
-        whenever(bluetoothTileDialogDelegate.deviceItemClick)
-            .thenReturn(getMutableStateFlow(deviceItem))
+        whenever(bluetoothTileDialogDelegate.deviceItemClick).thenReturn(MutableSharedFlow())
         whenever(bluetoothTileDialogDelegate.contentHeight).thenReturn(getMutableStateFlow(0))
         whenever(bluetoothTileDialogDelegate.bluetoothAutoOnToggle)
             .thenReturn(getMutableStateFlow(false))
-        whenever(audioSharingInteractor.audioSharingButtonStateUpdate)
-            .thenReturn(getMutableStateFlow(AudioSharingButtonState.Gone))
         whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
     }
 
@@ -175,6 +177,7 @@
     fun testShowDialog_noAnimation() {
         testScope.runTest {
             bluetoothTileDialogViewModel.showDialog(null)
+            runCurrent()
 
             verify(mDialogTransitionAnimator, never()).show(any(), any(), any())
         }
@@ -184,6 +187,7 @@
     fun testShowDialog_animated() {
         testScope.runTest {
             bluetoothTileDialogViewModel.showDialog(expandable)
+            runCurrent()
 
             verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
         }
@@ -194,6 +198,7 @@
         testScope.runTest {
             backgroundExecutor.execute {
                 bluetoothTileDialogViewModel.showDialog(expandable)
+                runCurrent()
 
                 verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
             }
@@ -204,6 +209,7 @@
     fun testShowDialog_fetchDeviceItem() {
         testScope.runTest {
             bluetoothTileDialogViewModel.showDialog(null)
+            runCurrent()
 
             verify(deviceItemInteractor).deviceItemUpdate
         }
@@ -214,6 +220,7 @@
         testScope.runTest {
             whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
             bluetoothTileDialogViewModel.showDialog(null)
+            runCurrent()
 
             val clickedView = View(context)
             bluetoothTileDialogViewModel.onPairNewDeviceClicked(clickedView)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
index 681ea75..9c427c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
@@ -15,34 +15,22 @@
  */
 package com.android.systemui.bluetooth.qsdialog
 
-import android.bluetooth.BluetoothDevice
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.dx.mockito.inline.extended.StaticMockitoSession
-import com.android.settingslib.bluetooth.BluetoothUtils
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.settingslib.bluetooth.LeAudioProfile
-import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.activityStarter
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
 import org.mockito.Mock
-import org.mockito.Mockito
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
@@ -56,28 +44,18 @@
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
     private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
     private lateinit var actionInteractorImpl: DeviceItemActionInteractor
-    private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var activeMediaDeviceItem: DeviceItem
     private lateinit var notConnectedDeviceItem: DeviceItem
-    private lateinit var connectedAudioSharingMediaDeviceItem: DeviceItem
     private lateinit var connectedMediaDeviceItem: DeviceItem
     private lateinit var connectedOtherDeviceItem: DeviceItem
     @Mock private lateinit var dialog: SystemUIDialog
-    @Mock private lateinit var profileManager: LocalBluetoothProfileManager
-    @Mock private lateinit var leAudioProfile: LeAudioProfile
-    @Mock private lateinit var assistantProfile: LocalBluetoothLeBroadcastAssistant
-    @Mock private lateinit var bluetoothDevice: BluetoothDevice
-    @Mock private lateinit var bluetoothDeviceGroupId2: BluetoothDevice
-    @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
 
     @Before
     fun setUp() {
-        mockitoSession =
-            mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
         activeMediaDeviceItem =
             DeviceItem(
                 type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
-                cachedBluetoothDevice = cachedBluetoothDevice,
+                cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
                 deviceName = DEVICE_NAME,
                 connectionSummary = DEVICE_CONNECTION_SUMMARY,
                 iconWithDescription = null,
@@ -86,7 +64,7 @@
         notConnectedDeviceItem =
             DeviceItem(
                 type = DeviceItemType.SAVED_BLUETOOTH_DEVICE,
-                cachedBluetoothDevice = cachedBluetoothDevice,
+                cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
                 deviceName = DEVICE_NAME,
                 connectionSummary = DEVICE_CONNECTION_SUMMARY,
                 iconWithDescription = null,
@@ -95,16 +73,7 @@
         connectedMediaDeviceItem =
             DeviceItem(
                 type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
-                cachedBluetoothDevice = cachedBluetoothDevice,
-                deviceName = DEVICE_NAME,
-                connectionSummary = DEVICE_CONNECTION_SUMMARY,
-                iconWithDescription = null,
-                background = null
-            )
-        connectedAudioSharingMediaDeviceItem =
-            DeviceItem(
-                type = DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
-                cachedBluetoothDevice = cachedBluetoothDevice,
+                cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
                 deviceName = DEVICE_NAME,
                 connectionSummary = DEVICE_CONNECTION_SUMMARY,
                 iconWithDescription = null,
@@ -113,18 +82,13 @@
         connectedOtherDeviceItem =
             DeviceItem(
                 type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
-                cachedBluetoothDevice = cachedBluetoothDevice,
+                cachedBluetoothDevice = kosmos.cachedBluetoothDevice,
                 deviceName = DEVICE_NAME,
                 connectionSummary = DEVICE_CONNECTION_SUMMARY,
                 iconWithDescription = null,
                 background = null
             )
-        actionInteractorImpl = kosmos.deviceItemActionInteractor
-    }
-
-    @After
-    fun tearDown() {
-        mockitoSession.finishMocking()
+        actionInteractorImpl = kosmos.deviceItemActionInteractorImpl
     }
 
     @Test
@@ -132,14 +96,8 @@
         with(kosmos) {
             testScope.runTest {
                 whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
                 actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
                 verify(cachedBluetoothDevice).setActive()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedBluetoothDevice.address,
-                        DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
-                    )
             }
         }
     }
@@ -149,14 +107,8 @@
         with(kosmos) {
             testScope.runTest {
                 whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
                 actionInteractorImpl.onClick(activeMediaDeviceItem, dialog)
                 verify(cachedBluetoothDevice).disconnect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedBluetoothDevice.address,
-                        DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE
-                    )
             }
         }
     }
@@ -166,14 +118,8 @@
         with(kosmos) {
             testScope.runTest {
                 whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
                 actionInteractorImpl.onClick(connectedOtherDeviceItem, dialog)
                 verify(cachedBluetoothDevice).disconnect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedBluetoothDevice.address,
-                        DeviceItemType.CONNECTED_BLUETOOTH_DEVICE
-                    )
             }
         }
     }
@@ -183,293 +129,8 @@
         with(kosmos) {
             testScope.runTest {
                 whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
                 actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
                 verify(cachedBluetoothDevice).connect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedBluetoothDevice.address,
-                        DeviceItemType.SAVED_BLUETOOTH_DEVICE
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_connectedAudioSharingMediaDevice_logClick() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog)
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedBluetoothDevice.address,
-                        DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
-
-                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
-                verify(activityStarter, Mockito.never())
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(cachedBluetoothDevice.uiAccessibleProfiles)
-                    .thenReturn(listOf(leAudioProfile))
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
-                whenever(
-                        BluetoothUtils.hasConnectedBroadcastSource(
-                            ArgumentMatchers.any(),
-                            ArgumentMatchers.any()
-                        )
-                    )
-                    .thenReturn(true)
-
-                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
-                verify(activityStarter, Mockito.never())
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
-                whenever(cachedBluetoothDevice.uiAccessibleProfiles)
-                    .thenReturn(listOf(leAudioProfile))
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
-                whenever(
-                        BluetoothUtils.hasConnectedBroadcastSource(
-                            ArgumentMatchers.any(),
-                            ArgumentMatchers.any()
-                        )
-                    )
-                    .thenReturn(false)
-
-                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
-                verify(activityStarter)
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_noConnectedLeDevice_shouldNotLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
-                verify(activityStarter, Mockito.never())
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_hasOneConnectedLeDevice_clickedNonLe_shouldNotLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                whenever(
-                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
-                    )
-                    .thenReturn(listOf(bluetoothDevice))
-
-                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
-                verify(activityStarter, Mockito.never())
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_hasOneConnectedLeDevice_clickedLe_shouldLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
-                whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                whenever(
-                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
-                    )
-                    .thenReturn(listOf(bluetoothDevice))
-
-                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
-                verify(activityStarter)
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_hasOneConnectedLeDevice_clickedConnectedLe_shouldNotLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                whenever(
-                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
-                    )
-                    .thenReturn(listOf(bluetoothDevice))
-
-                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
-                verify(activityStarter, Mockito.never())
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_hasTwoConnectedLeDevice_clickedNotConnectedLe_shouldNotLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                whenever(
-                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
-                    )
-                    .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
-                whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
-                    val device = it.arguments.first() as BluetoothDevice
-                    if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
-                }
-
-                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
-                verify(activityStarter, Mockito.never())
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_hasTwoConnectedLeDevice_clickedActiveLe_shouldLaunchSettings() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
-                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
-                whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
-                whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
-
-                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
-                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
-                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
-                whenever(profileManager.leAudioBroadcastAssistantProfile)
-                    .thenReturn(assistantProfile)
-
-                whenever(
-                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
-                    )
-                    .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
-                whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
-                    val device = it.arguments.first() as BluetoothDevice
-                    if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
-                }
-
-                actionInteractorImpl.onClick(activeMediaDeviceItem, dialog)
-                verify(activityStarter)
-                    .postStartActivityDismissingKeyguard(
-                        ArgumentMatchers.any(),
-                        ArgumentMatchers.anyInt(),
-                        ArgumentMatchers.any()
-                    )
             }
         }
     }
@@ -478,7 +139,5 @@
         const val DEVICE_NAME = "device"
         const val DEVICE_CONNECTION_SUMMARY = "active"
         const val DEVICE_ADDRESS = "address"
-        const val GROUP_ID_1 = 1
-        const val GROUP_ID_2 = 2
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
index ef441c1..10c3457 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
@@ -133,8 +133,8 @@
 
     @Test
     fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_flagOff_returnsFalse() {
-         // Flags.FLAG_ENABLE_LE_AUDIO_SHARING off or the device doesn't support broadcast
-         // source or assistant.
+        // Flags.FLAG_ENABLE_LE_AUDIO_SHARING off or the device doesn't support broadcast
+        // source or assistant.
         `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
 
         assertThat(
@@ -145,9 +145,9 @@
     }
 
     @Test
-    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isActiveDevice_returnsFalse() {
-         // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and
-         // assistant.
+    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isActiveDevice_false() {
+        // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and
+        // assistant.
         `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
         `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(true)
 
@@ -159,9 +159,9 @@
     }
 
     @Test
-    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isNotAvailable_returnsFalse() {
-         // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and
-         // assistant.
+    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isNotAvailable_false() {
+        // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and
+        // assistant.
         `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
         `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false)
         `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true)
@@ -177,8 +177,8 @@
 
     @Test
     fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_returnsTrue() {
-         // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and
-         // assistant.
+        // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and
+        // assistant.
         `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
         `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false)
         `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
index 194590c..c39b9a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
@@ -83,18 +83,6 @@
     fun setUp() {
         dispatcher = UnconfinedTestDispatcher()
         testScope = TestScope(dispatcher)
-        interactor =
-            DeviceItemInteractor(
-                bluetoothTileDialogRepository,
-                audioManager,
-                adapter,
-                localBluetoothManager,
-                fakeSystemClock,
-                logger,
-                testScope.backgroundScope,
-                dispatcher
-            )
-
         `when`(deviceItem1.cachedBluetoothDevice).thenReturn(cachedDevice1)
         `when`(deviceItem2.cachedBluetoothDevice).thenReturn(cachedDevice2)
         `when`(cachedDevice1.address).thenReturn("ADDRESS")
@@ -108,9 +96,19 @@
     fun testUpdateDeviceItems_noCachedDevice_returnEmpty() {
         testScope.runTest {
             `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(emptyList())
-            interactor.setDeviceItemFactoryListForTesting(
-                listOf(createFactory({ true }, deviceItem1))
-            )
+            interactor =
+                DeviceItemInteractor(
+                    bluetoothTileDialogRepository,
+                    audioManager,
+                    adapter,
+                    localBluetoothManager,
+                    fakeSystemClock,
+                    logger,
+                    listOf(createFactory({ true }, deviceItem1)),
+                    emptyList(),
+                    testScope.backgroundScope,
+                    dispatcher
+                )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
             val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
@@ -125,9 +123,19 @@
     fun testUpdateDeviceItems_hasCachedDevice_filterNotMatch_returnEmpty() {
         testScope.runTest {
             `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1))
-            interactor.setDeviceItemFactoryListForTesting(
-                listOf(createFactory({ false }, deviceItem1))
-            )
+            interactor =
+                DeviceItemInteractor(
+                    bluetoothTileDialogRepository,
+                    audioManager,
+                    adapter,
+                    localBluetoothManager,
+                    fakeSystemClock,
+                    logger,
+                    listOf(createFactory({ false }, deviceItem1)),
+                    emptyList(),
+                    testScope.backgroundScope,
+                    dispatcher
+                )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
             val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
@@ -142,9 +150,19 @@
     fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnDeviceItem() {
         testScope.runTest {
             `when`(bluetoothTileDialogRepository.cachedDevices).thenReturn(listOf(cachedDevice1))
-            interactor.setDeviceItemFactoryListForTesting(
-                listOf(createFactory({ true }, deviceItem1))
-            )
+            interactor =
+                DeviceItemInteractor(
+                    bluetoothTileDialogRepository,
+                    audioManager,
+                    adapter,
+                    localBluetoothManager,
+                    fakeSystemClock,
+                    logger,
+                    listOf(createFactory({ true }, deviceItem1)),
+                    emptyList(),
+                    testScope.backgroundScope,
+                    dispatcher
+                )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
             val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
@@ -159,9 +177,22 @@
     fun testUpdateDeviceItems_hasCachedDevice_filterMatch_returnMultipleDeviceItem() {
         testScope.runTest {
             `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null)
-            interactor.setDeviceItemFactoryListForTesting(
-                listOf(createFactory({ false }, deviceItem1), createFactory({ true }, deviceItem2))
-            )
+            interactor =
+                DeviceItemInteractor(
+                    bluetoothTileDialogRepository,
+                    audioManager,
+                    adapter,
+                    localBluetoothManager,
+                    fakeSystemClock,
+                    logger,
+                    listOf(
+                        createFactory({ false }, deviceItem1),
+                        createFactory({ true }, deviceItem2)
+                    ),
+                    emptyList(),
+                    testScope.backgroundScope,
+                    dispatcher
+                )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
             val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
@@ -176,18 +207,31 @@
     fun testUpdateDeviceItems_sortByDisplayPriority() {
         testScope.runTest {
             `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null)
-            interactor.setDeviceItemFactoryListForTesting(
-                listOf(
-                    createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1),
-                    createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2)
+            interactor =
+                DeviceItemInteractor(
+                    bluetoothTileDialogRepository,
+                    audioManager,
+                    adapter,
+                    localBluetoothManager,
+                    fakeSystemClock,
+                    logger,
+                    listOf(
+                        createFactory(
+                            { cachedDevice -> cachedDevice.device == device1 },
+                            deviceItem1
+                        ),
+                        createFactory(
+                            { cachedDevice -> cachedDevice.device == device2 },
+                            deviceItem2
+                        )
+                    ),
+                    listOf(
+                        DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+                        DeviceItemType.CONNECTED_BLUETOOTH_DEVICE
+                    ),
+                    testScope.backgroundScope,
+                    dispatcher
                 )
-            )
-            interactor.setDisplayPriorityForTesting(
-                listOf(
-                    DeviceItemType.SAVED_BLUETOOTH_DEVICE,
-                    DeviceItemType.CONNECTED_BLUETOOTH_DEVICE
-                )
-            )
             `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
             `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
 
@@ -204,15 +248,28 @@
     fun testUpdateDeviceItems_sameType_sortByRecentlyConnected() {
         testScope.runTest {
             `when`(adapter.mostRecentlyConnectedDevices).thenReturn(listOf(device2, device1))
-            interactor.setDeviceItemFactoryListForTesting(
-                listOf(
-                    createFactory({ cachedDevice -> cachedDevice.device == device1 }, deviceItem1),
-                    createFactory({ cachedDevice -> cachedDevice.device == device2 }, deviceItem2)
+            interactor =
+                DeviceItemInteractor(
+                    bluetoothTileDialogRepository,
+                    audioManager,
+                    adapter,
+                    localBluetoothManager,
+                    fakeSystemClock,
+                    logger,
+                    listOf(
+                        createFactory(
+                            { cachedDevice -> cachedDevice.device == device1 },
+                            deviceItem1
+                        ),
+                        createFactory(
+                            { cachedDevice -> cachedDevice.device == device2 },
+                            deviceItem2
+                        )
+                    ),
+                    listOf(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE),
+                    testScope.backgroundScope,
+                    dispatcher
                 )
-            )
-            interactor.setDisplayPriorityForTesting(
-                listOf(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
-            )
             `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
             `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
 
@@ -231,10 +288,19 @@
             `when`(bluetoothTileDialogRepository.cachedDevices)
                 .thenReturn(listOf(cachedDevice2, cachedDevice2, cachedDevice2, cachedDevice2))
             `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null)
-            interactor.setDeviceItemFactoryListForTesting(
-                listOf(createFactory({ true }, deviceItem2))
-            )
-
+            interactor =
+                DeviceItemInteractor(
+                    bluetoothTileDialogRepository,
+                    audioManager,
+                    adapter,
+                    localBluetoothManager,
+                    fakeSystemClock,
+                    logger,
+                    listOf(createFactory({ true }, deviceItem2)),
+                    emptyList(),
+                    testScope.backgroundScope,
+                    dispatcher
+                )
             val latest by collectLastValue(interactor.deviceItemUpdate)
             val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
             interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
index 85e8ab4..5741d64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
@@ -122,6 +122,7 @@
 
     @Test
     @Throws(IOException::class)
+    @DisableFlags(FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE)
     fun test_imageClipData_loadFailure() {
         whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
         whenever(mMockContext.resources).thenReturn(mContext.resources)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
new file mode 100644
index 0000000..5d5c120
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DisplayScopeRepositoryImplTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val testScope = kosmos.testScope
+    private val fakeDisplayRepository = kosmos.displayRepository
+
+    private val repo =
+        DisplayScopeRepositoryImpl(
+            kosmos.applicationCoroutineScope,
+            kosmos.testDispatcher,
+            fakeDisplayRepository,
+        )
+
+    @Before
+    fun setUp() {
+        repo.start()
+    }
+
+    @Test
+    fun scopeForDisplay_multipleCallsForSameDisplayId_returnsSameInstance() {
+        val scopeForDisplay = repo.scopeForDisplay(displayId = 1)
+
+        assertThat(repo.scopeForDisplay(displayId = 1)).isSameInstanceAs(scopeForDisplay)
+    }
+
+    @Test
+    fun scopeForDisplay_differentDisplayId_returnsNewInstance() {
+        val scopeForDisplay1 = repo.scopeForDisplay(displayId = 1)
+        val scopeForDisplay2 = repo.scopeForDisplay(displayId = 2)
+
+        assertThat(scopeForDisplay1).isNotSameInstanceAs(scopeForDisplay2)
+    }
+
+    @Test
+    fun scopeForDisplay_activeByDefault() =
+        testScope.runTest {
+            val scopeForDisplay = repo.scopeForDisplay(displayId = 1)
+
+            assertThat(scopeForDisplay.isActive).isTrue()
+        }
+
+    @Test
+    fun scopeForDisplay_afterDisplayRemoved_scopeIsCancelled() =
+        testScope.runTest {
+            val scopeForDisplay = repo.scopeForDisplay(displayId = 1)
+
+            fakeDisplayRepository.removeDisplay(displayId = 1)
+
+            assertThat(scopeForDisplay.isActive).isFalse()
+        }
+
+    @Test
+    fun scopeForDisplay_afterDisplayRemoved_returnsNewInstance() =
+        testScope.runTest {
+            val initialScope = repo.scopeForDisplay(displayId = 1)
+
+            fakeDisplayRepository.removeDisplay(displayId = 1)
+
+            val newScope = repo.scopeForDisplay(displayId = 1)
+            assertThat(newScope).isNotSameInstanceAs(initialScope)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
new file mode 100644
index 0000000..ff3186a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import android.content.testableContext
+import android.platform.test.annotations.EnableFlags
+import android.view.Display
+import android.view.mockWindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.shared.model.DisplayWindowProperties
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
+    private val fakeDisplayRepository = kosmos.displayRepository
+    private val testScope = kosmos.testScope
+
+    private val applicationContext = kosmos.testableContext
+    private val applicationWindowManager = kosmos.mockWindowManager
+
+    private val repo =
+        DisplayWindowPropertiesRepositoryImpl(
+            kosmos.applicationCoroutineScope,
+            applicationContext,
+            applicationWindowManager,
+            fakeDisplayRepository,
+        )
+
+    @Before
+    fun start() {
+        repo.start()
+    }
+
+    @Before
+    fun addDisplays() = runBlocking {
+        fakeDisplayRepository.addDisplay(createDisplay(DEFAULT_DISPLAY_ID))
+        fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID))
+    }
+
+    @Test
+    fun get_defaultDisplayId_returnsDefaultProperties() =
+        testScope.runTest {
+            val displayContext = repo.get(DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            assertThat(displayContext)
+                .isEqualTo(
+                    DisplayWindowProperties(
+                        displayId = DEFAULT_DISPLAY_ID,
+                        windowType = WINDOW_TYPE_FOO,
+                        context = applicationContext,
+                        windowManager = applicationWindowManager,
+                    )
+                )
+        }
+
+    @Test
+    fun get_nonDefaultDisplayId_returnsNewStatusBarContext() =
+        testScope.runTest {
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            assertThat(displayContext.context).isNotSameInstanceAs(applicationContext)
+        }
+
+    @Test
+    fun get_nonDefaultDisplayId_returnsNewWindowManager() =
+        testScope.runTest {
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            assertThat(displayContext.windowManager).isNotSameInstanceAs(applicationWindowManager)
+        }
+
+    @Test
+    fun get_multipleCallsForDefaultDisplay_returnsSameInstance() =
+        testScope.runTest {
+            val displayContext = repo.get(DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            assertThat(repo.get(DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO))
+                .isSameInstanceAs(displayContext)
+        }
+
+    @Test
+    fun get_multipleCallsForNonDefaultDisplay_returnsSameInstance() =
+        testScope.runTest {
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            assertThat(repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO))
+                .isSameInstanceAs(displayContext)
+        }
+
+    @Test
+    fun get_multipleCalls_differentType_returnsNewInstance() =
+        testScope.runTest {
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            assertThat(repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_BAR))
+                .isNotSameInstanceAs(displayContext)
+        }
+
+    @Test
+    fun get_afterDisplayRemoved_returnsNewInstance() =
+        testScope.runTest {
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+            fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
+            fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID))
+
+            assertThat(repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO))
+                .isNotSameInstanceAs(displayContext)
+        }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun get_nonExistingDisplayId_throws() =
+        testScope.runTest { repo.get(NON_EXISTING_DISPLAY_ID, WINDOW_TYPE_FOO) }
+
+    private fun createDisplay(displayId: Int) =
+        mock<Display> { on { getDisplayId() } doReturn displayId }
+
+    companion object {
+        private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
+        private const val NON_DEFAULT_DISPLAY_ID = DEFAULT_DISPLAY_ID + 1
+        private const val NON_EXISTING_DISPLAY_ID = DEFAULT_DISPLAY_ID + 2
+        private const val WINDOW_TYPE_FOO = 123
+        private const val WINDOW_TYPE_BAR = 321
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index b87647e..eb72f29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -48,11 +48,11 @@
 import android.app.ActivityManager;
 import android.content.res.Configuration;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
+import androidx.test.annotation.UiThreadTest;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -69,7 +69,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @UiThreadTest
 public class DozeMachineTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index ae635b8..df50f76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -77,6 +77,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.RingerModeLiveData;
@@ -125,6 +126,7 @@
     @Mock private LightBarController mLightBarController;
     @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
     @Mock private StatusBarWindowController mStatusBarWindowController;
+    @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore;
     @Mock private IWindowManager mWindowManager;
     @Mock private Executor mBackgroundExecutor;
     @Mock private UiEventLogger mUiEventLogger;
@@ -155,7 +157,8 @@
         when(mUserContextProvider.getUserContext()).thenReturn(mContext);
         when(mResources.getConfiguration()).thenReturn(
                 getContext().getResources().getConfiguration());
-
+        when(mStatusBarWindowControllerStore.getDefaultDisplay())
+                .thenReturn(mStatusBarWindowController);
         mGlobalSettings = new FakeGlobalSettings();
         mSecureSettings = new FakeSettings();
         mInteractor = mKosmos.getGlobalActionsInteractor();
@@ -184,7 +187,7 @@
                 mStatusBarService,
                 mLightBarController,
                 mNotificationShadeWindowController,
-                mStatusBarWindowController,
+                mStatusBarWindowControllerStore,
                 mWindowManager,
                 mBackgroundExecutor,
                 mUiEventLogger,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 6608542..b3cccea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -26,6 +26,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR;
+import static com.android.systemui.Flags.FLAG_RELOCK_WITH_POWER_BUTTON_IMMEDIATELY;
 import static com.android.systemui.Flags.FLAG_SIM_PIN_BOUNCER_RESET;
 import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
 import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
@@ -63,6 +64,7 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
@@ -841,6 +843,32 @@
 
     @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    @EnableFlags(FLAG_RELOCK_WITH_POWER_BUTTON_IMMEDIATELY)
+    public void testCancelKeyguardExitAnimationDueToSleep_withPendingLockAndRelockFlag_keyguardWillBeShowing() {
+        startMockKeyguardExitAnimation();
+
+        mViewMediator.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
+        mViewMediator.onFinishedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, false);
+
+        cancelMockKeyguardExitAnimation();
+
+        mViewMediator.maybeHandlePendingLock();
+        TestableLooper.get(this).processAllMessages();
+
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+
+        verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(true);
+
+        // Unlock animators call `exitKeyguardAndFinishSurfaceBehindRemoteAnimation` when canceled
+        mViewMediator.exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mUpdateMonitor, never()).dispatchKeyguardDismissAnimationFinished();
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    @DisableFlags(FLAG_RELOCK_WITH_POWER_BUTTON_IMMEDIATELY)
     public void testCancelKeyguardExitAnimationDueToSleep_withPendingLock_keyguardWillBeShowing() {
         startMockKeyguardExitAnimation();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 96a0aad..ecc62e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -298,7 +298,7 @@
             underTest.applyDefaultConstraints(cs)
             val referencedIds =
                 cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom)
-            referencedIds.contentEquals(intArrayOf(R.id.lockscreen_clock_view))
+            referencedIds.contentEquals(intArrayOf(customR.id.lockscreen_clock_view))
         }
 
     @Test
@@ -323,7 +323,7 @@
         }
 
     private fun assertLargeClockTop(cs: ConstraintSet, expectedLargeClockTopMargin: Int) {
-        val largeClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view_large)
+        val largeClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view_large)
         assertThat(largeClockConstraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
         assertThat(largeClockConstraint.layout.topMargin).isEqualTo(expectedLargeClockTopMargin)
     }
@@ -332,7 +332,7 @@
         val smallClockGuidelineConstraint = cs.getConstraint(R.id.small_clock_guideline_top)
         assertThat(smallClockGuidelineConstraint.layout.topToTop).isEqualTo(-1)
 
-        val smallClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view)
+        val smallClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view)
         assertThat(smallClockConstraint.layout.topToBottom)
             .isEqualTo(R.id.small_clock_guideline_top)
         assertThat(smallClockConstraint.layout.topMargin).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index bfb8a57..cea51a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -18,13 +18,11 @@
 package com.android.systemui.keyguard.ui.view.layout.sections
 
 import android.graphics.Point
-import android.platform.test.annotations.DisableFlags
 import android.view.WindowManager
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.keyguard.LegacyLockIconViewController
 import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -60,7 +58,6 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
     @Mock private lateinit var notificationPanelView: NotificationPanelView
     private lateinit var featureFlags: FakeFeatureFlags
-    @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
     @Mock private lateinit var falsingManager: FalsingManager
     @Mock private lateinit var deviceEntryIconViewModel: DeviceEntryIconViewModel
     private lateinit var underTest: DefaultDeviceEntrySection
@@ -81,7 +78,6 @@
                 context,
                 notificationPanelView,
                 featureFlags,
-                { lockIconViewController },
                 { deviceEntryIconViewModel },
                 { mock(DeviceEntryForegroundViewModel::class.java) },
                 { mock(DeviceEntryBackgroundViewModel::class.java) },
@@ -102,37 +98,13 @@
     @Test
     fun addViewsConditionally_migrateAndRefactorFlagsOn() {
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         val constraintLayout = ConstraintLayout(context, null)
         underTest.addViews(constraintLayout)
         assertThat(constraintLayout.childCount).isGreaterThan(0)
     }
 
     @Test
-    @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-    fun addViewsConditionally_migrateFlagOff() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        val constraintLayout = ConstraintLayout(context, null)
-        underTest.addViews(constraintLayout)
-        assertThat(constraintLayout.childCount).isEqualTo(0)
-    }
-
-    @Test
-    fun applyConstraints_udfps_refactor_off() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        val cs = ConstraintSet()
-        underTest.applyConstraints(cs)
-
-        val constraint = cs.getConstraint(R.id.lock_icon_view)
-
-        assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
-        assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
-    }
-
-    @Test
-    fun applyConstraints_udfps_refactor_on() {
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+    fun applyConstraints() {
         whenever(deviceEntryIconViewModel.isUdfpsSupported).thenReturn(MutableStateFlow(false))
         val cs = ConstraintSet()
         underTest.applyConstraints(cs)
@@ -144,24 +116,7 @@
     }
 
     @Test
-    fun testCenterIcon_udfps_refactor_off() {
-        mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-        val cs = ConstraintSet()
-        underTest.centerIcon(Point(5, 6), 1F, cs)
-
-        val constraint = cs.getConstraint(R.id.lock_icon_view)
-
-        assertThat(constraint.layout.mWidth).isEqualTo(2)
-        assertThat(constraint.layout.mHeight).isEqualTo(2)
-        assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
-        assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
-        assertThat(constraint.layout.topMargin).isEqualTo(5)
-        assertThat(constraint.layout.startMargin).isEqualTo(4)
-    }
-
-    @Test
-    fun testCenterIcon_udfps_refactor_on() {
-        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+    fun testCenterIcon() {
         val cs = ConstraintSet()
         underTest.centerIcon(Point(5, 6), 1F, cs)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 5b21662..cb2c8fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -114,9 +114,6 @@
     private lateinit var dozingToLockscreenTransitionViewModel:
         DozingToLockscreenTransitionViewModel
     @Mock
-    private lateinit var dreamingHostedToLockscreenTransitionViewModel:
-        DreamingHostedToLockscreenTransitionViewModel
-    @Mock
     private lateinit var dreamingToLockscreenTransitionViewModel:
         DreamingToLockscreenTransitionViewModel
     @Mock
@@ -135,9 +132,6 @@
     private lateinit var lockscreenToDozingTransitionViewModel:
         LockscreenToDozingTransitionViewModel
     @Mock
-    private lateinit var lockscreenToDreamingHostedTransitionViewModel:
-        LockscreenToDreamingHostedTransitionViewModel
-    @Mock
     private lateinit var lockscreenToDreamingTransitionViewModel:
         LockscreenToDreamingTransitionViewModel
     @Mock
@@ -182,8 +176,8 @@
                     BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
                     ":" +
-                    BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
-            )
+                    BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET,
+            ),
         )
 
         homeControlsQuickAffordanceConfig =
@@ -263,8 +257,6 @@
         whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha)
             .thenReturn(intendedAlphaMutableStateFlow)
         whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
-        whenever(dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha)
-            .thenReturn(emptyFlow())
         whenever(dreamingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
         whenever(goneToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
         whenever(occludedToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
@@ -274,8 +266,6 @@
         whenever(lockscreenToAodTransitionViewModel.shortcutsAlpha)
             .thenReturn(intendedAlphaMutableStateFlow)
         whenever(lockscreenToDozingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
-        whenever(lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha)
-            .thenReturn(emptyFlow())
         whenever(lockscreenToDreamingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
         whenever(lockscreenToGoneTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
         whenever(lockscreenToOccludedTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
@@ -314,8 +304,6 @@
                 shadeInteractor = shadeInteractor,
                 aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
                 dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
-                dreamingHostedToLockscreenTransitionViewModel =
-                    dreamingHostedToLockscreenTransitionViewModel,
                 dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
                 goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel,
                 occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
@@ -326,8 +314,6 @@
                     glanceableHubToLockscreenTransitionViewModel,
                 lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
                 lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
-                lockscreenToDreamingHostedTransitionViewModel =
-                    lockscreenToDreamingHostedTransitionViewModel,
                 lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel,
                 lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel,
                 lockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel,
@@ -683,8 +669,8 @@
                 min(
                     1f,
                     KeyguardQuickAffordancesCombinedViewModel
-                        .AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD + 0.1f
-                ),
+                        .AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD + 0.1f,
+                )
             )
 
             val testConfig =
@@ -779,7 +765,7 @@
         testScope.runTest {
             kosmos.setTransition(
                 sceneTransition = Idle(Scenes.Lockscreen),
-                stateTransition = TransitionStep(from = AOD, to = LOCKSCREEN)
+                stateTransition = TransitionStep(from = AOD, to = LOCKSCREEN),
             )
             intendedShadeAlphaMutableStateFlow.value = 0.25f
             val underTest = collectLastValue(underTest.transitionAlpha)
@@ -794,7 +780,7 @@
         testScope.runTest {
             kosmos.setTransition(
                 sceneTransition = Idle(Scenes.Gone),
-                stateTransition = TransitionStep(from = AOD, to = GONE)
+                stateTransition = TransitionStep(from = AOD, to = GONE),
             )
             intendedShadeAlphaMutableStateFlow.value = 0.5f
             val underTest = collectLastValue(underTest.transitionAlpha)
@@ -829,7 +815,7 @@
                         when (testConfig.isActivated) {
                             true -> ActivationState.Active
                             false -> ActivationState.Inactive
-                        }
+                        },
                 )
             } else {
                 KeyguardQuickAffordanceConfig.LockScreenState.Hidden
@@ -878,7 +864,7 @@
         val intent: Intent? = null,
         val isSelected: Boolean = false,
         val isDimmed: Boolean = false,
-        val slotId: String = ""
+        val slotId: String = "",
     ) {
         init {
             check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ActivatableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ActivatableTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 8731853..63ec78fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -35,6 +35,8 @@
 import android.app.WallpaperColors;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -44,6 +46,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.media.flags.Flags;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.SysuiTestCase;
@@ -738,4 +741,68 @@
 
         assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(updatedList.size());
     }
+
+    @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagDisabled_InputDeviceMutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(true /* isInputDevice */, true /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.media_output_icon_volume_off);
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagDisabled_OutputDeviceMutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(false /* isInputDevice */, true /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.media_output_icon_volume_off);
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagDisabled_InputDeviceUnmutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(true /* isInputDevice */, false /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.media_output_icon_volume);
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagDisabled_OutputDeviceUnmutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(false /* isInputDevice */, false /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.media_output_icon_volume);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagEnabled_InputDeviceMutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(true /* isInputDevice */, true /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.ic_mic_off);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagEnabled_OutputDeviceMutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(false /* isInputDevice */, true /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.media_output_icon_volume_off);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagEnabled_InputDeviceUnmutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(true /* isInputDevice */, false /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.ic_mic_26dp);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void getDrawableId_FlagEnabled_OutputDeviceUnmutedIcon() {
+        assertThat(
+                mViewHolder.getDrawableId(false /* isInputDevice */, false /* isMutedVolumeIcon */))
+                .isEqualTo(R.drawable.media_output_icon_volume);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 07e48b9..bf4ef50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -125,6 +125,8 @@
     private static final boolean VOLUME_FIXED_TRUE = true;
     private static final int LATCH_COUNT_DOWN_TIME_IN_SECOND = 5;
     private static final int LATCH_TIME_OUT_TIME_IN_SECOND = 10;
+    private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic";
+    private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
 
     @Mock
     private DialogTransitionAnimator mDialogTransitionAnimator;
@@ -568,7 +570,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_BUILTIN_MIC);
         final MediaDevice mediaDevice4 =
                 InputMediaDevice.create(
                         mContext,
@@ -576,7 +579,8 @@
                         AudioDeviceInfo.TYPE_WIRED_HEADSET,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_WIRED_HEADSET);
         final List<MediaDevice> inputDevices = new ArrayList<>();
         inputDevices.add(mediaDevice3);
         inputDevices.add(mediaDevice4);
@@ -1355,7 +1359,8 @@
                         AudioDeviceInfo.TYPE_BUILTIN_MIC,
                         MAX_VOLUME,
                         CURRENT_VOLUME,
-                        VOLUME_FIXED_TRUE);
+                        VOLUME_FIXED_TRUE,
+                        PRODUCT_NAME_BUILTIN_MIC);
         mMediaSwitchingController.connectDevice(inputMediaDevice);
 
         CountDownLatch latch = new CountDownLatch(LATCH_COUNT_DOWN_TIME_IN_SECOND);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index 413aa55..bc3c0d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -40,6 +40,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.hardware.devicestate.DeviceStateManager;
 import android.util.SparseArray;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -94,6 +95,8 @@
     private NavigationBarComponent.Factory mNavigationBarFactory;
     @Mock
     TaskbarDelegate mTaskbarDelegate;
+    @Mock
+    private DeviceStateManager mDeviceStateManager;
 
     @Before
     public void setUp() {
@@ -116,7 +119,8 @@
                         Optional.of(mock(Pip.class)),
                         Optional.of(mock(BackAnimation.class)),
                         mock(SecureSettings.class),
-                        mDisplayTracker));
+                        mDisplayTracker,
+                        mDeviceStateManager));
         initializeNavigationBars();
         mMockitoSession = mockitoSession().mockStatic(Utilities.class).startMocking();
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSImplTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
index ae2a9ad..6fce108 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
@@ -20,10 +20,10 @@
 import android.content.Context
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
-import android.testing.UiThreadTest
 import android.view.ContextThemeWrapper
 import android.view.View
 import android.widget.ImageView
+import androidx.test.annotation.UiThreadTest
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.qs.QSTile
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index 643debf..7b24233 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -17,13 +17,13 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.testing.TestableLooper;
-import android.testing.UiThreadTest;
 import android.view.View;
 import android.view.Window;
 import android.widget.LinearLayout;
 import android.widget.Switch;
 import android.widget.TextView;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index 3aaaf95..83ede46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -16,6 +16,11 @@
 
 package com.android.systemui.reardisplay;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotSame;
 
@@ -53,6 +58,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+import java.util.Set;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -69,13 +77,25 @@
     private SysUiState mSysUiState;
     @Mock
     private Resources mResources;
+    @Mock
+    private DeviceStateManager mDeviceStateManager;
 
     LayoutInflater mLayoutInflater = LayoutInflater.from(mContext);
 
     private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
 
-    private static final int CLOSED_BASE_STATE = 0;
-    private static final int OPEN_BASE_STATE = 1;
+    private static final DeviceState CLOSED_BASE_STATE = new DeviceState(
+            new DeviceState.Configuration.Builder(0, "CLOSED").setSystemProperties(
+                    Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY))
+                    .setPhysicalProperties(Set.of(
+                            PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED))
+                    .build());
+    private static final DeviceState OPEN_BASE_STATE = new DeviceState(
+            new DeviceState.Configuration.Builder(1, "OPEN").setSystemProperties(
+                    Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY))
+                    .setPhysicalProperties(Set.of(
+                            PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))
+                    .build());
 
     @Before
     public void setup() {
@@ -92,12 +112,13 @@
                 mFakeExecutor,
                 mResources,
                 mLayoutInflater,
-                mSystemUIDialogFactory);
+                mSystemUIDialogFactory,
+                mDeviceStateManager);
         controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
-        controller.setFoldedStates(new int[]{0});
+        controller.setFoldedStates(List.of(0));
         controller.setAnimationRepeatCount(0);
 
-        controller.showRearDisplayDialog(CLOSED_BASE_STATE);
+        controller.showRearDisplayDialog(CLOSED_BASE_STATE.getIdentifier());
         verify(mSystemUIDialog).show();
 
         View container = getDialogViewContainer();
@@ -115,12 +136,13 @@
                 mFakeExecutor,
                 mResources,
                 mLayoutInflater,
-                mSystemUIDialogFactory);
+                mSystemUIDialogFactory,
+                mDeviceStateManager);
         controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
-        controller.setFoldedStates(new int[]{0});
+        controller.setFoldedStates(List.of(0));
         controller.setAnimationRepeatCount(0);
 
-        controller.showRearDisplayDialog(CLOSED_BASE_STATE);
+        controller.showRearDisplayDialog(CLOSED_BASE_STATE.getIdentifier());
         verify(mSystemUIDialog).show();
         View container = getDialogViewContainer();
         TextView deviceClosedTitleTextView = container.findViewById(
@@ -144,12 +166,13 @@
                 mFakeExecutor,
                 mResources,
                 mLayoutInflater,
-                mSystemUIDialogFactory);
+                mSystemUIDialogFactory,
+                mDeviceStateManager);
         controller.setDeviceStateManagerCallback(new TestDeviceStateManagerCallback());
-        controller.setFoldedStates(new int[]{0});
+        controller.setFoldedStates(List.of(0));
         controller.setAnimationRepeatCount(0);
 
-        controller.showRearDisplayDialog(OPEN_BASE_STATE);
+        controller.showRearDisplayDialog(OPEN_BASE_STATE.getIdentifier());
 
         verify(mSystemUIDialog).show();
         View container = getDialogViewContainer();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 4959224..3bfde68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -35,7 +35,6 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
@@ -122,9 +121,6 @@
         Optional<UnfoldTransitionProgressForwarder>
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
 
-    @Mock
-    private lateinit var keyboardTouchpadEduStatsInteractor: KeyboardTouchpadEduStatsInteractor
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -293,7 +289,6 @@
             dumpManager,
             unfoldTransitionProgressForwarder,
             broadcastDispatcher,
-            keyboardTouchpadEduStatsInteractor,
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 52266ee..ba6518f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -16,31 +16,32 @@
 
 package com.android.systemui.screenshot
 
+import android.app.assist.AssistContent
 import android.content.Intent
 import android.net.Uri
 import android.os.Process
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
 import com.google.common.truth.Truth.assertThat
 import java.util.UUID
 import kotlin.test.Test
-import kotlinx.coroutines.test.runTest
 import org.junit.runner.RunWith
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
+@RunWith(AndroidJUnit4::class)
 class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
     private val actionExecutor = mock<ActionExecutor>()
     private val uiEventLogger = mock<UiEventLogger>()
@@ -76,7 +77,7 @@
     }
 
     @Test
-    fun actionAccessed_withResult_launchesIntent() = runTest {
+    fun actionAccessed_withResult_launchesIntent() {
         actionsProvider = createActionsProvider()
 
         actionsProvider.setCompletedScreenshot(validResult)
@@ -94,7 +95,32 @@
     }
 
     @Test
-    fun actionAccessed_whilePending_launchesMostRecentAction() = runTest {
+    @EnableFlags(Flags.FLAG_SCREENSHOT_CONTEXT_URL)
+    fun shareAction_includesAssistContentUri() {
+        actionsProvider = createActionsProvider()
+
+        actionsProvider.setCompletedScreenshot(validResult)
+
+        val uri = Uri.parse("http://www.android.com")
+        val assistContent = mock<AssistContent>() { on { webUri } doReturn uri }
+
+        actionsProvider.onAssistContent(assistContent)
+
+        val actionButtonCaptor = argumentCaptor<() -> Unit>()
+        verify(actionsCallback, times(2))
+            .provideActionButton(any(), any(), actionButtonCaptor.capture())
+        actionButtonCaptor.firstValue.invoke()
+
+        val intentCaptor = argumentCaptor<Intent>()
+        verify(actionExecutor)
+            .startSharedTransition(intentCaptor.capture(), eq(Process.myUserHandle()), eq(false))
+        val innerIntent =
+            intentCaptor.lastValue.extras?.getParcelable(Intent.EXTRA_INTENT, Intent::class.java)
+        assertThat(innerIntent?.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(uri.toString())
+    }
+
+    @Test
+    fun actionAccessed_whilePending_launchesMostRecentAction() {
         actionsProvider = createActionsProvider()
 
         val previewActionCaptor = argumentCaptor<PreviewAction>()
@@ -116,7 +142,7 @@
     }
 
     @Test
-    fun scrollChipClicked_callsOnClick() = runTest {
+    fun scrollChipClicked_callsOnClick() {
         actionsProvider = createActionsProvider()
 
         val onScrollClick = mock<Runnable>()
@@ -131,7 +157,7 @@
     }
 
     @Test
-    fun scrollChipClicked_afterInvalidate_doesNothing() = runTest {
+    fun scrollChipClicked_afterInvalidate_doesNothing() {
         actionsProvider = createActionsProvider()
 
         val onScrollClick = mock<Runnable>()
@@ -147,7 +173,7 @@
     }
 
     @Test
-    fun scrollChipClicked_afterUpdate_runsNewAction() = runTest {
+    fun scrollChipClicked_afterUpdate_runsNewAction() {
         actionsProvider = createActionsProvider()
 
         val onScrollClick = mock<Runnable>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
index e4329a5..7709a65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
@@ -16,39 +16,145 @@
 package com.android.systemui.screenshot.policy
 
 import android.content.ComponentName
+import android.graphics.Bitmap
 import android.graphics.Insets
 import android.graphics.Rect
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.systemui.Flags
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.screenshot.ImageCapture
 import com.android.systemui.screenshot.ScreenshotData
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.Bounds.FULL_SCREEN
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.TaskSpec
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.emptyDisplayContent
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.launcherOnly
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen
 import com.android.systemui.screenshot.data.repository.DisplayContentRepository
+import com.android.systemui.screenshot.data.repository.profileTypeRepository
+import com.android.systemui.screenshot.policy.CaptureType.FullScreen
+import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
 import com.android.systemui.screenshot.policy.TestUserIds.PERSONAL
 import com.android.systemui.screenshot.policy.TestUserIds.WORK
+import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 class PolicyRequestProcessorTest {
+    private val kosmos = Kosmos()
 
-    val imageCapture =
-        object : ImageCapture {
-            override fun captureDisplay(displayId: Int, crop: Rect?) = null
+    private val screenshotRequest =
+        ScreenshotData(
+            TAKE_SCREENSHOT_FULLSCREEN,
+            SCREENSHOT_KEY_CHORD,
+            UserHandle.CURRENT,
+            topComponent = null,
+            originalScreenBounds = FULL_SCREEN,
+            taskId = -1,
+            originalInsets = Insets.NONE,
+            bitmap = null,
+            displayId = DEFAULT_DISPLAY,
+        )
 
-            override suspend fun captureTask(taskId: Int) = null
-        }
+    val defaultComponent = ComponentName("default", "Component")
+    val defaultOwner = UserHandle.of(PERSONAL)
+
+    @get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+    /** Tests applying CaptureParameters with 'IsolatedTask' CaptureType */
+    @Test
+    @EnableFlags(Flags.FLAG_SCREENSHOT_POLICY_SPLIT_AND_DESKTOP_MODE)
+    fun testProcess_newPolicy_isolatedTask() = runTest {
+        val taskImage = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+
+        /* Create a policy request processor with no capture policies */
+        val requestProcessor =
+            PolicyRequestProcessor(
+                Dispatchers.Unconfined,
+                createImageCapture(task = taskImage),
+                policy = ScreenshotPolicy(kosmos.profileTypeRepository),
+                policies = emptyList(),
+                defaultOwner = defaultOwner,
+                defaultComponent = defaultComponent,
+                displayTasks = { emptyDisplayContent },
+            )
+
+        val result =
+            requestProcessor.modify(
+                screenshotRequest,
+                CaptureParameters(
+                    IsolatedTask(taskId = TASK_ID, taskBounds = null),
+                    ComponentName.unflattenFromString(FILES),
+                    UserHandle.of(WORK),
+                ),
+            )
+
+        assertWithMessage("The screenshot bitmap").that(result.bitmap).isSameInstanceAs(taskImage)
+
+        assertWithMessage("The assigned owner of the screenshot")
+            .that(result.userHandle)
+            .isEqualTo(UserHandle.of(WORK))
+
+        assertWithMessage("The topComponent of the screenshot")
+            .that(result.topComponent)
+            .isEqualTo(ComponentName.unflattenFromString(FILES))
+
+        assertWithMessage("Task ID").that(result.taskId).isEqualTo(TASK_ID)
+    }
+
+    /** Tests applying CaptureParameters with 'FullScreen' CaptureType */
+    @Test
+    @EnableFlags(Flags.FLAG_SCREENSHOT_POLICY_SPLIT_AND_DESKTOP_MODE)
+    fun testProcess_newPolicy_fullScreen() = runTest {
+        val screenImage = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+
+        /* Create a policy request processor with no capture policies */
+        val requestProcessor =
+            PolicyRequestProcessor(
+                Dispatchers.Unconfined,
+                createImageCapture(display = screenImage),
+                policy = ScreenshotPolicy(kosmos.profileTypeRepository),
+                policies = emptyList(),
+                defaultOwner = defaultOwner,
+                defaultComponent = defaultComponent,
+                displayTasks = { emptyDisplayContent },
+            )
+
+        val result =
+            requestProcessor.modify(
+                screenshotRequest,
+                CaptureParameters(FullScreen(displayId = 0), defaultComponent, defaultOwner),
+            )
+
+        assertWithMessage("The result bitmap").that(result.bitmap).isSameInstanceAs(screenImage)
+
+        assertWithMessage("The assigned owner of the screenshot")
+            .that(result.userHandle)
+            .isEqualTo(defaultOwner)
+
+        assertWithMessage("The topComponent of the screenshot")
+            .that(result.topComponent)
+            .isEqualTo(defaultComponent)
+
+        assertWithMessage("Task ID").that(result.taskId).isEqualTo(-1)
+    }
 
     /** Tests behavior when no policies are applied */
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_POLICY_SPLIT_AND_DESKTOP_MODE)
     fun testProcess_defaultOwner_whenNoPolicyApplied() {
         val fullScreenWork = DisplayContentRepository {
             singleFullScreen(TaskSpec(taskId = TASK_ID, name = FILES, userId = WORK))
@@ -71,7 +177,8 @@
         val requestProcessor =
             PolicyRequestProcessor(
                 Dispatchers.Unconfined,
-                imageCapture,
+                createImageCapture(),
+                policy = ScreenshotPolicy(kosmos.profileTypeRepository),
                 policies = emptyList(),
                 defaultOwner = UserHandle.of(PERSONAL),
                 defaultComponent = ComponentName("default", "Component"),
@@ -91,6 +198,72 @@
         assertWithMessage("Task ID").that(result.taskId).isEqualTo(TASK_ID)
     }
 
+    @Test
+    fun testProcess_throwsWhenCaptureFails() {
+        val request = ScreenshotData.forTesting()
+
+        /* Create a policy request processor with no capture policies */
+        val requestProcessor =
+            PolicyRequestProcessor(
+                Dispatchers.Unconfined,
+                createImageCapture(display = null),
+                policy = ScreenshotPolicy(kosmos.profileTypeRepository),
+                policies = emptyList(),
+                defaultComponent = ComponentName("default", "Component"),
+                displayTasks = DisplayContentRepository { launcherOnly() },
+            )
+
+        val result = runCatching { runBlocking { requestProcessor.process(request) } }
+
+        assertThat(result.isFailure).isTrue()
+    }
+
+    @Test
+    fun testProcess_throwsWhenTaskCaptureFails() {
+        val request = ScreenshotData.forTesting()
+        val fullScreenWork = DisplayContentRepository {
+            singleFullScreen(TaskSpec(taskId = TASK_ID, name = FILES, userId = WORK))
+        }
+
+        val captureTaskPolicy = CapturePolicy {
+            CapturePolicy.PolicyResult.Matched(
+                policy = "",
+                reason = "",
+                parameters =
+                    CaptureParameters(
+                        IsolatedTask(taskId = 0, taskBounds = null),
+                        null,
+                        UserHandle.CURRENT,
+                    ),
+            )
+        }
+
+        /* Create a policy request processor with no capture policies */
+        val requestProcessor =
+            PolicyRequestProcessor(
+                Dispatchers.Unconfined,
+                createImageCapture(task = null),
+                policy = ScreenshotPolicy(kosmos.profileTypeRepository),
+                policies = listOf(captureTaskPolicy),
+                defaultComponent = ComponentName("default", "Component"),
+                displayTasks = fullScreenWork,
+            )
+
+        val result = runCatching { runBlocking { requestProcessor.process(request) } }
+
+        assertThat(result.isFailure).isTrue()
+    }
+
+    private fun createImageCapture(
+        display: Bitmap? = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888),
+        task: Bitmap? = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888),
+    ) =
+        object : ImageCapture {
+            override fun captureDisplay(displayId: Int, crop: Rect?) = display
+
+            override suspend fun captureTask(taskId: Int) = task
+        }
+
     companion object {
         const val TASK_ID = 1001
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
index a68ba06..e2f22cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
@@ -17,20 +17,16 @@
 package com.android.systemui.statusbar
 
 import android.testing.TestableLooper
-import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE
 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP
 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
 import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
@@ -38,36 +34,6 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class KeyguardIndicationControllerWithCoroutinesTest : KeyguardIndicationControllerBaseTest() {
     @Test
-    fun testIndicationAreaVisibility_onLockscreenHostedDreamStateChanged() =
-        runBlocking(IMMEDIATE) {
-            // GIVEN starting state for keyguard indication and wallpaper dream enabled
-            createController()
-            mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true)
-            mController.setVisible(true)
-
-            // THEN indication area is visible
-            verify(mIndicationArea, times(2)).visibility = View.VISIBLE
-
-            // WHEN the device is dreaming with lockscreen hosted dream
-            mController.mIsActiveDreamLockscreenHostedCallback.accept(
-                true /* isActiveDreamLockscreenHosted */
-            )
-            mExecutor.runAllReady()
-
-            // THEN the indication area is hidden
-            verify(mIndicationArea).visibility = View.GONE
-
-            // WHEN the device stops dreaming with lockscreen hosted dream
-            mController.mIsActiveDreamLockscreenHostedCallback.accept(
-                false /* isActiveDreamLockscreenHosted */
-            )
-            mExecutor.runAllReady()
-
-            // THEN indication area is set visible
-            verify(mIndicationArea, times(3)).visibility = View.VISIBLE
-        }
-
-    @Test
     fun onTrustAgentErrorMessageDelayed_fingerprintEngaged() {
         createController()
         mController.setVisible(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 6febb91..7a579ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -58,7 +58,7 @@
     private static final int MIN_RSSI = -100;
     private static final int MAX_RSSI = -55;
     private WifiInfo mWifiInfo = mock(WifiInfo.class);
-    private VcnTransportInfo mVcnTransportInfo = mock(VcnTransportInfo.class);
+    private VcnTransportInfo mVcnTransportInfo = new VcnTransportInfo.Builder().build();
 
     @Before
     public void setUp() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 376873d..5d8a8fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.BatteryStatusChip
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -63,6 +64,7 @@
     @Mock private lateinit var systemEventCoordinator: SystemEventCoordinator
 
     @Mock private lateinit var statusBarWindowController: StatusBarWindowController
+    @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore
 
     @Mock private lateinit var statusBarContentInsetProvider: StatusBarContentInsetsProvider
 
@@ -82,12 +84,14 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
 
+        whenever(statusBarWindowControllerStore.defaultDisplay)
+            .thenReturn(statusBarWindowController)
         systemClock = FakeSystemClock()
         chipAnimationController =
             SystemEventChipAnimationController(
                 mContext,
-                statusBarWindowController,
-                statusBarContentInsetProvider
+                statusBarWindowControllerStore,
+                statusBarContentInsetProvider,
             )
 
         // StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values.
@@ -660,7 +664,7 @@
             SystemStatusAnimationSchedulerImpl(
                 systemEventCoordinator,
                 chipAnimationController,
-                statusBarWindowController,
+                statusBarWindowControllerStore,
                 dumpManager,
                 systemClock,
                 CoroutineScope(StandardTestDispatcher(testScope.testScheduler)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
deleted file mode 100644
index ac73882..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification
-
-import android.platform.test.annotations.DisableFlags
-import android.provider.DeviceConfig
-import android.provider.Settings
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
-import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
-import com.android.systemui.util.DeviceConfigProxyFake
-import com.android.systemui.util.Utils
-import com.android.systemui.util.mockito.any
-import org.junit.After
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.MockitoSession
-import org.mockito.kotlin.whenever
-import org.mockito.quality.Strictness
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-// this class has no testable logic with either of these flags enabled
-@DisableFlags(PriorityPeopleSection.FLAG_NAME, NotificationMinimalism.FLAG_NAME)
-class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
-    lateinit var manager: NotificationSectionsFeatureManager
-    private val proxyFake = DeviceConfigProxyFake()
-    private lateinit var staticMockSession: MockitoSession
-
-    @Before
-    fun setup() {
-        manager = NotificationSectionsFeatureManager(proxyFake, mContext)
-        manager.clearCache()
-        staticMockSession =
-            ExtendedMockito.mockitoSession()
-                .mockStatic(Utils::class.java)
-                .strictness(Strictness.LENIENT)
-                .startMocking()
-        whenever(Utils.useQsMediaPlayer(any())).thenReturn(false)
-        Settings.Global.putInt(
-            context.getContentResolver(),
-            Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS,
-            0
-        )
-    }
-
-    @After
-    fun teardown() {
-        staticMockSession.finishMocking()
-    }
-
-    @Test
-    fun testPeopleFilteringOff_newInterruptionModelOn() {
-        proxyFake.setProperty(
-            DeviceConfig.NAMESPACE_SYSTEMUI,
-            NOTIFICATIONS_USE_PEOPLE_FILTERING,
-            "false",
-            false
-        )
-
-        assertFalse("People filtering should be disabled", manager.isFilteringEnabled())
-        assertTrue(
-            "Expecting 2 buckets when people filtering is disabled",
-            manager.getNumberOfBuckets() == 2
-        )
-    }
-
-    @Test
-    fun testPeopleFilteringOn_newInterruptionModelOn() {
-        proxyFake.setProperty(
-            DeviceConfig.NAMESPACE_SYSTEMUI,
-            NOTIFICATIONS_USE_PEOPLE_FILTERING,
-            "true",
-            false
-        )
-
-        assertTrue("People filtering should be enabled", manager.isFilteringEnabled())
-        assertTrue(
-            "Expecting 5 buckets when people filtering is enabled",
-            manager.getNumberOfBuckets() == 5
-        )
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index 2d8e692..c8ef663 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -31,12 +31,12 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
 import android.util.FloatProperty;
 import android.util.Property;
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.app.animation.Interpolators;
@@ -270,4 +270,4 @@
         PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
         assertTrue(PropertyAnimator.isAnimating(mView, mProperty));
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt
deleted file mode 100644
index 4d5ea92..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.statusbar.notification.collection.coordinator
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.withArgCaptor
-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
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DreamCoordinatorTest : SysuiTestCase() {
-    @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
-    @Mock private lateinit var notifPipeline: NotifPipeline
-    @Mock private lateinit var filterListener: Pluggable.PluggableListener<NotifFilter>
-
-    private val keyguardRepository = FakeKeyguardRepository()
-    private var fakeEntry: NotificationEntry = NotificationEntryBuilder().build()
-    val testDispatcher = UnconfinedTestDispatcher()
-    val testScope = TestScope(testDispatcher)
-
-    private lateinit var filter: NotifFilter
-    private lateinit var statusBarListener: StatusBarStateController.StateListener
-    private lateinit var dreamCoordinator: DreamCoordinator
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
-        // Build the coordinator
-        dreamCoordinator =
-            DreamCoordinator(
-                statusBarStateController,
-                testScope.backgroundScope,
-                keyguardRepository
-            )
-
-        // Attach the pipeline and capture the listeners/filters that it registers
-        dreamCoordinator.attach(notifPipeline)
-
-        filter = withArgCaptor { verify(notifPipeline).addPreGroupFilter(capture()) }
-        filter.setInvalidationListener(filterListener)
-
-        statusBarListener = withArgCaptor {
-            verify(statusBarStateController).addCallback(capture())
-        }
-    }
-
-    @Test
-    fun hideNotifications_whenDreamingAndOnKeyguard() =
-        testScope.runTest {
-            // GIVEN we are on keyguard and not dreaming
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
-            runCurrent()
-
-            // THEN notifications are not filtered out
-            verifyPipelinesNotInvalidated()
-            assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse()
-
-            // WHEN dreaming starts and the active dream is hosted in lockscreen
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            runCurrent()
-
-            // THEN pipeline is notified and notifications should all be filtered out
-            verifyPipelinesInvalidated()
-            assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue()
-        }
-
-    @Test
-    fun showNotifications_whenDreamingAndNotOnKeyguard() =
-        testScope.runTest {
-            // GIVEN we are on the keyguard and active dream is hosted in lockscreen
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            runCurrent()
-
-            // THEN pipeline is notified and notifications are all filtered out
-            verifyPipelinesInvalidated()
-            clearPipelineInvocations()
-            assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue()
-
-            // WHEN we are no longer on the keyguard
-            statusBarListener.onStateChanged(StatusBarState.SHADE)
-
-            // THEN pipeline is notified and notifications are not filtered out
-            verifyPipelinesInvalidated()
-            assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse()
-        }
-
-    @Test
-    fun showNotifications_whenOnKeyguardAndNotDreaming() =
-        testScope.runTest {
-            // GIVEN we are on the keyguard and active dream is hosted in lockscreen
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            runCurrent()
-
-            // THEN pipeline is notified and notifications are all filtered out
-            verifyPipelinesInvalidated()
-            clearPipelineInvocations()
-            assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue()
-
-            // WHEN the lockscreen hosted dream stops
-            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
-            runCurrent()
-
-            // THEN pipeline is notified and notifications are not filtered out
-            verifyPipelinesInvalidated()
-            assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse()
-        }
-
-    private fun verifyPipelinesInvalidated() {
-        verify(filterListener).onPluggableInvalidated(eq(filter), any())
-    }
-
-    private fun verifyPipelinesNotInvalidated() {
-        verify(filterListener, never()).onPluggableInvalidated(eq(filter), any())
-    }
-
-    private fun clearPipelineInvocations() {
-        clearInvocations(filterListener)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ed7383c..e3e2491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -612,6 +612,8 @@
         Assert.assertNull(child.getParent());
         Assert.assertNull(child.getNotificationParent());
         Assert.assertFalse(child.keepInParentForDismissAnimation());
+        verify(mNotificationTestHelper.getMockLogger())
+                .logCancelAppearDrawing(child.getEntry(), false);
         verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger());
     }
 
@@ -1013,7 +1015,7 @@
         assertThat(row.isHeadsUpAnimatingAway()).isTrue();
 
         // on disappear animation ends
-        row.onAppearAnimationFinished(/* wasAppearing = */ false);
+        row.onAppearAnimationFinished(/* wasAppearing = */ false, /* cancelled = */ false);
         assertThat(row.isHeadsUpAnimatingAway()).isFalse();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 59fc0d1..87cda64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -591,8 +591,8 @@
         ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
         verify(mStackScroller).setFooterView(captor.capture());
 
-        assertNotNull(captor.getValue().findViewById(R.id.manage_text).hasOnClickListeners());
-        assertNotNull(captor.getValue().findViewById(R.id.dismiss_text).hasOnClickListeners());
+        assertNotNull(captor.getValue().findViewById(R.id.manage_text));
+        assertNotNull(captor.getValue().findViewById(R.id.dismiss_text));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 15ea811..1e88215 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -18,10 +18,15 @@
 
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP;
+import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE;
 import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
 import static android.provider.Settings.Global.HEADS_UP_ON;
 
-import static com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR;
 import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
 import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
 import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
@@ -124,7 +129,6 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.notetask.NoteTaskController;
@@ -170,6 +174,7 @@
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.core.StatusBarInitializerImpl;
 import com.android.systemui.statusbar.core.StatusBarOrchestrator;
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -186,6 +191,8 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent;
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -194,6 +201,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.util.FakeEventLog;
 import com.android.systemui.util.WallpaperController;
@@ -222,7 +230,9 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
+import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 
 import javax.inject.Provider;
 
@@ -232,8 +242,22 @@
 @EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
 public class CentralSurfacesImplTest extends SysuiTestCase {
 
-    private static final int FOLD_STATE_FOLDED = 0;
-    private static final int FOLD_STATE_UNFOLDED = 1;
+    private static final DeviceState FOLD_STATE_FOLDED = new DeviceState(
+            new DeviceState.Configuration.Builder(0 /* identifier */, "FOLDED" /* name */)
+                    .setSystemProperties(
+                            Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                                    PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP))
+                    .setPhysicalProperties(
+                            Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED))
+                    .build());
+    private static final DeviceState FOLD_STATE_UNFOLDED = new DeviceState(
+            new DeviceState.Configuration.Builder(1 /* identifier */, "UNFOLDED" /* name */)
+                    .setSystemProperties(
+                            Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+                                    PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE))
+                    .setPhysicalProperties(
+                            Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))
+                    .build());
 
     private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
 
@@ -258,7 +282,6 @@
     @Mock private QuickSettingsController mQuickSettingsController;
     @Mock private IStatusBarService mBarService;
     @Mock private IDreamManager mDreamManager;
-    @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
     @Mock private LightRevealScrim mLightRevealScrim;
     @Mock private DozeScrimController mDozeScrimController;
     @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@@ -292,6 +315,7 @@
     @Mock private KeyguardBypassController mKeyguardBypassController;
     @Mock private AutoHideController mAutoHideController;
     @Mock private StatusBarWindowController mStatusBarWindowController;
+    @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore;
     @Mock private Provider<CollapsedStatusBarFragment> mCollapsedStatusBarFragmentProvider;
     @Mock private StatusBarWindowStateController mStatusBarWindowStateController;
     @Mock private Bubbles mBubbles;
@@ -383,6 +407,9 @@
 
         when(mBubbles.canShowBubbleNotification()).thenReturn(true);
 
+        when(mStatusBarWindowControllerStore.getDefaultDisplay())
+                .thenReturn(mStatusBarWindowController);
+
         mVisualInterruptionDecisionProvider =
                 VisualInterruptionDecisionProviderTestUtil.INSTANCE.createProviderByFlag(
                         mAmbientDisplayConfiguration,
@@ -424,6 +451,8 @@
         when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
         when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
         when(mPowerManagerService.isInteractive()).thenReturn(true);
+        when(mDeviceStateManager.getSupportedDeviceStates())
+                .thenReturn(List.of(FOLD_STATE_FOLDED, FOLD_STATE_UNFOLDED));
 
         doAnswer(invocation -> {
             OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
@@ -465,7 +494,7 @@
                     mKeyguardStateController,
                     mStatusBarStateController,
                     mStatusBarKeyguardViewManager,
-                    mStatusBarWindowController,
+                    mStatusBarWindowControllerStore,
                     mDeviceProvisionedController,
                     mNotificationShadeWindowController,
                     0,
@@ -509,8 +538,10 @@
                 new StatusBarInitializerImpl(
                         mStatusBarWindowController,
                         mCollapsedStatusBarFragmentProvider,
+                        mock(StatusBarRootFactory.class),
+                        mock(HomeStatusBarComponent.Factory.class),
                         emptySet()),
-                mStatusBarWindowController,
+                mStatusBarWindowControllerStore,
                 mStatusBarWindowStateController,
                 new FakeStatusBarModeRepository(),
                 mKeyguardUpdateMonitor,
@@ -598,7 +629,6 @@
                 mWiredChargingRippleController,
                 mDreamManager,
                 mCameraLauncherLazy,
-                () -> mLightRevealScrimViewModel,
                 mLightRevealScrim,
                 mAlternateBouncerInteractor,
                 mUserTracker,
@@ -852,34 +882,6 @@
     }
 
     @Test
-    @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() {
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-        when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
-
-        mCentralSurfaces.updateScrimController();
-
-        verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
-        verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
-    }
-
-    @Test
-    @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void testOccludingQSNotExpanded_flagOff_transitionToAuthScrimmed() {
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // GIVEN device occluded and panel is NOT expanded
-        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
-        when(mKeyguardStateController.isOccluded()).thenReturn(true);
-        when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false);
-
-        mCentralSurfaces.updateScrimController();
-
-        verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED));
-    }
-
-    @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     public void testNotOccluding_QSNotExpanded_flagOn_doesNotTransitionScrimState() {
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
@@ -895,7 +897,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
     public void testNotOccluding_QSExpanded_flagOn_doesTransitionScrimStateToKeyguard() {
         when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
 
@@ -911,21 +912,6 @@
     }
 
     @Test
-    @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-    public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
-        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
-        // GIVEN device occluded and qs IS expanded
-        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
-        when(mKeyguardStateController.isOccluded()).thenReturn(true);
-        when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
-
-        mCentralSurfaces.updateScrimController();
-
-        verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
-    }
-
-    @Test
     public void testEnteringGlanceableHub_updatesScrim() {
         // Transition to the glanceable hub.
         mKosmos.getCommunalRepository()
@@ -1034,8 +1020,8 @@
 
     @Test
     public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
-        setFoldedStates(FOLD_STATE_FOLDED);
-        setGoToSleepStates(FOLD_STATE_FOLDED);
+        setFoldedStates(FOLD_STATE_FOLDED.getIdentifier());
+        setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier());
         mCentralSurfaces.setBarStateForTest(SHADE);
         when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(true);
 
@@ -1046,8 +1032,8 @@
 
     @Test
     public void deviceStateChange_unfolded_shadeOpen_onKeyguard_doesNotSetLeaveOpenOnKeyguardHide() {
-        setFoldedStates(FOLD_STATE_FOLDED);
-        setGoToSleepStates(FOLD_STATE_FOLDED);
+        setFoldedStates(FOLD_STATE_FOLDED.getIdentifier());
+        setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier());
         mCentralSurfaces.setBarStateForTest(KEYGUARD);
         when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(true);
 
@@ -1059,8 +1045,8 @@
 
     @Test
     public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
-        setFoldedStates(FOLD_STATE_FOLDED);
-        setGoToSleepStates(FOLD_STATE_FOLDED);
+        setFoldedStates(FOLD_STATE_FOLDED.getIdentifier());
+        setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier());
         mCentralSurfaces.setBarStateForTest(SHADE);
         when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(false);
 
@@ -1071,8 +1057,8 @@
 
     @Test
     public void deviceStateChange_unfolded_shadeExpanding_onKeyguard_closesQS() {
-        setFoldedStates(FOLD_STATE_FOLDED);
-        setGoToSleepStates(FOLD_STATE_FOLDED);
+        setFoldedStates(FOLD_STATE_FOLDED.getIdentifier());
+        setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier());
         mCentralSurfaces.setBarStateForTest(KEYGUARD);
         when(mNotificationPanelViewController.isExpandingOrCollapsing()).thenReturn(true);
 
@@ -1084,8 +1070,8 @@
 
     @Test
     public void deviceStateChange_unfolded_shadeExpanded_onKeyguard_closesQS() {
-        setFoldedStates(FOLD_STATE_FOLDED);
-        setGoToSleepStates(FOLD_STATE_FOLDED);
+        setFoldedStates(FOLD_STATE_FOLDED.getIdentifier());
+        setGoToSleepStates(FOLD_STATE_FOLDED.getIdentifier());
         mCentralSurfaces.setBarStateForTest(KEYGUARD);
         when(mNotificationPanelViewController.isShadeFullyExpanded()).thenReturn(true);
 
@@ -1149,6 +1135,7 @@
     }
 
     @Test
+    @DisableFlags(StatusBarSimpleFragment.FLAG_NAME)
     public void bubbleBarVisibility() {
         createCentralSurfaces();
         mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN);
@@ -1401,9 +1388,7 @@
         mCentralSurfaces.updateIsKeyguard(false /* forceStateChange */);
     }
 
-    private void setDeviceState(int state) {
-        DeviceState deviceState = new DeviceState(
-                new DeviceState.Configuration.Builder(state, "TEST").build());
+    private void setDeviceState(DeviceState deviceState) {
         ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor =
                 ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
         verify(mDeviceStateManager).registerCallback(any(), callbackCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
index a3e2d19..2e65478 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -15,12 +15,15 @@
  */
 package com.android.systemui.statusbar.phone
 
-import android.hardware.devicestate.DeviceState
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.foldedDeviceStateList
+import com.android.systemui.halfFoldedDeviceState
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.phone.FoldStateListener.OnFoldStateChangeListener
+import com.android.systemui.unfoldedDeviceState
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,8 +37,7 @@
 @SmallTest
 class FoldStateListenerTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var listener: OnFoldStateChangeListener
+    @Mock private lateinit var listener: OnFoldStateChangeListener
     private lateinit var sut: FoldStateListener
 
     @Before
@@ -111,25 +113,13 @@
     }
 
     private fun setFoldedStates(vararg states: Int) {
-        mContext.orCreateTestableResources.addOverride(
-            R.array.config_foldedDeviceStates,
-            states
-        )
+        mContext.orCreateTestableResources.addOverride(R.array.config_foldedDeviceStates, states)
     }
 
     companion object {
-        private val DEVICE_STATE_FOLDED = DeviceState(
-            DeviceState.Configuration.Builder(123 /* id */, "FOLDED" /* name */)
-                    .build()
-        )
-        private val DEVICE_STATE_HALF_FOLDED = DeviceState(
-            DeviceState.Configuration.Builder(456 /* id */, "HALF_FOLDED" /* name */)
-                    .build()
-        )
-        private val DEVICE_STATE_UNFOLDED = DeviceState(
-            DeviceState.Configuration.Builder(789 /* id */, "UNFOLDED" /* name */)
-                    .build()
-        )
+        private val DEVICE_STATE_FOLDED = Kosmos().foldedDeviceStateList.first()
+        private val DEVICE_STATE_HALF_FOLDED = Kosmos().halfFoldedDeviceState
+        private val DEVICE_STATE_UNFOLDED = Kosmos().unfoldedDeviceState
 
         private const val FOLDED = true
         private const val NOT_FOLDED = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 68df748..ee79ca0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -57,10 +58,15 @@
         get() = view.requireViewById(R.id.system_icons)
 
     private val windowController = mock<StatusBarWindowController>()
+    private val windowControllerStore = mock<StatusBarWindowControllerStore>()
 
     @Before
     fun setUp() {
-        mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
+        whenever(windowControllerStore.defaultDisplay).thenReturn(windowController)
+        mDependency.injectTestDependency(
+            StatusBarWindowControllerStore::class.java,
+            windowControllerStore,
+        )
         context.ensureTestableResources()
         view = spy(createStatusBarView())
         whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e804b33..c639b3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -44,7 +44,6 @@
 import static org.mockito.Mockito.when;
 
 import android.animation.Animator;
-import android.app.AlarmManager;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
@@ -90,7 +89,6 @@
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
 import com.android.systemui.utils.os.FakeHandler;
-import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
 
 import com.google.common.truth.Expect;
 
@@ -139,7 +137,6 @@
     private boolean mAlwaysOnEnabled;
     private TestableLooper mLooper;
     private Context mContext;
-    @Mock private AlarmManager mAlarmManager;
     @Mock private DozeParameters mDozeParameters;
     @Mock private LightBarController mLightBarController;
     @Mock private DelayedWakeLock.Factory mDelayedWakeLockFactory;
@@ -157,7 +154,6 @@
     private final FakeKeyguardTransitionRepository mKeyguardTransitionRepository =
             mKosmos.getKeyguardTransitionRepository();
     @Mock private KeyguardInteractor mKeyguardInteractor;
-    private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
     @Mock private TypedArray mMockTypedArray;
 
     // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
@@ -282,7 +278,6 @@
         mScrimController = new ScrimController(
                 mLightBarController,
                 mDozeParameters,
-                mAlarmManager,
                 mKeyguardStateController,
                 mDelayedWakeLockFactory,
                 new FakeHandler(mLooper.getLooper()),
@@ -298,10 +293,8 @@
                 mAlternateBouncerToGoneTransitionViewModel,
                 mKeyguardTransitionInteractor,
                 mKeyguardInteractor,
-                mWallpaperRepository,
                 mKosmos.getTestDispatcher(),
                 mLinearLargeScreenShadeInterpolator);
-        mScrimController.start();
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
@@ -309,9 +302,6 @@
         // Attach behind scrim so flows that are collecting on it start running.
         ViewUtils.attachView(mScrimBehind);
 
-        mScrimController.setHasBackdrop(false);
-
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
         mTestScope.getTestScheduler().runCurrent();
 
         if (SceneContainerFlag.isEnabled()) {
@@ -438,8 +428,6 @@
                 mScrimInFront, true,
                 mScrimBehind, true
         ));
-
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
     }
 
     @Test
@@ -451,72 +439,6 @@
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, TRANSPARENT,
                 mNotificationsScrim, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
-
-        assertScrimTinted(Map.of(
-                mScrimInFront, true,
-                mScrimBehind, true
-        ));
-    }
-
-    @Test
-    public void transitionToAod_withAodWallpaper() {
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
-        mTestScope.getTestScheduler().runCurrent();
-
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-        finishAnimationsImmediately();
-
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, TRANSPARENT));
-        assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
-
-        // Pulsing notification should conserve AOD wallpaper.
-        mScrimController.legacyTransitionTo(ScrimState.PULSING);
-        finishAnimationsImmediately();
-
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, TRANSPARENT));
-        assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
-    }
-
-    @Test
-    public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
-        mScrimController.setHasBackdrop(true);
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
-        mTestScope.getTestScheduler().runCurrent();
-
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-        finishAnimationsImmediately();
-
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
-
-        assertScrimTinted(Map.of(
-                mScrimInFront, true,
-                mScrimBehind, true
-        ));
-    }
-
-    @Test
-    public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
-        mTestScope.getTestScheduler().runCurrent();
-
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-        finishAnimationsImmediately();
-        mScrimController.setHasBackdrop(true);
-        finishAnimationsImmediately();
-
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
-
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
                 mScrimBehind, true
@@ -540,14 +462,12 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and that if we set it while we're in AOD, it does take immediate effect.
         mScrimController.setAodFrontScrimAlpha(1f);
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and make sure we recall the previous front scrim alpha even if we transition away
         // for a bit.
@@ -557,7 +477,6 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and alpha updates should be completely ignored if always_on is off.
         // Passing it forward would mess up the wake-up transition.
@@ -588,7 +507,6 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... but will take effect after docked
         when(mDockManager.isDocked()).thenReturn(true);
@@ -600,7 +518,6 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and that if we set it while we're in AOD, it does take immediate effect after docked.
         mScrimController.setAodFrontScrimAlpha(1f);
@@ -608,7 +525,6 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // Reset value since enums are static.
         mScrimController.setAodFrontScrimAlpha(0f);
@@ -619,7 +535,6 @@
         // Pre-condition
         // Need to go to AoD first because PULSING doesn't change
         // the back scrim opacity - otherwise it would hide AoD wallpapers.
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
         mTestScope.getTestScheduler().runCurrent();
 
         mScrimController.legacyTransitionTo(ScrimState.AOD);
@@ -627,7 +542,6 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         mScrimController.legacyTransitionTo(ScrimState.PULSING);
         finishAnimationsImmediately();
@@ -637,7 +551,6 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
@@ -651,15 +564,12 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         mScrimController.setWakeLockScreenSensorActive(true);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
-        assertEquals(ScrimController.WAKE_SENSOR_SCRIM_ALPHA,
-                mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // Reset value since enums are static.
         mScrimController.setAodFrontScrimAlpha(0f);
@@ -1333,7 +1243,6 @@
         mScrimController = new ScrimController(
                 mLightBarController,
                 mDozeParameters,
-                mAlarmManager,
                 mKeyguardStateController,
                 mDelayedWakeLockFactory,
                 new FakeHandler(mLooper.getLooper()),
@@ -1349,15 +1258,11 @@
                 mAlternateBouncerToGoneTransitionViewModel,
                 mKeyguardTransitionInteractor,
                 mKeyguardInteractor,
-                mWallpaperRepository,
                 mKosmos.getTestDispatcher(),
                 mLinearLargeScreenShadeInterpolator);
-        mScrimController.start();
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
-        mScrimController.setHasBackdrop(false);
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
         mTestScope.getTestScheduler().runCurrent();
         mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
@@ -1454,57 +1359,6 @@
     }
 
     @Test
-    public void testHoldsAodWallpaperAnimationLock() {
-        // Pre-conditions
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-        finishAnimationsImmediately();
-        reset(mWakeLock);
-
-        mScrimController.onHideWallpaperTimeout();
-        verify(mWakeLock).acquire(anyString());
-        verify(mWakeLock, never()).release(anyString());
-        finishAnimationsImmediately();
-        verify(mWakeLock).release(anyString());
-    }
-
-    @Test
-    public void testHoldsPulsingWallpaperAnimationLock() {
-        // Pre-conditions
-        mScrimController.legacyTransitionTo(ScrimState.PULSING);
-        finishAnimationsImmediately();
-        reset(mWakeLock);
-
-        mScrimController.onHideWallpaperTimeout();
-        verify(mWakeLock).acquire(anyString());
-        verify(mWakeLock, never()).release(anyString());
-        finishAnimationsImmediately();
-        verify(mWakeLock).release(anyString());
-    }
-
-    @Test
-    public void testWillHideAodWallpaper() {
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
-        mTestScope.getTestScheduler().runCurrent();
-
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-        verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
-        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
-        verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
-    }
-
-    @Test
-    public void testWillHideDockedWallpaper() {
-        mAlwaysOnEnabled = false;
-        when(mDockManager.isDocked()).thenReturn(true);
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
-        mTestScope.getTestScheduler().runCurrent();
-
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-
-        verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
-    }
-
-    @Test
     public void testConservesExpansionOpacityAfterTransition() {
         mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setRawPanelExpansionFraction(0.5f);
@@ -1542,43 +1396,6 @@
     }
 
     @Test
-    public void testHidesShowWhenLockedActivity() {
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
-        mTestScope.getTestScheduler().runCurrent();
-
-        mScrimController.setKeyguardOccluded(true);
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-        finishAnimationsImmediately();
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE));
-
-        mScrimController.legacyTransitionTo(ScrimState.PULSING);
-        finishAnimationsImmediately();
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE));
-    }
-
-    @Test
-    public void testHidesShowWhenLockedActivity_whenAlreadyInAod() {
-        mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
-        mTestScope.getTestScheduler().runCurrent();
-
-        mScrimController.legacyTransitionTo(ScrimState.AOD);
-        finishAnimationsImmediately();
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, TRANSPARENT));
-
-        mScrimController.setKeyguardOccluded(true);
-        finishAnimationsImmediately();
-        assertScrimAlpha(Map.of(
-                mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE));
-    }
-
-    @Test
     public void testEatsTouchEvent() {
         HashSet<ScrimState> eatsTouches =
                 new HashSet<>(Collections.singletonList(ScrimState.AOD));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index e57e8d1..d01c1ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,7 +16,6 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS;
 import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
 import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -61,17 +60,18 @@
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
+import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ui.DarkIconManager;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder;
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel;
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewBinder;
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewModel;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
@@ -109,9 +109,9 @@
     private final CarrierConfigTracker mCarrierConfigTracker = mock(CarrierConfigTracker.class);
 
     @Mock
-    private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+    private HomeStatusBarComponent.Factory mStatusBarFragmentComponentFactory;
     @Mock
-    private StatusBarFragmentComponent mStatusBarFragmentComponent;
+    private HomeStatusBarComponent mHomeStatusBarComponent;
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
@@ -122,8 +122,8 @@
     private DarkIconManager.Factory mIconManagerFactory;
     @Mock
     private DarkIconManager mIconManager;
-    private FakeCollapsedStatusBarViewModel mCollapsedStatusBarViewModel;
-    private FakeCollapsedStatusBarViewBinder mCollapsedStatusBarViewBinder;
+    private FakeHomeStatusBarViewModel mCollapsedStatusBarViewModel;
+    private FakeHomeStatusBarViewBinder mCollapsedStatusBarViewBinder;
     @Mock
     private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
     @Mock
@@ -633,7 +633,7 @@
     @Test
     @EnableFlags({
             FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
-            FLAG_STATUS_BAR_RON_CHIPS,
+            StatusBarNotifChips.FLAG_NAME,
             FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() {
         resumeAndGetFragment();
@@ -660,8 +660,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
         resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -673,7 +673,7 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
         resumeAndGetFragment();
@@ -689,8 +689,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -705,9 +705,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() {
+    public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -724,8 +724,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -740,9 +740,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() {
+    public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -759,8 +759,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
         resumeAndGetFragment();
 
         // Ongoing activity started
@@ -781,9 +781,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() {
+    public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
         resumeAndGetFragment();
 
         // Ongoing activity started
@@ -804,7 +804,7 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void secondaryOngoingActivityEnded_chipHidden() {
         resumeAndGetFragment();
@@ -828,8 +828,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
         fragment.enableAnimationsForTesting();
@@ -846,9 +846,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() {
+    public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
         fragment.enableAnimationsForTesting();
@@ -866,8 +866,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
-    public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() {
+    @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN there *is* an ongoing call via old callback
@@ -898,9 +898,9 @@
     }
 
     @Test
-    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
     @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
-    public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() {
+    public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
         // WHEN there *is* an ongoing call via old callback
@@ -1060,7 +1060,7 @@
     public void setUp_fragmentCreatesDaggerComponent() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
-        assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
+        assertEquals(mHomeStatusBarComponent, fragment.getHomeStatusBarComponent());
     }
 
     @Test
@@ -1190,8 +1190,8 @@
         mSecureSettings = mock(SecureSettings.class);
 
         mShadeExpansionStateManager = new ShadeExpansionStateManager();
-        mCollapsedStatusBarViewModel = new FakeCollapsedStatusBarViewModel();
-        mCollapsedStatusBarViewBinder = new FakeCollapsedStatusBarViewBinder();
+        mCollapsedStatusBarViewModel = new FakeHomeStatusBarViewModel();
+        mCollapsedStatusBarViewBinder = new FakeHomeStatusBarViewBinder();
 
         return new CollapsedStatusBarFragment(
                 mStatusBarFragmentComponentFactory,
@@ -1224,8 +1224,8 @@
 
     private void setUpDaggerComponent() {
         when(mStatusBarFragmentComponentFactory.create(any()))
-                .thenReturn(mStatusBarFragmentComponent);
-        when(mStatusBarFragmentComponent.getHeadsUpAppearanceController())
+                .thenReturn(mHomeStatusBarComponent);
+        when(mHomeStatusBarComponent.getHeadsUpAppearanceController())
                 .thenReturn(mHeadsUpAppearanceController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 4b6e313..c48898a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -50,8 +50,8 @@
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
@@ -66,6 +66,7 @@
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -104,6 +105,7 @@
 // to run the callback and this makes the looper place nicely with TestScope etc.
 @TestableLooper.RunWithLooper
 class MobileConnectionsRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private val flags =
         FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
@@ -120,13 +122,13 @@
     @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
     @Mock private lateinit var logger: MobileInputLogger
-    @Mock private lateinit var summaryLogger: TableLogBuffer
+    private val summaryLogger = logcatTableLogBuffer(kosmos, "summaryLogger")
     @Mock private lateinit var logBufferFactory: TableLogBufferFactory
     @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
     @Mock private lateinit var wifiManager: WifiManager
     @Mock private lateinit var wifiPickerTrackerFactory: WifiPickerTrackerFactory
     @Mock private lateinit var wifiPickerTracker: WifiPickerTracker
-    @Mock private lateinit var wifiTableLogBuffer: TableLogBuffer
+    private val wifiTableLogBuffer = logcatTableLogBuffer(kosmos, "wifiTableLog")
 
     private val mobileMappings = FakeMobileMappingsProxy()
     private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
@@ -134,6 +136,7 @@
     private val wifiLogBuffer = LogBuffer("wifi", maxSize = 100, logcatEchoTracker = mock())
     private val wifiPickerTrackerCallback =
         argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>()
+    private val vcnTransportInfo = VcnTransportInfo.Builder().build()
 
     private val testDispatcher = StandardTestDispatcher()
     private val testScope = TestScope(testDispatcher)
@@ -153,7 +156,7 @@
         }
 
         whenever(logBufferFactory.getOrCreate(anyString(), anyInt())).thenAnswer { _ ->
-            mock<TableLogBuffer>()
+            logcatTableLogBuffer(kosmos, "test")
         }
 
         whenever(wifiPickerTrackerFactory.create(any(), capture(wifiPickerTrackerCallback), any()))
@@ -606,10 +609,7 @@
 
             // WHEN an appropriate intent gets sent out
             val intent = serviceStateIntent(subId = -1)
-            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
-                context,
-                intent,
-            )
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
             runCurrent()
 
             // THEN the repo's state is updated despite no listeners
@@ -636,10 +636,7 @@
 
             // GIVEN a broadcast goes out for the appropriate subID
             val intent = serviceStateIntent(subId = -1)
-            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
-                context,
-                intent,
-            )
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
             runCurrent()
 
             // THEN the device is in ECM, because one of the service states is
@@ -666,10 +663,7 @@
 
             // GIVEN a broadcast goes out for the appropriate subID
             val intent = serviceStateIntent(subId = -1)
-            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
-                context,
-                intent,
-            )
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
             runCurrent()
 
             // THEN the device is in ECM, because one of the service states is
@@ -820,17 +814,9 @@
 
             // Get repos to trigger creation
             underTest.getRepoForSubId(SUB_1_ID)
-            verify(logBufferFactory)
-                .getOrCreate(
-                    eq(tableBufferLogName(SUB_1_ID)),
-                    anyInt(),
-                )
+            verify(logBufferFactory).getOrCreate(eq(tableBufferLogName(SUB_1_ID)), anyInt())
             underTest.getRepoForSubId(SUB_2_ID)
-            verify(logBufferFactory)
-                .getOrCreate(
-                    eq(tableBufferLogName(SUB_2_ID)),
-                    anyInt(),
-                )
+            verify(logBufferFactory).getOrCreate(eq(tableBufferLogName(SUB_2_ID)), anyInt())
         }
 
     @Test
@@ -1018,6 +1004,18 @@
             assertThat(latest).isTrue()
         }
 
+    private fun newWifiNetwork(wifiInfo: WifiInfo): Network {
+        val network = mock<Network>()
+        val capabilities =
+            mock<NetworkCapabilities>().also {
+                whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+                whenever(it.transportInfo).thenReturn(wifiInfo)
+            }
+        whenever(connectivityManager.getNetworkCapabilities(network)).thenReturn(capabilities)
+
+        return network
+    }
+
     /** Regression test for b/272586234. */
     @Test
     fun hasCarrierMergedConnection_carrierMergedViaWifiWithVcnTransport_isTrue() =
@@ -1027,10 +1025,12 @@
                     whenever(this.isCarrierMerged).thenReturn(true)
                     whenever(this.isPrimary).thenReturn(true)
                 }
+            val underlyingWifi = newWifiNetwork(carrierMergedInfo)
             val caps =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
                 }
 
             val latest by collectLastValue(underTest.hasCarrierMergedConnection)
@@ -1049,10 +1049,12 @@
                     whenever(this.isCarrierMerged).thenReturn(true)
                     whenever(this.isPrimary).thenReturn(true)
                 }
+            val underlyingWifi = newWifiNetwork(carrierMergedInfo)
             val caps =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
                 }
 
             val latest by collectLastValue(underTest.hasCarrierMergedConnection)
@@ -1109,10 +1111,15 @@
                     whenever(this.isCarrierMerged).thenReturn(true)
                     whenever(this.isPrimary).thenReturn(true)
                 }
+
+            // The Wifi network that is under the VCN network
+            val physicalWifiNetwork = newWifiNetwork(carrierMergedInfo)
+
             val underlyingCapabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(physicalWifiNetwork))
                 }
             whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
                 .thenReturn(underlyingCapabilities)
@@ -1578,9 +1585,7 @@
          * To properly mimic telephony manager, create a service state, and then turn it into an
          * intent
          */
-        private fun serviceStateIntent(
-            subId: Int,
-        ): Intent {
+        private fun serviceStateIntent(subId: Int): Intent {
             return Intent(Intent.ACTION_SERVICE_STATE).apply {
                 putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId)
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
index 0945742..88f262b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -23,6 +23,7 @@
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
 import android.net.NetworkCapabilities.TRANSPORT_VPN
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.TelephonyNetworkSpecifier
 import android.net.VpnTransportInfo
 import android.net.vcn.VcnTransportInfo
 import android.net.wifi.WifiInfo
@@ -74,6 +75,8 @@
     private val testScope = kosmos.testScope
     private val tunerService = mock<TunerService>()
 
+    private val vcnTransportInfo = VcnTransportInfo.Builder().build()
+
     @Before
     fun setUp() {
         createAndSetRepo()
@@ -343,6 +346,30 @@
             assertThat(latest!!.wifi.isDefault).isTrue()
         }
 
+    private fun newWifiNetwork(wifiInfo: WifiInfo): Network {
+        val network = mock<Network>()
+        val capabilities =
+            mock<NetworkCapabilities>().also {
+                whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+                whenever(it.transportInfo).thenReturn(wifiInfo)
+            }
+        whenever(connectivityManager.getNetworkCapabilities(network)).thenReturn(capabilities)
+
+        return network
+    }
+
+    private fun newCellNetwork(subId: Int): Network {
+        val network = mock<Network>()
+        val capabilities =
+            mock<NetworkCapabilities>().also {
+                whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                whenever(it.networkSpecifier).thenReturn(TelephonyNetworkSpecifier(subId))
+            }
+        whenever(connectivityManager.getNetworkCapabilities(network)).thenReturn(capabilities)
+
+        return network
+    }
+
     @Test
     fun defaultConnections_carrierMergedViaWifiWithVcnTransport_wifiAndCarrierMergedDefault() =
         testScope.runTest {
@@ -350,10 +377,12 @@
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
+            val underlyingWifi = newWifiNetwork(carrierMergedInfo)
             val capabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
                 }
@@ -373,10 +402,12 @@
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
+            val underlyingWifi = newWifiNetwork(carrierMergedInfo)
             val capabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
                 }
@@ -561,10 +592,12 @@
             val underlyingCarrierMergedNetwork = mock<Network>()
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
+            val underlyingWifi = newWifiNetwork(carrierMergedInfo)
             val underlyingCapabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
                 }
             whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
                 .thenReturn(underlyingCapabilities)
@@ -645,14 +678,15 @@
     @Test
     fun vcnSubId_tracksVcnTransportInfo() =
         testScope.runTest {
-            val vcnInfo = VcnTransportInfo(SUB_1_ID)
+            val underlyingCell = newCellNetwork(SUB_1_ID)
 
             val latest by collectLastValue(underTest.vcnSubId)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(vcnInfo)
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingCell))
                 }
 
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
@@ -663,14 +697,15 @@
     @Test
     fun vcnSubId_filersOutInvalid() =
         testScope.runTest {
-            val vcnInfo = VcnTransportInfo(INVALID_SUBSCRIPTION_ID)
+            val underlyingCell = newCellNetwork(INVALID_SUBSCRIPTION_ID)
 
             val latest by collectLastValue(underTest.vcnSubId)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(vcnInfo)
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingCell))
                 }
 
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
@@ -703,11 +738,12 @@
             val latest by collectLastValue(underTest.vcnSubId)
 
             val wifiInfo = mock<WifiInfo>()
-            val vcnInfo = VcnTransportInfo(wifiInfo)
+            val underlyingWifi = newWifiNetwork(wifiInfo)
             val capabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(vcnInfo)
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
                 }
 
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
@@ -721,14 +757,15 @@
             val latest by collectLastValue(underTest.vcnSubId)
 
             val wifiInfo = mock<WifiInfo>()
-            val wifiVcnInfo = VcnTransportInfo(wifiInfo)
-            val sub1VcnInfo = VcnTransportInfo(SUB_1_ID)
-            val sub2VcnInfo = VcnTransportInfo(SUB_2_ID)
+            val underlyingWifi = newWifiNetwork(wifiInfo)
+            val underlyingCell1 = newCellNetwork(SUB_1_ID)
+            val underlyingCell2 = newCellNetwork(SUB_2_ID)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
-                    whenever(it.transportInfo).thenReturn(wifiVcnInfo)
+                    whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                    whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
                 }
 
             // WIFI VCN info
@@ -738,14 +775,16 @@
 
             // Cellular VCN info with subId 1
             whenever(capabilities.hasTransport(eq(TRANSPORT_CELLULAR))).thenReturn(true)
-            whenever(capabilities.transportInfo).thenReturn(sub1VcnInfo)
+            whenever(capabilities.transportInfo).thenReturn(vcnTransportInfo)
+            whenever(capabilities.underlyingNetworks).thenReturn(listOf(underlyingCell1))
 
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest).isEqualTo(SUB_1_ID)
 
             // Cellular VCN info with subId 2
-            whenever(capabilities.transportInfo).thenReturn(sub2VcnInfo)
+            whenever(capabilities.transportInfo).thenReturn(vcnTransportInfo)
+            whenever(capabilities.underlyingNetworks).thenReturn(listOf(underlyingCell2))
 
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
@@ -776,11 +815,12 @@
     @Test
     fun getMainOrUnderlyingWifiInfo_vcnWithWifi_hasInfo() {
         val wifiInfo = mock<WifiInfo>()
-        val vcnInfo = VcnTransportInfo(wifiInfo)
+        val underlyingWifi = newWifiNetwork(wifiInfo)
         val capabilities =
             mock<NetworkCapabilities>().also {
                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
-                whenever(it.transportInfo).thenReturn(vcnInfo)
+                whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
             }
 
         val result = capabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
@@ -860,11 +900,15 @@
     fun getMainOrUnderlyingWifiInfo_cellular_underlyingVcnWithWifi_hasInfo() {
         val wifiInfo = mock<WifiInfo>()
         val underlyingNetwork = mock<Network>()
-        val underlyingVcnInfo = VcnTransportInfo(wifiInfo)
+
+        // The Wifi network that is under the VCN network
+        val physicalWifiNetwork = newWifiNetwork(wifiInfo)
+
         val underlyingWifiCapabilities =
             mock<NetworkCapabilities>().also {
                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
-                whenever(it.transportInfo).thenReturn(underlyingVcnInfo)
+                whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                whenever(it.underlyingNetworks).thenReturn(listOf(physicalWifiNetwork))
             }
         whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
             .thenReturn(underlyingWifiCapabilities)
@@ -887,11 +931,15 @@
     @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
     fun getMainOrUnderlyingWifiInfo_notCellular_underlyingVcnWithWifi_noInfo() {
         val underlyingNetwork = mock<Network>()
-        val underlyingVcnInfo = VcnTransportInfo(mock<WifiInfo>())
+
+        // The Wifi network that is under the VCN network
+        val physicalWifiNetwork = newWifiNetwork(mock<WifiInfo>())
+
         val underlyingWifiCapabilities =
             mock<NetworkCapabilities>().also {
                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
-                whenever(it.transportInfo).thenReturn(underlyingVcnInfo)
+                whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                whenever(it.underlyingNetworks).thenReturn(listOf(physicalWifiNetwork))
             }
         whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
             .thenReturn(underlyingWifiCapabilities)
@@ -917,10 +965,15 @@
         val underlyingCarrierMergedNetwork = mock<Network>()
         val carrierMergedInfo =
             mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
+
+        // The Wifi network that is under the VCN network
+        val physicalWifiNetwork = newWifiNetwork(carrierMergedInfo)
+
         val underlyingCapabilities =
             mock<NetworkCapabilities>().also {
                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
-                whenever(it.transportInfo).thenReturn(VcnTransportInfo(carrierMergedInfo))
+                whenever(it.transportInfo).thenReturn(vcnTransportInfo)
+                whenever(it.underlyingNetworks).thenReturn(listOf(physicalWifiNetwork))
             }
         whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
             .thenReturn(underlyingCapabilities)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index fd4b77d..44e1437 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -26,19 +26,20 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.time.fakeSystemClock
 import com.android.wifitrackerlib.HotspotNetworkEntry
 import com.android.wifitrackerlib.HotspotNetworkEntry.DeviceType
 import com.android.wifitrackerlib.MergedCarrierEntry
@@ -67,6 +68,7 @@
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class WifiRepositoryImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     // Using lazy means that the class will only be constructed once it's fetched. Because the
     // repository internally sets some values on construction, we need to set up some test
@@ -84,9 +86,9 @@
         )
     }
 
-    private val executor = FakeExecutor(FakeSystemClock())
+    private val executor = FakeExecutor(kosmos.fakeSystemClock)
     private val logger = LogBuffer("name", maxSize = 100, logcatEchoTracker = mock())
-    private val tableLogger = mock<TableLogBuffer>()
+    private val tableLogger = logcatTableLogBuffer(kosmos, "WifiRepositoryImplTest")
     private val wifiManager =
         mock<WifiManager>().apply { whenever(this.maxSignalLevel).thenReturn(10) }
     private val wifiPickerTrackerFactory = mock<WifiPickerTrackerFactory>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index f6e07d3..3247a1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -16,6 +16,11 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
@@ -24,7 +29,9 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.UserHandle;
@@ -51,14 +58,37 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
 
+    private static final DeviceState DEFAULT_FOLDED_STATE = createDeviceState(0 /* identifier */,
+            "folded", Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY),
+            Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED));
+    private static final DeviceState DEFAULT_HALF_FOLDED_STATE = createDeviceState(
+            2 /* identifier */, "half_folded",
+            Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY),
+            Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN));
+    private static final DeviceState DEFAULT_UNFOLDED_STATE = createDeviceState(1 /* identifier */,
+            "unfolded",
+            Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY),
+            Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN));
+    private static final DeviceState UNKNOWN_DEVICE_STATE = createDeviceState(8 /* identifier */,
+            "unknown", Collections.emptySet(), Collections.emptySet());
+    private static final List<DeviceState> DEVICE_STATE_LIST = List.of(DEFAULT_FOLDED_STATE,
+            DEFAULT_HALF_FOLDED_STATE, DEFAULT_UNFOLDED_STATE);
+
     private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"};
-    private static final int[] DEFAULT_FOLDED_STATES = new int[]{0};
-    private static final int[] DEFAULT_HALF_FOLDED_STATES = new int[]{2};
-    private static final int[] DEFAULT_UNFOLDED_STATES = new int[]{1};
+    private static final int[] DEFAULT_FOLDED_STATE_IDENTIFIERS =
+            new int[]{DEFAULT_FOLDED_STATE.getIdentifier()};
+    private static final int[] DEFAULT_HALF_FOLDED_STATE_IDENTIFIERS =
+            new int[]{DEFAULT_HALF_FOLDED_STATE.getIdentifier()};
+    private static final int[] DEFAULT_UNFOLDED_STATE_IDENTIFIERS =
+            new int[]{DEFAULT_UNFOLDED_STATE.getIdentifier()};
 
     @Mock private DeviceStateManager mDeviceStateManager;
     @Mock private DeviceStateRotationLockSettingControllerLogger mLogger;
@@ -77,10 +107,12 @@
         MockitoAnnotations.initMocks(/* testClass= */ this);
         TestableResources resources = mContext.getOrCreateTestableResources();
         resources.addOverride(R.array.config_perDeviceStateRotationLockDefaults, DEFAULT_SETTINGS);
-        resources.addOverride(R.array.config_foldedDeviceStates, DEFAULT_FOLDED_STATES);
-        resources.addOverride(R.array.config_halfFoldedDeviceStates, DEFAULT_HALF_FOLDED_STATES);
-        resources.addOverride(R.array.config_openDeviceStates, DEFAULT_UNFOLDED_STATES);
-
+        resources.addOverride(R.array.config_foldedDeviceStates, DEFAULT_FOLDED_STATE_IDENTIFIERS);
+        resources.addOverride(R.array.config_halfFoldedDeviceStates,
+                DEFAULT_HALF_FOLDED_STATE_IDENTIFIERS);
+        resources.addOverride(R.array.config_openDeviceStates, DEFAULT_UNFOLDED_STATE_IDENTIFIERS);
+        when(mDeviceStateManager.getSupportedDeviceStates()).thenReturn(DEVICE_STATE_LIST);
+        mContext.addMockSystemService(DeviceStateManager.class, mDeviceStateManager);
         ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor =
                 ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
 
@@ -120,11 +152,11 @@
                 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
         // Settings only exist for state 0 and 1
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE);
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
     }
@@ -135,10 +167,10 @@
                 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED);
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
     }
 
@@ -148,7 +180,7 @@
         mFakeRotationPolicy.setRotationLock(true);
 
         // State 2 -> Ignored -> Fall back to state 1 which is unlocked
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE);
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
     }
@@ -162,7 +194,7 @@
         mFakeRotationPolicy.setRotationLock(false);
 
         // State 2 -> Ignored -> Fall back to state 1 which is locked
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE);
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
     }
@@ -174,7 +206,7 @@
         mSettingsManager.onPersistedSettingsChanged();
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -190,10 +222,10 @@
 
     @Test
     public void whenDeviceStateSwitchedToIgnoredState_useFallbackSetting() {
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
 
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
     }
 
@@ -203,10 +235,10 @@
                 8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
 
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8));
+        mDeviceStateCallback.onDeviceStateChanged(UNKNOWN_DEVICE_STATE);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -226,7 +258,7 @@
                 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(false);
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE);
 
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
@@ -242,7 +274,7 @@
         initializeSettingsWith(
                 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_FOLDED_STATE);
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                 /* rotationLocked= */ false,
@@ -263,7 +295,7 @@
                 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                 2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_HALF_FOLDED_STATE);
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                 /* rotationLocked= */ true,
@@ -284,8 +316,8 @@
                 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                 8, DEVICE_STATE_ROTATION_LOCK_IGNORED);
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
-        mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8));
+        mDeviceStateCallback.onDeviceStateChanged(DEFAULT_UNFOLDED_STATE);
+        mDeviceStateCallback.onDeviceStateChanged(UNKNOWN_DEVICE_STATE);
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                 /* rotationLocked= */ true,
@@ -321,8 +353,13 @@
         mSettingsManager.onPersistedSettingsChanged();
     }
 
-    private DeviceState createDeviceStateForIdentifier(int id) {
-        return new DeviceState(new DeviceState.Configuration.Builder(id, "" /* name */).build());
+    private static DeviceState createDeviceState(int identifier, @NonNull String name,
+            @NonNull Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties,
+            @NonNull Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties) {
+        DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder(
+                identifier, name).setSystemProperties(systemProperties).setPhysicalProperties(
+                physicalProperties).build();
+        return new DeviceState(deviceStateConfiguration);
     }
 
     private static class FakeRotationPolicy implements RotationPolicyWrapper {
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 9cfb0bb..1ceb20a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -46,6 +46,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
@@ -197,6 +198,8 @@
 import com.android.wm.shell.taskview.TaskViewTransitions;
 import com.android.wm.shell.transition.Transitions;
 
+import kotlin.Lazy;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -215,7 +218,6 @@
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
-import kotlin.Lazy;
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
 
@@ -2470,6 +2472,52 @@
         verify(stackView, never()).showOverflow(anyBoolean());
     }
 
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_addBubble() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        mEntryListener.onEntryAdded(mRow);
+
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED));
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_updateBubble() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        mEntryListener.onEntryAdded(mRow);
+        // Mark the notification as updated
+        NotificationEntryHelper.modifyRanking(mRow).setTextChanged(true).build();
+        mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED));
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_dragBubbleToDismiss() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        mEntryListener.onEntryAdded(mRow);
+        mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L);
+
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
+    }
+
     /** Creates a bubble using the userId and package. */
     private Bubble createBubble(int userId, String pkg) {
         final UserHandle userHandle = new UserHandle(userId);
@@ -2655,6 +2703,10 @@
         assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded);
     }
 
+    private Bubble eqBubbleWithKey(String key) {
+        return argThat(b -> b.getKey().equals(key));
+    }
+
     private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener {
 
         int mStateChangeCalls = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS
new file mode 100644
index 0000000..eae8629
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS
@@ -0,0 +1,5 @@
+# Bubbles team
+madym@google.com
+atsjenk@google.com
+liranb@google.com
+mpodolian@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
index f4d281d..e1c6699 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.app.viewcapture
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import org.mockito.kotlin.mock
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+val Kosmos.mockViewCaptureAwareWindowManager by
+    Kosmos.Fixture { mock<ViewCaptureAwareWindowManager>() }
+
+var Kosmos.viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager by
+    Kosmos.Fixture { mockViewCaptureAwareWindowManager }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt
new file mode 100644
index 0000000..9c55820
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/DeviceStateManagerKosmos.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.hardware.devicestate.DeviceState
+import android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+import android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN
+import android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
+import android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE
+import android.hardware.devicestate.DeviceStateManager
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.deviceStateManager by Kosmos.Fixture { mock<DeviceStateManager>() }
+
+val Kosmos.defaultDeviceState by
+    Kosmos.Fixture {
+        DeviceState(DeviceState.Configuration.Builder(0 /* identifier */, "DEFAULT").build())
+    }
+
+val Kosmos.foldedDeviceStateList by
+    Kosmos.Fixture {
+        listOf(
+            DeviceState(
+                DeviceState.Configuration.Builder(0, "FOLDED_0")
+                    .setSystemProperties(
+                        setOf(
+                            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                            PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
+                        )
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+                    )
+                    .build()
+            ),
+            DeviceState(
+                DeviceState.Configuration.Builder(1, "FOLDED_1")
+                    .setSystemProperties(
+                        setOf(
+                            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                            PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
+                        )
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+                    )
+                    .build()
+            ),
+            DeviceState(
+                DeviceState.Configuration.Builder(2, "FOLDED_2")
+                    .setSystemProperties(
+                        setOf(
+                            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                            PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP
+                        )
+                    )
+                    .setPhysicalProperties(
+                        setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+                    )
+                    .build()
+            )
+        )
+    }
+
+val Kosmos.halfFoldedDeviceState by
+    Kosmos.Fixture {
+        DeviceState(
+            DeviceState.Configuration.Builder(3 /* identifier */, "HALF_FOLDED")
+                .setSystemProperties(
+                    setOf(
+                        PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+                        PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE
+                    )
+                )
+                .setPhysicalProperties(
+                    setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)
+                )
+                .build()
+        )
+    }
+
+val Kosmos.unfoldedDeviceState by
+    Kosmos.Fixture {
+        DeviceState(
+            DeviceState.Configuration.Builder(4 /* identifier */, "UNFOLDED")
+                .setSystemProperties(
+                    setOf(
+                        PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+                        PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE
+                    )
+                )
+                .setPhysicalProperties(setOf(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))
+                .build()
+        )
+    }
+
+val Kosmos.rearDisplayDeviceState by
+    Kosmos.Fixture {
+        DeviceState(
+            DeviceState.Configuration.Builder(5 /* identifier */, "REAR_DISPLAY")
+                .setSystemProperties(
+                    setOf(
+                        PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                        PROPERTY_FEATURE_REAR_DISPLAY
+                    )
+                )
+                .build()
+        )
+    }
+
+val Kosmos.unknownDeviceState by
+    Kosmos.Fixture {
+        DeviceState(DeviceState.Configuration.Builder(8 /* identifier */, "UNKNOWN").build())
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelKosmos.kt
new file mode 100644
index 0000000..cac4ff3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.audioSharingButtonViewModel: AudioSharingButtonViewModel by
+    Kosmos.Fixture {
+        AudioSharingButtonViewModel(
+            localBluetoothManager,
+            audioSharingInteractor,
+            bluetoothStateInteractor,
+            deviceItemInteractor,
+        )
+    }
+
+val Kosmos.audioSharingButtonViewModelFactory: AudioSharingButtonViewModel.Factory by
+    Kosmos.Fixture {
+        object : AudioSharingButtonViewModel.Factory {
+            override fun create(): AudioSharingButtonViewModel {
+                return audioSharingButtonViewModel
+            }
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorKosmos.kt
new file mode 100644
index 0000000..8019efc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorKosmos.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import com.android.internal.logging.uiEventLogger
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
+
+val Kosmos.audioSharingDeviceItemActionInteractorImpl: AudioSharingDeviceItemActionInteractorImpl by
+    Kosmos.Fixture {
+        AudioSharingDeviceItemActionInteractorImpl(
+            activityStarter,
+            audioSharingInteractor,
+            dialogTransitionAnimator,
+            localBluetoothManager,
+            testDispatcher,
+            testDispatcher,
+            bluetoothTileDialogLogger,
+            uiEventLogger,
+            audioSharingDialogDelegateFactory,
+            deviceItemActionInteractorImpl,
+        )
+    }
+
+val Kosmos.audioSharingDialogDelegateFactory: AudioSharingDialogDelegate.Factory by
+    Kosmos.Fixture {
+        object : AudioSharingDialogDelegate.Factory {
+            override fun create(
+                cachedBluetoothDevice: CachedBluetoothDevice
+            ): AudioSharingDialogDelegate {
+                return audioSharingDialogDelegate
+            }
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelKosmos.kt
new file mode 100644
index 0000000..b8899de8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModelKosmos.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import android.content.applicationContext
+import com.android.internal.logging.uiEventLogger
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.phone.systemUIDialogDotFactory
+import kotlinx.coroutines.CoroutineScope
+import org.mockito.kotlin.mock
+
+val Kosmos.cachedBluetoothDevice: CachedBluetoothDevice by Kosmos.Fixture { mock {} }
+
+val Kosmos.audioSharingDialogViewModel: AudioSharingDialogViewModel by
+    Kosmos.Fixture {
+        AudioSharingDialogViewModel(
+            deviceItemInteractor,
+            audioSharingInteractor,
+            applicationContext,
+            localBluetoothManager,
+            cachedBluetoothDevice,
+            testScope.backgroundScope,
+            testDispatcher
+        )
+    }
+
+val Kosmos.audioSharingDialogViewModelFactory: AudioSharingDialogViewModel.Factory by
+    Kosmos.Fixture {
+        object : AudioSharingDialogViewModel.Factory {
+            override fun create(
+                cachedBluetoothDevice: CachedBluetoothDevice,
+                coroutineScope: CoroutineScope
+            ): AudioSharingDialogViewModel {
+                return audioSharingDialogViewModel
+            }
+        }
+    }
+
+val Kosmos.audioSharingDialogDelegate: AudioSharingDialogDelegate by
+    Kosmos.Fixture {
+        AudioSharingDialogDelegate(
+            cachedBluetoothDevice,
+            testScope.backgroundScope,
+            audioSharingDialogViewModelFactory,
+            systemUIDialogDotFactory,
+            uiEventLogger
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt
similarity index 68%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt
index fde174d..4f4d1da 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.bluetooth.qsdialog
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 
-val Kosmos.partitionedGridViewModel by
+val Kosmos.audioSharingInteractor: AudioSharingInteractor by
     Kosmos.Fixture {
-        PartitionedGridViewModel(
-            iconTilesViewModel,
-            fixedColumnsSizeViewModel,
-            iconLabelVisibilityViewModel,
+        AudioSharingInteractorImpl(
+            localBluetoothManager,
+            bluetoothTileDialogAudioSharingRepository,
+            testDispatcher,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryKosmos.kt
index 2f5daaa..d15d0e5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.bluetooth.qsdialog
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.bluetoothTileDialogAudioSharingRepository by
+    Kosmos.Fixture { FakeAudioSharingRepository() }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorKosmos.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorKosmos.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorKosmos.kt
similarity index 64%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorKosmos.kt
index fde174d..aaa918c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorKosmos.kt
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.bluetooth.qsdialog
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 
-val Kosmos.partitionedGridViewModel by
+val Kosmos.bluetoothStateInteractor: BluetoothStateInteractor by
     Kosmos.Fixture {
-        PartitionedGridViewModel(
-            iconTilesViewModel,
-            fixedColumnsSizeViewModel,
-            iconLabelVisibilityViewModel,
+        BluetoothStateInteractor(
+            localBluetoothManager,
+            bluetoothTileDialogLogger,
+            testScope.backgroundScope,
+            testDispatcher
         )
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
similarity index 79%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
index 5ff4634..b5b2f5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
@@ -20,8 +20,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.util.mockito.mock
+import org.mockito.kotlin.mock
 
 val Kosmos.bluetoothTileDialogLogger: BluetoothTileDialogLogger by Kosmos.Fixture { mock {} }
 
@@ -29,14 +28,10 @@
 
 val Kosmos.dialogTransitionAnimator: DialogTransitionAnimator by Kosmos.Fixture { mock {} }
 
-val Kosmos.deviceItemActionInteractor: DeviceItemActionInteractor by
+val Kosmos.deviceItemActionInteractorImpl: DeviceItemActionInteractorImpl by
     Kosmos.Fixture {
-        DeviceItemActionInteractor(
-            activityStarter,
-            dialogTransitionAnimator,
-            localBluetoothManager,
+        DeviceItemActionInteractorImpl(
             testDispatcher,
-            bluetoothTileDialogLogger,
             uiEventLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/FakeAudioSharingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/FakeAudioSharingRepository.kt
new file mode 100644
index 0000000..a839f17
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/FakeAudioSharingRepository.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bluetooth.qsdialog
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class FakeAudioSharingRepository : AudioSharingRepository {
+    private var mutableAvailable: Boolean = false
+
+    private val mutableInAudioSharing: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+    private val mutableAudioSourceStateUpdate = MutableSharedFlow<Unit>()
+
+    var sourceAdded: Boolean = false
+        private set
+
+    private var profile: LocalBluetoothLeBroadcast? = null
+
+    override val leAudioBroadcastProfile: LocalBluetoothLeBroadcast?
+        get() = profile
+
+    override val audioSourceStateUpdate: Flow<Unit> = mutableAudioSourceStateUpdate
+
+    override val inAudioSharing: StateFlow<Boolean> = mutableInAudioSharing
+
+    override suspend fun audioSharingAvailable(): Boolean = mutableAvailable
+
+    override suspend fun addSource() {
+        sourceAdded = true
+    }
+
+    override suspend fun setActive(cachedBluetoothDevice: CachedBluetoothDevice) {}
+
+    override suspend fun startAudioSharing() {}
+
+    fun setAudioSharingAvailable(available: Boolean) {
+        mutableAvailable = available
+    }
+
+    fun setInAudioSharing(state: Boolean) {
+        mutableInAudioSharing.value = state
+    }
+
+    fun setLeAudioBroadcastProfile(leAudioBroadcastProfile: LocalBluetoothLeBroadcast?) {
+        profile = leAudioBroadcastProfile
+    }
+
+    fun emitAudioSourceStateUpdate() {
+        mutableAudioSourceStateUpdate.tryEmit(Unit)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index baaf604..703e2d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
@@ -49,8 +49,6 @@
     private val _isAlternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = 0L
-    private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
-    override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
     override val lastShownSecurityMode: MutableStateFlow<KeyguardSecurityModel.SecurityMode> =
         MutableStateFlow(KeyguardSecurityModel.SecurityMode.Invalid)
     override var bouncerDismissActionModel: BouncerDismissActionModel? = null
@@ -63,10 +61,6 @@
         _isAlternateBouncerVisible.value = isVisible
     }
 
-    override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
-        _isAlternateBouncerUIAvailable.value = isAvailable
-    }
-
     override fun setPrimaryShow(isShowing: Boolean) {
         _primaryBouncerShow.value = isShowing
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index 63323b2..8bbb8a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -16,33 +16,23 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
-import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.statusbar.policy.keyguardStateController
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.systemClock
 
 val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by
     Kosmos.Fixture {
         AlternateBouncerInteractor(
-            statusBarStateController = statusBarStateController,
-            keyguardStateController = keyguardStateController,
             bouncerRepository = keyguardBouncerRepository,
             fingerprintPropertyRepository = fingerprintPropertyRepository,
-            biometricSettingsRepository = biometricSettingsRepository,
             systemClock = systemClock,
-            keyguardUpdateMonitor = keyguardUpdateMonitor,
             deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor },
             keyguardInteractor = { keyguardInteractor },
             keyguardTransitionInteractor = { keyguardTransitionInteractor },
@@ -54,21 +44,9 @@
 fun Kosmos.givenCanShowAlternateBouncer() {
     this.givenAlternateBouncerSupported()
     this.keyguardBouncerRepository.setPrimaryShow(false)
-    this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
-    this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
     this.deviceEntryFaceAuthRepository.setLockedOut(false)
-    whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
-    whenever(this.keyguardStateController.isUnlocked).thenReturn(false)
 }
 
 fun Kosmos.givenAlternateBouncerSupported() {
-    if (DeviceEntryUdfpsRefactor.isEnabled) {
-        this.fingerprintPropertyRepository.supportsUdfps()
-    } else {
-        this.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true)
-    }
-}
-
-fun Kosmos.givenCannotShowAlternateBouncer() {
-    this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+    this.fingerprintPropertyRepository.supportsUdfps()
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
index 3087d01..77ec838 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
@@ -23,6 +23,7 @@
 import com.android.internal.util.emergencyAffordanceManager
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.bouncer.data.repository.emergencyServicesRepository
+import com.android.systemui.haptics.msdl.bouncerHapticPlayer
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
@@ -52,5 +53,6 @@
         metricsLogger = metricsLogger,
         dozeLogger = mock(),
         sceneInteractor = { sceneInteractor },
+        bouncerHapticPlayer,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt
index 0e84273..e470e37 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt
@@ -19,14 +19,13 @@
 import com.android.systemui.brightness.data.repository.screenBrightnessRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.log.table.logcatTableLogBuffer
 
 val Kosmos.screenBrightnessInteractor by
     Kosmos.Fixture {
         ScreenBrightnessInteractor(
             screenBrightnessRepository,
             applicationCoroutineScope,
-            mock<TableLogBuffer>(),
+            logcatTableLogBuffer(this, "screenBrightness"),
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
index d208465..6889b8a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.brightness.domain.interactor.brightnessPolicyEnforcementInteractor
 import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor
+import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 
@@ -27,5 +28,6 @@
             screenBrightnessInteractor = screenBrightnessInteractor,
             brightnessPolicyEnforcementInteractor = brightnessPolicyEnforcementInteractor,
             applicationScope = applicationCoroutineScope,
+            hapticsViewModelFactory = sliderHapticsViewModelFactory,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt
index fde174d..ff4ba61 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.display.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 
-val Kosmos.partitionedGridViewModel by
-    Kosmos.Fixture {
-        PartitionedGridViewModel(
-            iconTilesViewModel,
-            fixedColumnsSizeViewModel,
-            iconLabelVisibilityViewModel,
-        )
-    }
+val Kosmos.fakeDisplayScopeRepository by
+    Kosmos.Fixture { FakeDisplayScopeRepository(testDispatcher) }
+
+var Kosmos.displayScopeRepository: DisplayScopeRepository by
+    Kosmos.Fixture { fakeDisplayScopeRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryKosmos.kt
index f4d281d..65b18c1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryKosmos.kt
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.display.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+val Kosmos.fakeDisplayWindowPropertiesRepository by
+    Kosmos.Fixture { FakeDisplayWindowPropertiesRepository() }
+
+var Kosmos.displayWindowPropertiesRepository: DisplayWindowPropertiesRepository by
+    Kosmos.Fixture { fakeDisplayWindowPropertiesRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 6c3cf91..78ea700 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -24,16 +24,12 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import org.mockito.Mockito.`when` as whenever
 
 /** Creates a mock display. */
-fun display(
-    type: Int,
-    flags: Int = 0,
-    id: Int = 0,
-    state: Int? = null,
-): Display {
+fun display(type: Int, flags: Int = 0, id: Int = 0, state: Int? = null): Display {
     return mock {
         whenever(this.displayId).thenReturn(id)
         whenever(this.type).thenReturn(type)
@@ -51,10 +47,25 @@
 @SysUISingleton
 /** Fake [DisplayRepository] implementation for testing. */
 class FakeDisplayRepository @Inject constructor() : DisplayRepository {
-    private val flow = MutableSharedFlow<Set<Display>>(replay = 1)
+    private val flow = MutableStateFlow<Set<Display>>(emptySet())
     private val pendingDisplayFlow =
         MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
-    private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 1)
+    private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
+    private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
+
+    suspend fun addDisplay(displayId: Int, type: Int = Display.TYPE_EXTERNAL) {
+        addDisplay(display(type, id = displayId))
+    }
+
+    suspend fun addDisplay(display: Display) {
+        flow.value += display
+        displayAdditionEventFlow.emit(display)
+    }
+
+    suspend fun removeDisplay(displayId: Int) {
+        flow.value = flow.value.filter { it.displayId != displayId }.toSet()
+        displayRemovalEventFlow.emit(displayId)
+    }
 
     /** Emits [value] as [displayAdditionEvent] flow value. */
     suspend fun emit(value: Display?) = displayAdditionEventFlow.emit(value)
@@ -65,7 +76,7 @@
     /** Emits [value] as [pendingDisplay] flow value. */
     suspend fun emit(value: DisplayRepository.PendingDisplay?) = pendingDisplayFlow.emit(value)
 
-    override val displays: Flow<Set<Display>>
+    override val displays: StateFlow<Set<Display>>
         get() = flow
 
     override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
@@ -78,8 +89,11 @@
     override val displayAdditionEvent: Flow<Display?>
         get() = displayAdditionEventFlow
 
+    override val displayRemovalEvent: Flow<Int> = displayRemovalEventFlow
+
     private val _displayChangeEvent = MutableSharedFlow<Int>(replay = 1)
     override val displayChangeEvent: Flow<Int> = _displayChangeEvent
+
     suspend fun emitDisplayChangeEvent(displayId: Int) = _displayChangeEvent.emit(displayId)
 
     fun setDefaultDisplayOff(defaultDisplayOff: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt
new file mode 100644
index 0000000..3c25924
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+
+class FakeDisplayScopeRepository(private val dispatcher: CoroutineDispatcher) :
+    DisplayScopeRepository {
+
+    private val perDisplayScopes = mutableMapOf<Int, CoroutineScope>()
+
+    override fun scopeForDisplay(displayId: Int): CoroutineScope {
+        return perDisplayScopes.computeIfAbsent(displayId) { CoroutineScope(dispatcher) }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
new file mode 100644
index 0000000..9282f27
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import com.android.systemui.display.shared.model.DisplayWindowProperties
+import com.google.common.collect.HashBasedTable
+import org.mockito.kotlin.mock
+
+class FakeDisplayWindowPropertiesRepository : DisplayWindowPropertiesRepository {
+
+    private val properties = HashBasedTable.create<Int, Int, DisplayWindowProperties>()
+
+    override fun get(displayId: Int, windowType: Int): DisplayWindowProperties {
+        return properties.get(displayId, windowType)
+            ?: DisplayWindowProperties(
+                    displayId = displayId,
+                    windowType = windowType,
+                    context = mock(),
+                    windowManager = mock(),
+                )
+                .also { properties.put(displayId, windowType, it) }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
new file mode 100644
index 0000000..e379726
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.CoroutineScope
+
+class FakePerDisplayStore(
+    backgroundApplicationScope: CoroutineScope,
+    displayRepository: DisplayRepository,
+) : PerDisplayStoreImpl<TestPerDisplayInstance>(backgroundApplicationScope, displayRepository) {
+
+    val removalActions = mutableListOf<TestPerDisplayInstance>()
+
+    override fun createInstanceForDisplay(displayId: Int): TestPerDisplayInstance {
+        return TestPerDisplayInstance(displayId)
+    }
+
+    override val instanceClass = TestPerDisplayInstance::class.java
+
+    override suspend fun onDisplayRemovalAction(instance: TestPerDisplayInstance) {
+        removalActions += instance
+    }
+}
+
+data class TestPerDisplayInstance(val displayId: Int)
+
+val Kosmos.fakePerDisplayStore by
+    Kosmos.Fixture {
+        FakePerDisplayStore(
+            backgroundApplicationScope = applicationCoroutineScope,
+            displayRepository = displayRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
index 2d275f9..3fd2503 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.touchpad.data.repository.touchpadRepository
 import com.android.systemui.user.data.repository.userRepository
 import org.mockito.kotlin.mock
@@ -38,27 +39,13 @@
                     testDispatcher,
                     keyboardRepository,
                     touchpadRepository,
-                    userRepository
-                ),
-            clock = fakeEduClock
-        )
-    }
-
-var Kosmos.mockEduInputManager by Kosmos.Fixture { mock<InputManager>() }
-
-var Kosmos.keyboardTouchpadEduStatsInteractor by
-    Kosmos.Fixture {
-        KeyboardTouchpadEduStatsInteractorImpl(
-            backgroundScope = testScope.backgroundScope,
-            contextualEducationInteractor = contextualEducationInteractor,
-            inputDeviceRepository =
-                UserInputDeviceRepository(
-                    testDispatcher,
-                    keyboardRepository,
-                    touchpadRepository,
-                    userRepository
+                    userRepository,
                 ),
             tutorialSchedulerRepository,
-            fakeEduClock
+            mockOverviewProxyService,
+            clock = fakeEduClock,
         )
     }
+
+var Kosmos.mockOverviewProxyService by Kosmos.Fixture { mock<OverviewProxyService>() }
+var Kosmos.mockEduInputManager by Kosmos.Fixture { mock<InputManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index c0152b26..41402ba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.flags
 
 import android.platform.test.annotations.EnableFlags
-import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
@@ -36,7 +35,6 @@
     FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
     FLAG_PREDICTIVE_BACK_SYSUI,
     FLAG_SCENE_CONTAINER,
-    FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
 )
 @Retention(AnnotationRetention.RUNTIME)
 @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/fragments/FragmentServiceKosmos.kt
similarity index 74%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/fragments/FragmentServiceKosmos.kt
index 2f5daaa..c088685 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/fragments/FragmentServiceKosmos.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.fragments
 
 import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.mockFragmentService by Kosmos.Fixture { mock<FragmentService>() }
+
+var Kosmos.fragmentService by Kosmos.Fixture { mockFragmentService }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt
new file mode 100644
index 0000000..257d758
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.haptics.slider
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.interaction.InteractionSource
+import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
+import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.sliderHapticsViewModelFactory by
+    Kosmos.Fixture {
+        object : SliderHapticsViewModel.Factory {
+            override fun create(
+                interactionSource: InteractionSource,
+                sliderRange: ClosedFloatingPointRange<Float>,
+                orientation: Orientation,
+                sliderHapticFeedbackConfig: SliderHapticFeedbackConfig,
+                sliderTrackerConfig: SeekableSliderTrackerConfig,
+            ): SliderHapticsViewModel =
+                SliderHapticsViewModel(
+                    interactionSource,
+                    sliderRange,
+                    orientation,
+                    sliderHapticFeedbackConfig,
+                    sliderTrackerConfig,
+                    vibratorHelper,
+                    fakeSystemClock,
+                )
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
index 805a710..4181dc3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
@@ -40,6 +40,8 @@
     override val isAnimating: Boolean
         get() = false
 
+    override val maxAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
+
     override fun startRevealAmountAnimator(reveal: Boolean, duration: Long) {
         if (reveal) {
             _revealAmount.value = 1.0f
@@ -50,8 +52,5 @@
         revealAnimatorRequests.add(RevealAnimatorRequest(reveal, duration))
     }
 
-    data class RevealAnimatorRequest(
-        val reveal: Boolean,
-        val duration: Long
-    )
+    data class RevealAnimatorRequest(val reveal: Boolean, val duration: Long)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
index 4ccee6f..2aa2744 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
@@ -22,18 +22,16 @@
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 
 val Kosmos.deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor by
     Kosmos.Fixture {
         DeviceEntrySideFpsOverlayInteractor(
-            applicationScope = testScope.backgroundScope,
             context = applicationContext,
             deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
             sceneInteractor = sceneInteractor,
             primaryBouncerInteractor = primaryBouncerInteractor,
             alternateBouncerInteractor = alternateBouncerInteractor,
-            keyguardUpdateMonitor = keyguardUpdateMonitor
+            keyguardUpdateMonitor = keyguardUpdateMonitor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt
deleted file mode 100644
index 7ebef10..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
-
-var Kosmos.fromDreamingLockscreenHostedTransitionInteractor by
-    Kosmos.Fixture {
-        FromDreamingLockscreenHostedTransitionInteractor(
-            transitionRepository = keyguardTransitionRepository,
-            transitionInteractor = keyguardTransitionInteractor,
-            internalTransitionInteractor = internalKeyguardTransitionInteractor,
-            scope = applicationCoroutineScope,
-            bgDispatcher = testDispatcher,
-            mainDispatcher = testDispatcher,
-            keyguardInteractor = keyguardInteractor,
-            powerInteractor = powerInteractor,
-            keyguardOcclusionInteractor = keyguardOcclusionInteractor,
-        )
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index f97f303..522c387 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.bouncerRepository
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.classifier.falsingCollector
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
@@ -152,6 +153,7 @@
     val wifiInteractor by lazy { kosmos.wifiInteractor }
     val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
     val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor }
+    val alternateBouncerInteractor by lazy { kosmos.alternateBouncerInteractor }
 
     val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
     val scrimController by lazy { kosmos.scrimController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index e4a2a87..b45120e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -37,11 +37,11 @@
     var hasUserActivity: Boolean = false
         private set
 
-    private var hasVibrator: Boolean = true
-
-    private val state = VolumeDialogController.State()
     private val callbacks = CopyOnWriteArraySet<VolumeDialogController.Callbacks>()
 
+    private var hasVibrator: Boolean = true
+    private var state = VolumeDialogController.State()
+
     override fun setActiveStream(stream: Int) {
         // ensure streamState existence for the active stream
         state.states.getOrElse(stream) {
@@ -110,6 +110,11 @@
         hasUserActivity = false
     }
 
+    fun updateState(update: VolumeDialogController.State.() -> Unit) {
+        state = state.copy().apply(update)
+        getState()
+    }
+
     override fun getState() {
         callbacks.sendEvent { it.onStateChanged(state) }
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index cfc31c7..10b073e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.plugins.statusbar
 
 import com.android.internal.logging.uiEventLogger
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.jank.interactionJankMonitor
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
@@ -45,5 +46,6 @@
             { sceneContainerOcclusionInteractor },
             { keyguardClockInteractor },
             { sceneBackInteractor },
+            { alternateBouncerInteractor },
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
index c218ff6..dff5625 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -20,6 +20,7 @@
 import androidx.lifecycle.LifecycleCoroutineScope
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.footerActionsController
 import com.android.systemui.qs.footerActionsViewModelFactory
@@ -30,7 +31,9 @@
 import com.android.systemui.shade.transition.largeScreenShadeInterpolator
 import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
 import com.android.systemui.statusbar.sysuiStatusBarStateController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
+@OptIn(ExperimentalCoroutinesApi::class)
 val Kosmos.qsFragmentComposeViewModelFactory by
     Kosmos.Fixture {
         object : QSFragmentComposeViewModel.Factory {
@@ -45,6 +48,7 @@
                     sysuiStatusBarStateController,
                     deviceEntryInteractor,
                     disableFlagsRepository,
+                    keyguardTransitionInteractor,
                     largeScreenShadeInterpolator,
                     configurationInteractor,
                     largeScreenHeaderHelper,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt
similarity index 75%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt
index 2f5daaa..742b79c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSColumnsRepositoryKosmos.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.qs.panels.data.repository
 
+import android.content.res.mainResources
+import com.android.systemui.common.ui.data.repository.configurationRepository
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+var Kosmos.qsColumnsRepository by
+    Kosmos.Fixture { QSColumnsRepository(mainResources, configurationRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
index 546129f..b4317ad 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
@@ -18,11 +18,11 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
-import com.android.systemui.qs.panels.ui.viewmodel.fixedColumnsSizeViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.qsColumnsViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.tileSquishinessViewModel
 
 val Kosmos.infiniteGridLayout by
     Kosmos.Fixture {
-        InfiniteGridLayout(iconTilesViewModel, fixedColumnsSizeViewModel, tileSquishinessViewModel)
+        InfiniteGridLayout(iconTilesViewModel, qsColumnsViewModel, tileSquishinessViewModel)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt
index f4d281d..47615f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSColumnsInteractorKosmos.kt
@@ -17,7 +17,11 @@
 package com.android.systemui.qs.panels.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.qs.panels.data.repository.qsColumnsRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+val Kosmos.qsColumnsInteractor by
+    Kosmos.Fixture {
+        QSColumnsInteractor(applicationCoroutineScope, qsColumnsRepository, shadeInteractor)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 85e9265..10d8e1e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -24,7 +24,7 @@
     Kosmos.Fixture {
         PaginatedGridViewModel(
             iconTilesViewModel,
-            fixedColumnsSizeViewModel,
+            qsColumnsViewModel,
             iconLabelVisibilityViewModel,
             paginatedGridInteractor,
             applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
similarity index 77%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
index feadc91..16b2f54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModelKosmos.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.panels.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.domain.interactor.fixedColumnsSizeInteractor
+import com.android.systemui.qs.panels.domain.interactor.qsColumnsInteractor
 
-val Kosmos.fixedColumnsSizeViewModel by
-    Kosmos.Fixture { FixedColumnsSizeViewModelImpl(fixedColumnsSizeInteractor) }
+val Kosmos.qsColumnsViewModel by Kosmos.Fixture { QSColumnsSizeViewModelImpl(qsColumnsInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
index babbd50..67d9e0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
@@ -25,7 +25,7 @@
     Kosmos.Fixture {
         QuickQuickSettingsViewModel(
             currentTilesInteractor,
-            fixedColumnsSizeViewModel,
+            qsColumnsViewModel,
             quickQuickSettingsRowInteractor,
             tileSquishinessViewModel,
             iconTilesViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
index f4d281d..e16756b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
@@ -13,11 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.systemui.qs.tiles.impl.hearingdevices
 
-package com.android.systemui.qs.panels.domain.interactor
-
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import com.android.systemui.qs.qsEventLogger
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+val Kosmos.qsHearingDevicesTileConfig by
+    Kosmos.Fixture { QSAccessibilityModule.provideHearingDevicesTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
index ddcc6d6..b9f0c9a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
@@ -37,7 +37,7 @@
 import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.policy.deviceProvisionedController
-import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
@@ -67,7 +67,7 @@
             mock<KeyguardStateController>(),
             statusBarStateController,
             statusBarKeyguardViewManager,
-            mock<StatusBarWindowController>(),
+            mock<StatusBarWindowControllerStore>(),
             deviceProvisionedController,
             mock<NotificationShadeWindowController>(),
             0,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
similarity index 85%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
index c0d65a0..2316a2f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
 
 import android.content.packageManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.commandline.commandRegistry
 import com.android.systemui.util.time.fakeSystemClock
 
-val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by
+val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by
     Kosmos.Fixture {
-        DemoRonChipViewModel(
+        DemoNotifChipViewModel(
             commandRegistry = commandRegistry,
             packageManager = packageManager,
             systemClock = fakeSystemClock,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
index f4d281d..af24c37 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.chips.notification.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+val Kosmos.notifChipsViewModel: NotifChipsViewModel by
+    Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 5382c1c..0300bf4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -20,7 +20,8 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel
 import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel
 import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
 import com.android.systemui.statusbar.chips.statusBarChipsLogger
@@ -33,7 +34,8 @@
             shareToAppChipViewModel = shareToAppChipViewModel,
             castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
             callChipViewModel = callChipViewModel,
-            demoRonChipViewModel = demoRonChipViewModel,
+            notifChipsViewModel = notifChipsViewModel,
+            demoNotifChipViewModel = demoNotifChipViewModel,
             logger = statusBarChipsLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
index edd6604..9fa3abf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
@@ -19,11 +19,18 @@
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import org.mockito.kotlin.mock
 
-class FakeStatusBarInitializer(
-    private val statusBarViewController: PhoneStatusBarViewController,
-    private val statusBarTransitions: PhoneStatusBarTransitions,
-) : StatusBarInitializer {
+class FakeStatusBarInitializer : StatusBarInitializer {
+
+    val statusBarViewController = mock<PhoneStatusBarViewController>()
+    val statusBarTransitions = mock<PhoneStatusBarTransitions>()
+
+    var startedByCoreStartable: Boolean = false
+        private set
+
+    var initializedByCentralSurfaces: Boolean = false
+        private set
 
     override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
         set(value) {
@@ -31,5 +38,11 @@
             value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions)
         }
 
-    override fun initializeStatusBar() {}
+    override fun initializeStatusBar() {
+        initializedByCentralSurfaces = true
+    }
+
+    override fun start() {
+        startedByCoreStartable = true
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
similarity index 64%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
index f4d281d..8c218be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.core
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import com.android.systemui.statusbar.window.StatusBarWindowController
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory {
+
+    override fun create(
+        statusBarWindowController: StatusBarWindowController
+    ): StatusBarInitializer = FakeStatusBarInitializer()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt
new file mode 100644
index 0000000..0c2cba9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import android.view.Display
+
+class FakeStatusBarInitializerStore : StatusBarInitializerStore {
+
+    private val initializers = mutableMapOf<Int, FakeStatusBarInitializer>()
+
+    override val defaultDisplay: FakeStatusBarInitializer
+        get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+    override fun forDisplay(displayId: Int): FakeStatusBarInitializer {
+        return initializers.computeIfAbsent(displayId) { FakeStatusBarInitializer() }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
new file mode 100644
index 0000000..9197dcd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.core
+
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
+import kotlinx.coroutines.CoroutineScope
+import org.mockito.kotlin.mock
+
+class FakeStatusBarOrchestratorFactory : StatusBarOrchestrator.Factory {
+
+    private val createdOrchestrators = mutableMapOf<Int, StatusBarOrchestrator>()
+
+    fun createdOrchestratorForDisplay(displayId: Int): StatusBarOrchestrator? =
+        createdOrchestrators[displayId]
+
+    override fun create(
+        displayId: Int,
+        displayScope: CoroutineScope,
+        statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+        statusBarModeRepository: StatusBarModePerDisplayRepository,
+        statusBarInitializer: StatusBarInitializer,
+        statusBarWindowController: StatusBarWindowController,
+    ): StatusBarOrchestrator =
+        mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
index d103200..303529b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -16,13 +16,31 @@
 
 package com.android.systemui.statusbar.core
 
+import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.phone.phoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.phoneStatusBarViewController
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
 
-val Kosmos.fakeStatusBarInitializer by
-    Kosmos.Fixture {
-        FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions)
-    }
+val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() }
 
 var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer }
+
+val Kosmos.fakeStatusBarInitializerFactory by Kosmos.Fixture { FakeStatusBarInitializerFactory() }
+
+var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by
+    Kosmos.Fixture { fakeStatusBarInitializerFactory }
+
+val Kosmos.multiDisplayStatusBarInitializerStore by
+    Kosmos.Fixture {
+        MultiDisplayStatusBarInitializerStore(
+            applicationCoroutineScope,
+            displayRepository,
+            fakeStatusBarInitializerFactory,
+            fakeStatusBarWindowControllerStore,
+        )
+    }
+
+val Kosmos.fakeStatusBarInitializerStore by Kosmos.Fixture { FakeStatusBarInitializerStore() }
+
+var Kosmos.statusBarInitializerStore: StatusBarInitializerStore by
+    Kosmos.Fixture { fakeStatusBarInitializerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index c53e44d..87f7142 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -16,7 +16,11 @@
 
 package com.android.systemui.statusbar.core
 
+import android.content.testableContext
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayScopeRepository
+import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.mockDemoModeController
@@ -24,20 +28,25 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.shade.mockNotificationShadeWindowViewController
 import com.android.systemui.shade.mockShadeSurface
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.data.repository.statusBarModeRepository
 import com.android.systemui.statusbar.mockNotificationRemoteInputManager
 import com.android.systemui.statusbar.phone.mockAutoHideController
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
 import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
 import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.statusbar.window.statusBarWindowControllerStore
 import com.android.wm.shell.bubbles.bubblesOptional
 
 val Kosmos.statusBarOrchestrator by
     Kosmos.Fixture {
         StatusBarOrchestrator(
+            testableContext.displayId,
             applicationCoroutineScope,
+            fakeStatusBarWindowStatePerDisplayRepository,
+            fakeStatusBarModePerDisplayRepository,
             fakeStatusBarInitializer,
             fakeStatusBarWindowController,
-            fakeStatusBarModeRepository,
             mockDemoModeController,
             mockPluginDependencyProvider,
             mockAutoHideController,
@@ -45,8 +54,28 @@
             { mockNotificationShadeWindowViewController },
             mockShadeSurface,
             bubblesOptional,
-            statusBarWindowStateRepositoryStore,
+            dumpManager,
             powerInteractor,
             primaryBouncerInteractor,
         )
     }
+
+val Kosmos.fakeStatusBarOrchestratorFactory by Kosmos.Fixture { FakeStatusBarOrchestratorFactory() }
+
+var Kosmos.statusBarOrchestratorFactory: StatusBarOrchestrator.Factory by
+    Kosmos.Fixture { fakeStatusBarOrchestratorFactory }
+
+val Kosmos.multiDisplayStatusBarStarter by
+    Kosmos.Fixture {
+        MultiDisplayStatusBarStarter(
+            applicationCoroutineScope,
+            displayScopeRepository,
+            statusBarOrchestratorFactory,
+            statusBarWindowStateRepositoryStore,
+            statusBarModeRepository,
+            displayRepository,
+            statusBarInitializerStore,
+            statusBarWindowControllerStore,
+            statusBarInitializerStore,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 6069083..285cebb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.data.model.StatusBarAppearance
 import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.google.common.truth.Truth.assertThat
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
@@ -37,7 +36,6 @@
         FakeStatusBarModePerDisplayRepository()
 
     override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository {
-        assertThat(displayId).isEqualTo(DISPLAY_ID)
         return defaultDisplay
     }
 }
@@ -51,6 +49,7 @@
     override fun showTransient() {
         isTransientShown.value = true
     }
+
     override fun clearTransient() {
         isTransientShown.value = false
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
index 0f2b477..12db2f741 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
@@ -18,6 +18,9 @@
 
 import com.android.systemui.kosmos.Kosmos
 
+val Kosmos.fakeStatusBarModePerDisplayRepository by
+    Kosmos.Fixture { FakeStatusBarModePerDisplayRepository() }
+
 val Kosmos.statusBarModeRepository: StatusBarModeRepositoryStore by
     Kosmos.Fixture { fakeStatusBarModeRepository }
 val Kosmos.fakeStatusBarModeRepository by Kosmos.Fixture { FakeStatusBarModeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index fc4f05d..7f4c670 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -69,6 +69,9 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderImpl
+import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
 import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
 import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -99,7 +102,7 @@
 class ExpandableNotificationRowBuilder(
     private val context: Context,
     dependency: TestableDependency,
-    private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
+    private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
 ) {
 
     private val mMockLogger: ExpandableNotificationRowLogger
@@ -161,21 +164,21 @@
                         DeviceConfig.NAMESPACE_SYSTEMUI,
                         SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
                         "true",
-                        true
+                        true,
                     )
                     setProperty(
                         DeviceConfig.NAMESPACE_SYSTEMUI,
                         SystemUiDeviceConfigFlags.SSIN_ENABLED,
                         "true",
-                        true
+                        true,
                     )
                     setProperty(
                         DeviceConfig.NAMESPACE_SYSTEMUI,
                         SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
                         "false",
-                        true
+                        true,
                     )
-                }
+                },
             )
         val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags)
         val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY)
@@ -192,21 +195,21 @@
                             Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY),
                         remoteInputManager = remoteInputManager,
                         smartReplyController = mSmartReplyController,
-                        context = context
+                        context = context,
                     ),
                 smartActionsInflater =
                     SmartActionInflaterImpl(
                         constants = mSmartReplyConstants,
                         activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY),
                         smartReplyController = mSmartReplyController,
-                        headsUpManager = mHeadsUpManager
-                    )
+                        headsUpManager = mHeadsUpManager,
+                    ),
             )
         val notifLayoutInflaterFactoryProvider =
             object : NotifLayoutInflaterFactory.Provider {
                 override fun provide(
                     row: ExpandableNotificationRow,
-                    layoutType: Int
+                    layoutType: Int,
                 ): NotifLayoutInflaterFactory =
                     NotifLayoutInflaterFactory(row, layoutType, remoteViewsFactories)
             }
@@ -270,14 +273,14 @@
         whenever(
                 mOnUserInteractionCallback.registerFutureDismissal(
                     ArgumentMatchers.any(),
-                    ArgumentMatchers.anyInt()
+                    ArgumentMatchers.anyInt(),
                 )
             )
             .thenReturn(mFutureDismissalRunnable)
     }
 
     private fun getNotifRemoteViewsFactoryContainer(
-        featureFlags: FeatureFlags,
+        featureFlags: FeatureFlags
     ): NotifRemoteViewsFactoryContainer {
         return NotifRemoteViewsFactoryContainerImpl(
             featureFlags,
@@ -285,6 +288,10 @@
             BigPictureLayoutInflaterFactory(),
             NotificationOptimizedLinearLayoutFactory(),
             { Mockito.mock(NotificationViewFlipperFactory::class.java) },
+            NotificationRowIconViewInflaterFactory(
+                AppIconProviderImpl(context),
+                NotificationIconStyleProviderImpl(),
+            ),
         )
     }
 
@@ -293,7 +300,7 @@
             NotificationChannel(
                 notification.channelId,
                 notification.channelId,
-                NotificationManager.IMPORTANCE_DEFAULT
+                NotificationManager.IMPORTANCE_DEFAULT,
             )
         channel.isBlockable = true
         val entry =
@@ -321,7 +328,7 @@
 
     private fun generateRow(
         entry: NotificationEntry,
-        @InflationFlag extraInflationFlags: Int
+        @InflationFlag extraInflationFlags: Int,
     ): ExpandableNotificationRow {
         // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
         //  set, but we do not want to override an existing value that is needed by a specific test.
@@ -329,7 +336,7 @@
         val rowInflaterTask =
             RowInflaterTask(
                 mFakeSystemClock,
-                Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY)
+                Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY),
             )
         val row = rowInflaterTask.inflateSynchronously(context, null, entry)
 
@@ -364,7 +371,7 @@
             mSmartReplyController,
             featureFlags,
             Mockito.mock(IStatusBarService::class.java, STUB_ONLY),
-            Mockito.mock(UiEventLogger::class.java, STUB_ONLY)
+            Mockito.mock(UiEventLogger::class.java, STUB_ONLY),
         )
         row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
similarity index 77%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
index 2f5daaa..08c6bba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.statusbar.notification.row.icon
 
+import android.content.applicationContext
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
index 2f5daaa..611c90a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.statusbar.notification.row.icon
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 237f7e4..a25a3c0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -49,6 +49,7 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
@@ -92,6 +93,7 @@
             primaryBouncerToLockscreenTransitionViewModel,
         aodBurnInViewModel = aodBurnInViewModel,
         communalSceneInteractor = communalSceneInteractor,
+        headsUpNotificationInteractor = { headsUpNotificationInteractor },
         unfoldTransitionInteractor = unfoldTransitionInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt
index f4d281d..9c9673c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.phone
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import org.mockito.kotlin.mock
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+val Kosmos.mockStatusBarContentInsetsProvider by
+    Kosmos.Fixture { mock<StatusBarContentInsetsProvider>() }
+
+var Kosmos.statusBarContentInsetsProvider by Kosmos.Fixture { mockStatusBarContentInsetsProvider }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
similarity index 90%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index 1c7fd48..3a7ada2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -27,13 +28,14 @@
 import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor
 import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor
 
-val Kosmos.collapsedStatusBarViewModel: CollapsedStatusBarViewModel by
+val Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by
     Kosmos.Fixture {
-        CollapsedStatusBarViewModelImpl(
+        HomeStatusBarViewModelImpl(
             collapsedStatusBarInteractor,
             lightsOutInteractor,
             activeNotificationsInteractor,
             keyguardTransitionInteractor,
+            keyguardInteractor,
             sceneInteractor,
             sceneContainerOcclusionInteractor,
             shadeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
similarity index 82%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
index 2f5daaa..14f4d75 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
@@ -13,9 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.statusbar.policy
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.fakeBluetoothController by Kosmos.Fixture { FakeBluetoothController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt
index d4e9bfb..282f594 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt
@@ -17,8 +17,11 @@
 package com.android.systemui.statusbar.policy
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
 
 var Kosmos.configurationController: ConfigurationController by
     Kosmos.Fixture { fakeConfigurationController }
 val Kosmos.fakeConfigurationController: FakeConfigurationController by
     Kosmos.Fixture { FakeConfigurationController() }
+val Kosmos.statusBarConfigurationController: StatusBarConfigurationController by
+    Kosmos.Fixture { fakeConfigurationController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
new file mode 100644
index 0000000..4876cd8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.policy
+
+import android.bluetooth.BluetoothAdapter
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.statusbar.policy.BluetoothController.Callback
+import java.io.PrintWriter
+import java.util.Collections
+import java.util.concurrent.Executor
+
+class FakeBluetoothController : BluetoothController {
+
+    private var callbacks = mutableListOf<Callback>()
+    private var enabled = false
+
+    override fun addCallback(listener: Callback) {
+        callbacks += listener
+        listener.onBluetoothStateChange(isBluetoothEnabled)
+    }
+
+    override fun removeCallback(listener: Callback) {
+        callbacks -= listener
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {}
+
+    override fun isBluetoothSupported(): Boolean = false
+
+    override fun isBluetoothEnabled(): Boolean = enabled
+
+    override fun getBluetoothState(): Int = 0
+
+    override fun isBluetoothConnected(): Boolean = false
+
+    override fun isBluetoothConnecting(): Boolean = false
+
+    override fun isBluetoothAudioProfileOnly(): Boolean = false
+
+    override fun isBluetoothAudioActive(): Boolean = false
+
+    override fun getConnectedDeviceName(): String? = null
+
+    override fun setBluetoothEnabled(enabled: Boolean) {
+        this.enabled = enabled
+        callbacks.forEach { it.onBluetoothStateChange(enabled) }
+    }
+
+    override fun canConfigBluetooth(): Boolean = false
+
+    override fun getConnectedDevices(): MutableList<CachedBluetoothDevice> = Collections.emptyList()
+
+    override fun addOnMetadataChangedListener(
+        device: CachedBluetoothDevice?,
+        executor: Executor?,
+        listener: BluetoothAdapter.OnMetadataChangedListener?,
+    ) {}
+
+    override fun removeOnMetadataChangedListener(
+        device: CachedBluetoothDevice?,
+        listener: BluetoothAdapter.OnMetadataChangedListener?,
+    ) {}
+
+    /** Trigger the [Callback.onBluetoothDevicesChanged] method for all registered callbacks. */
+    @VisibleForTesting
+    fun onBluetoothDevicesChanged() {
+        callbacks.forEach { it.onBluetoothDevicesChanged() }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index 46a1053..6be13be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -2,13 +2,15 @@
 
 import android.content.res.Configuration
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
 
 /** Fake implementation of [ConfigurationController] for tests. */
 @SysUISingleton
-class FakeConfigurationController @Inject constructor() : ConfigurationController {
+class FakeConfigurationController @Inject constructor() :
+    ConfigurationController, StatusBarConfigurationController {
 
     private var listeners = mutableListOf<ConfigurationController.ConfigurationListener>()
     private var isRtl = false
@@ -43,6 +45,7 @@
     }
 
     override fun isLayoutRtl(): Boolean = isRtl
+
     override fun getNightModeName(): String = "undefined"
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
index 99cd830..68d08e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy.domain.interactor
 
-import android.content.testableContext
+import android.content.applicationContext
 import com.android.settingslib.notification.modes.zenIconLoader
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,7 +28,7 @@
 
 val Kosmos.zenModeInteractor by Fixture {
     ZenModeInteractor(
-        context = testableContext,
+        context = applicationContext,
         zenModeRepository = zenModeRepository,
         notificationSettingsRepository = notificationSettingsRepository,
         bgDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
new file mode 100644
index 0000000..bca13c6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.content.Context
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
+
+class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory {
+    override fun create(
+        context: Context,
+        viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+        statusBarConfigurationController: StatusBarConfigurationController
+    ) = FakeStatusBarWindowController()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt
new file mode 100644
index 0000000..35f95b6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window
+
+import android.view.Display
+
+class FakeStatusBarWindowControllerStore : StatusBarWindowControllerStore {
+
+    private val perDisplayControllers = mutableMapOf<Int, FakeStatusBarWindowController>()
+
+    override val defaultDisplay: FakeStatusBarWindowController
+        get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+    override fun forDisplay(displayId: Int): FakeStatusBarWindowController {
+        return perDisplayControllers.computeIfAbsent(displayId) { FakeStatusBarWindowController() }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowViewInflater.kt
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowViewInflater.kt
index fde174d..138b442 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowViewInflater.kt
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.ui.viewmodel
+package com.android.systemui.statusbar.window
 
-import com.android.systemui.kosmos.Kosmos
+import android.content.Context
+import org.mockito.kotlin.mock
 
-val Kosmos.partitionedGridViewModel by
-    Kosmos.Fixture {
-        PartitionedGridViewModel(
-            iconTilesViewModel,
-            fixedColumnsSizeViewModel,
-            iconLabelVisibilityViewModel,
-        )
+class FakeStatusBarWindowViewInflater : StatusBarWindowViewInflater {
+
+    val inflatedMockViews = mutableListOf<StatusBarWindowView>()
+
+    override fun inflate(context: Context): StatusBarWindowView {
+        return mock<StatusBarWindowView>().also { inflatedMockViews += it }
     }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
index c198b35..173e909 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -16,8 +16,42 @@
 
 package com.android.systemui.statusbar.window
 
+import android.content.testableContext
+import android.view.windowManagerService
+import com.android.app.viewcapture.viewCaptureAwareWindowManager
+import com.android.systemui.fragments.fragmentService
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.statusBarContentInsetsProvider
+import com.android.systemui.statusbar.policy.statusBarConfigurationController
+import java.util.Optional
 
 val Kosmos.fakeStatusBarWindowController by Kosmos.Fixture { FakeStatusBarWindowController() }
 
-var Kosmos.statusBarWindowController by Kosmos.Fixture { fakeStatusBarWindowController }
+val Kosmos.statusBarWindowControllerImpl by
+    Kosmos.Fixture {
+        StatusBarWindowControllerImpl(
+            testableContext,
+            statusBarWindowViewInflater,
+            viewCaptureAwareWindowManager,
+            statusBarConfigurationController,
+            windowManagerService,
+            statusBarContentInsetsProvider,
+            fragmentService,
+            Optional.empty(),
+        )
+    }
+
+var Kosmos.statusBarWindowController: StatusBarWindowController by
+    Kosmos.Fixture { fakeStatusBarWindowController }
+
+val Kosmos.fakeStatusBarWindowControllerStore by
+    Kosmos.Fixture { FakeStatusBarWindowControllerStore() }
+
+var Kosmos.statusBarWindowControllerStore: StatusBarWindowControllerStore by
+    Kosmos.Fixture { fakeStatusBarWindowControllerStore }
+
+val Kosmos.fakeStatusBarWindowControllerFactory by
+    Kosmos.Fixture { FakeStatusBarWindowControllerFactory() }
+
+var Kosmos.statusBarWindowControllerFactory: StatusBarWindowController.Factory by
+    Kosmos.Fixture { fakeStatusBarWindowControllerFactory }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowViewKosmos.kt
similarity index 70%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowViewKosmos.kt
index f4d281d..e7cf83f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowViewKosmos.kt
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.window
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+val Kosmos.fakeStatusBarWindowViewInflater by Kosmos.Fixture { FakeStatusBarWindowViewInflater() }
+
+var Kosmos.statusBarWindowViewInflater: StatusBarWindowViewInflater by
+    Kosmos.Fixture { fakeStatusBarWindowViewInflater }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
index 2205a3b..cbaf2bd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
@@ -21,6 +21,9 @@
 import com.android.systemui.settings.displayTracker
 import com.android.systemui.statusbar.commandQueue
 
+val Kosmos.fakeStatusBarWindowStatePerDisplayRepository by
+    Kosmos.Fixture { FakeStatusBarWindowStatePerDisplayRepository() }
+
 val Kosmos.fakeStatusBarWindowStateRepositoryStore by
     Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioSystemRepositoryKosmos.kt
similarity index 82%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioSystemRepositoryKosmos.kt
index 2f5daaa..ec2bf24 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/AudioSystemRepositoryKosmos.kt
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.volume.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.audioSystemRepository by Kosmos.Fixture { FakeAudioSystemRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt
index a4719e5..5da6ee9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSharingRepository.kt
@@ -22,6 +22,7 @@
 import kotlinx.coroutines.flow.StateFlow
 
 class FakeAudioSharingRepository : AudioSharingRepository {
+    private var mutableAvailable: Boolean = false
     private val mutableInAudioSharing: MutableStateFlow<Boolean> = MutableStateFlow(false)
     private val mutablePrimaryGroupId: MutableStateFlow<Int> =
         MutableStateFlow(TEST_GROUP_ID_INVALID)
@@ -34,8 +35,14 @@
     override val secondaryGroupId: StateFlow<Int> = mutableSecondaryGroupId
     override val volumeMap: StateFlow<GroupIdToVolumes> = mutableVolumeMap
 
+    override suspend fun audioSharingAvailable(): Boolean = mutableAvailable
+
     override suspend fun setSecondaryVolume(volume: Int) {}
 
+    fun setAudioSharingAvailable(available: Boolean) {
+        mutableAvailable = available
+    }
+
     fun setInAudioSharing(state: Boolean) {
         mutableInAudioSharing.value = state
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSystemRepository.kt
similarity index 65%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSystemRepository.kt
index f4d281d..0ff4dec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioSystemRepository.kt
@@ -14,10 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.volume.data.repository
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import com.android.settingslib.volume.data.repository.AudioSystemRepository
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+class FakeAudioSystemRepository : AudioSystemRepository {
+
+    override var isSingleVolume = false
+
+    fun setIsSingleVolume(singleVolume: Boolean) {
+        isSingleVolume = singleVolume
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepositoryKosmos.kt
similarity index 78%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepositoryKosmos.kt
index 2f5daaa..7681111 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.volume.dialog.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.volumeDialogStateRepository: VolumeDialogStateRepository by
+    Kosmos.Fixture { VolumeDialogStateRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogVisibilityRepositoryKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogVisibilityRepositoryKosmos.kt
index 2f5daaa..291dfc0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogVisibilityRepositoryKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.volume.dialog.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.dialog.data.VolumeDialogVisibilityRepository
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.volumeDialogVisibilityRepository by Kosmos.Fixture { VolumeDialogVisibilityRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractorKosmos.kt
new file mode 100644
index 0000000..8944861
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractorKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.plugins.volumeDialogController
+import com.android.systemui.volume.dialog.data.repository.volumeDialogStateRepository
+
+val Kosmos.volumeDialogStateInteractor: VolumeDialogStateInteractor by
+    Kosmos.Fixture {
+        VolumeDialogStateInteractor(
+            volumeDialogCallbacksInteractor,
+            volumeDialogController,
+            volumeDialogStateRepository,
+            applicationCoroutineScope,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
index e73539e..7376c7f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorKosmos.kt
@@ -18,8 +18,15 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
+import com.android.systemui.volume.dialog.utils.volumeTracer
 
 val Kosmos.volumeDialogVisibilityInteractor by
     Kosmos.Fixture {
-        VolumeDialogVisibilityInteractor(applicationCoroutineScope, volumeDialogCallbacksInteractor)
+        VolumeDialogVisibilityInteractor(
+            applicationCoroutineScope,
+            volumeDialogCallbacksInteractor,
+            volumeTracer,
+            volumeDialogVisibilityRepository,
+        )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorKosmos.kt
index c0d65a0..0f573d9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorKosmos.kt
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.volume.dialog.sliders.domain.interactor
 
 import android.content.packageManager
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.commandline.commandRegistry
-import com.android.systemui.util.time.fakeSystemClock
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
 
-val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by
+val Kosmos.volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor by
     Kosmos.Fixture {
-        DemoRonChipViewModel(
-            commandRegistry = commandRegistry,
-            packageManager = packageManager,
-            systemClock = fakeSystemClock,
+        VolumeDialogSlidersInteractor(
+            volumeDialogStateInteractor,
+            packageManager,
+            applicationCoroutineScope,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/utils/FakeVolumeTracer.kt
similarity index 65%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/utils/FakeVolumeTracer.kt
index f4d281d..a5074eb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/utils/FakeVolumeTracer.kt
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.volume.dialog.utils
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+class FakeVolumeTracer : VolumeTracer {
+
+    override fun traceVisibilityStart(model: VolumeDialogVisibilityModel) {}
+
+    override fun traceVisibilityEnd(model: VolumeDialogVisibilityModel) {}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/utils/VolumeTracerKosmos.kt
similarity index 75%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/utils/VolumeTracerKosmos.kt
index 2f5daaa..1382563 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/utils/VolumeTracerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.volume.dialog.utils
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+val Kosmos.fakeVolumeTracer: FakeVolumeTracer by Kosmos.Fixture { FakeVolumeTracer() }
+var Kosmos.volumeTracer: VolumeTracer by Kosmos.Fixture { fakeVolumeTracer }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
index 63a1325..db66c3e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
 
+import android.content.mockedContext
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.volume.domain.interactor.audioModeInteractor
@@ -27,6 +28,7 @@
 val Kosmos.mediaOutputComponentInteractor by
     Kosmos.Fixture {
         MediaOutputComponentInteractor(
+            mockedContext,
             testScope.backgroundScope,
             mediaDeviceSessionInteractor,
             audioOutputInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt
index dd5bbf3..3bc920e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.volume.data.repository.audioSystemRepository
 import com.android.systemui.volume.domain.interactor.audioModeInteractor
 import com.android.systemui.volume.mediaOutputInteractor
 
@@ -27,5 +28,6 @@
             applicationCoroutineScope,
             mediaOutputInteractor,
             audioModeInteractor,
+            audioSystemRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
index e6b52f0..55f0a28 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
@@ -19,6 +19,7 @@
 import android.content.applicationContext
 import com.android.internal.logging.uiEventLogger
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
 import com.android.systemui.volume.shared.volumePanelLogger
 import kotlinx.coroutines.CoroutineScope
@@ -36,6 +37,7 @@
                     coroutineScope,
                     applicationContext,
                     audioVolumeInteractor,
+                    zenModeInteractor,
                     uiEventLogger,
                     volumePanelLogger,
                 )
diff --git a/packages/Vcn/OWNERS b/packages/Vcn/OWNERS
new file mode 100644
index 0000000..ff2146e
--- /dev/null
+++ b/packages/Vcn/OWNERS
@@ -0,0 +1,6 @@
+evitayan@google.com
+nharold@google.com
+benedictwong@google.com #{LAST_RESORT_SUGGESTION}
+yangji@google.com #{LAST_RESORT_SUGGESTION}
+
+include platform/packages/modules/common:/MODULES_OWNERS
\ No newline at end of file
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 11b66fc..d918201 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -8,7 +8,15 @@
 
     // OWNER: g/ravenwood
     // Bug component: 25698
-    default_team: "trendy_team_framework_backstage_power",
+    default_team: "trendy_team_ravenwood",
+}
+
+filegroup {
+    name: "ravenwood-common-policies",
+    srcs: [
+        "texts/ravenwood-common-policies.txt",
+    ],
+    visibility: ["//visibility:private"],
 }
 
 filegroup {
@@ -16,7 +24,7 @@
     srcs: [
         "texts/ravenwood-services-policies.txt",
     ],
-    visibility: ["//visibility:public"],
+    visibility: ["//visibility:private"],
 }
 
 filegroup {
@@ -24,7 +32,7 @@
     srcs: [
         "texts/ravenwood-framework-policies.txt",
     ],
-    visibility: ["//visibility:public"],
+    visibility: ["//visibility:private"],
 }
 
 filegroup {
@@ -32,7 +40,7 @@
     srcs: [
         "texts/ravenwood-standard-options.txt",
     ],
-    visibility: ["//visibility:public"],
+    visibility: ["//visibility:private"],
 }
 
 filegroup {
@@ -40,7 +48,7 @@
     srcs: [
         "texts/ravenwood-annotation-allowed-classes.txt",
     ],
-    visibility: ["//visibility:public"],
+    visibility: ["//visibility:private"],
 }
 
 // This and the next module contain the same classes with different implementations.
@@ -116,6 +124,7 @@
     ],
     libs: [
         "framework-minus-apex.ravenwood",
+        "framework-configinfrastructure.ravenwood",
         "ravenwood-helper-libcore-runtime",
     ],
     sdk_version: "core_current",
@@ -154,6 +163,8 @@
         "framework-annotations-lib",
         "ravenwood-helper-framework-runtime",
         "ravenwood-helper-libcore-runtime",
+        "hoststubgen-helper-runtime.ravenwood",
+        "mockito-ravenwood-prebuilt",
     ],
     visibility: ["//frameworks/base"],
     jarjar_rules: ":ravenwood-services-jarjar-rules",
@@ -335,9 +346,34 @@
     ],
 }
 
+// JARs in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
+// Rename some of the dependencies to make sure they're included in the intended order.
+
+java_library {
+    name: "100-framework-minus-apex.ravenwood",
+    installable: false,
+    static_libs: ["framework-minus-apex.ravenwood"],
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "200-kxml2-android",
+    installable: false,
+    static_libs: ["kxml2-android"],
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "z00-all-updatable-modules-system-stubs",
+    installable: false,
+    static_libs: ["all-updatable-modules-system-stubs-for-host"],
+    visibility: ["//visibility:private"],
+}
+
 android_ravenwood_libgroup {
     name: "ravenwood-runtime",
     data: [
+        ":system-build.prop",
         ":framework-res",
         ":ravenwood-empty-res",
         ":framework-platform-compat-config",
@@ -360,6 +396,9 @@
         "icu4j-icudata-jarjar",
         "icu4j-icutzdata-jarjar",
 
+        // DeviceConfig
+        "framework-configinfrastructure.ravenwood",
+
         // Provide runtime versions of utils linked in below
         "junit",
         "truth",
@@ -393,3 +432,7 @@
         "inline-mockito-ravenwood-prebuilt",
     ],
 }
+
+build = [
+    "Framework.bp",
+]
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
new file mode 100644
index 0000000..1bea434
--- /dev/null
+++ b/ravenwood/Framework.bp
@@ -0,0 +1,346 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file hosts all the genrule and module definitions for all Android specific
+// code that needs further post-processing by hoststubgen to support Ravenwood.
+
+/////////////////////////
+// framework-minus-apex
+/////////////////////////
+
+// Process framework-minus-apex with hoststubgen for Ravenwood.
+// This step takes several tens of seconds, so we manually shard it to multiple modules.
+// All the copies have to be kept in sync.
+// TODO: Do the sharding better, either by making hostsubgen support sharding natively, or
+// making a better build rule.
+
+genrule_defaults {
+    name: "framework-minus-apex.ravenwood-base_defaults",
+    tools: ["hoststubgen"],
+    srcs: [
+        ":framework-minus-apex-for-host",
+        ":ravenwood-common-policies",
+        ":ravenwood-framework-policies",
+        ":ravenwood-standard-options",
+        ":ravenwood-annotation-allowed-classes",
+    ],
+    out: [
+        "ravenwood.jar",
+        "hoststubgen_framework-minus-apex.log",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+framework_minus_apex_cmd = "$(location hoststubgen) " +
+    "@$(location :ravenwood-standard-options) " +
+    "--debug-log $(location hoststubgen_framework-minus-apex.log) " +
+    "--out-jar $(location ravenwood.jar) " +
+    "--in-jar $(location :framework-minus-apex-for-host) " +
+    "--policy-override-file $(location :ravenwood-common-policies) " +
+    "--policy-override-file $(location :ravenwood-framework-policies) " +
+    "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) "
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X0",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 0",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X1",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 1",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X2",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 2",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X3",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 3",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X4",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 4",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X5",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 5",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X6",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 6",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X7",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 7",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X8",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 8",
+}
+
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_X9",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd + " --num-shards 10 --shard-index 9",
+}
+
+// Build framework-minus-apex.ravenwood-base without sharding.
+// We extract the various dump files from this one, rather than the sharded ones, because
+// some dumps use the output from other classes (e.g. base classes) which may not be in the
+// same shard. Also some of the dump files ("apis") may be slow even when sharded, because
+// the output contains the information from all the input classes, rather than the output classes.
+// Not using sharding is fine for this module because it's only used for collecting the
+// dump / stats files, which don't have to happen regularly.
+java_genrule {
+    name: "framework-minus-apex.ravenwood-base_all",
+    defaults: ["framework-minus-apex.ravenwood-base_defaults"],
+    cmd: framework_minus_apex_cmd +
+        "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " +
+        "--supported-api-list-file $(location hoststubgen_framework-minus-apex_apis.csv) " +
+
+        "--gen-keep-all-file $(location hoststubgen_framework-minus-apex_keep_all.txt) " +
+        "--gen-input-dump-file $(location hoststubgen_framework-minus-apex_dump.txt) ",
+
+    out: [
+        "hoststubgen_framework-minus-apex_keep_all.txt",
+        "hoststubgen_framework-minus-apex_dump.txt",
+        "hoststubgen_framework-minus-apex_stats.csv",
+        "hoststubgen_framework-minus-apex_apis.csv",
+    ],
+}
+
+// Marge all the sharded jars
+java_genrule {
+    name: "framework-minus-apex.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-java"],
+    cmd: "$(location merge_zips) $(out) $(in)",
+    tools: ["merge_zips"],
+    srcs: [
+        ":framework-minus-apex.ravenwood-base_X0{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X1{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X2{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X3{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X4{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X5{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X6{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X7{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X8{ravenwood.jar}",
+        ":framework-minus-apex.ravenwood-base_X9{ravenwood.jar}",
+    ],
+    out: [
+        "framework-minus-apex.ravenwood.jar",
+    ],
+}
+
+//////////////////
+// services.core
+//////////////////
+
+java_library {
+    name: "services.core-for-host",
+    installable: false, // host only jar.
+    static_libs: [
+        "services.core",
+    ],
+    sdk_version: "core_platform",
+    visibility: ["//visibility:private"],
+}
+
+java_genrule {
+    name: "services.core.ravenwood-base",
+    tools: ["hoststubgen"],
+    cmd: "$(location hoststubgen) " +
+        "@$(location :ravenwood-standard-options) " +
+
+        "--debug-log $(location hoststubgen_services.core.log) " +
+        "--stats-file $(location hoststubgen_services.core_stats.csv) " +
+        "--supported-api-list-file $(location hoststubgen_services.core_apis.csv) " +
+        "--gen-keep-all-file $(location hoststubgen_services.core_keep_all.txt) " +
+        "--gen-input-dump-file $(location hoststubgen_services.core_dump.txt) " +
+
+        "--out-jar $(location ravenwood.jar) " +
+        "--in-jar $(location :services.core-for-host) " +
+
+        "--policy-override-file $(location :ravenwood-common-policies) " +
+        "--policy-override-file $(location :ravenwood-services-policies) " +
+        "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) ",
+    srcs: [
+        ":services.core-for-host",
+        ":ravenwood-common-policies",
+        ":ravenwood-services-policies",
+        ":ravenwood-standard-options",
+        ":ravenwood-annotation-allowed-classes",
+    ],
+    out: [
+        "ravenwood.jar",
+
+        // Following files are created just as FYI.
+        "hoststubgen_services.core_keep_all.txt",
+        "hoststubgen_services.core_dump.txt",
+
+        "hoststubgen_services.core.log",
+        "hoststubgen_services.core_stats.csv",
+        "hoststubgen_services.core_apis.csv",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+java_genrule {
+    name: "services.core.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":services.core.ravenwood-base{ravenwood.jar}",
+    ],
+    out: [
+        "services.core.ravenwood.jar",
+    ],
+}
+
+// TODO(b/313930116) This jarjar is a bit slow. We should use hoststubgen for renaming,
+// but services.core.ravenwood has complex dependencies, so it'll take more than
+// just using hoststubgen "rename"s.
+java_library {
+    name: "services.core.ravenwood-jarjar",
+    defaults: ["ravenwood-internal-only-visibility-java"],
+    installable: false,
+    static_libs: [
+        "services.core.ravenwood",
+    ],
+    jarjar_rules: ":ravenwood-services-jarjar-rules",
+}
+
+///////////////
+// core-icu4j
+///////////////
+
+java_genrule {
+    name: "core-icu4j-for-host.ravenwood-base",
+    tools: ["hoststubgen"],
+    cmd: "$(location hoststubgen) " +
+        "@$(location :ravenwood-standard-options) " +
+
+        "--debug-log $(location hoststubgen_core-icu4j-for-host.log) " +
+        "--stats-file $(location hoststubgen_core-icu4j-for-host_stats.csv) " +
+        "--supported-api-list-file $(location hoststubgen_core-icu4j-for-host_apis.csv) " +
+        "--gen-keep-all-file $(location hoststubgen_core-icu4j-for-host_keep_all.txt) " +
+        "--gen-input-dump-file $(location hoststubgen_core-icu4j-for-host_dump.txt) " +
+
+        "--out-jar $(location ravenwood.jar) " +
+        "--in-jar $(location :core-icu4j-for-host) " +
+
+        "--policy-override-file $(location :ravenwood-common-policies) " +
+        "--policy-override-file $(location :icu-ravenwood-policies) ",
+    srcs: [
+        ":core-icu4j-for-host",
+
+        ":ravenwood-common-policies",
+        ":icu-ravenwood-policies",
+        ":ravenwood-standard-options",
+    ],
+    out: [
+        "ravenwood.jar",
+
+        // Following files are created just as FYI.
+        "hoststubgen_core-icu4j-for-host_keep_all.txt",
+        "hoststubgen_core-icu4j-for-host_dump.txt",
+
+        "hoststubgen_core-icu4j-for-host.log",
+        "hoststubgen_core-icu4j-for-host_stats.csv",
+        "hoststubgen_core-icu4j-for-host_apis.csv",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+java_genrule {
+    name: "core-icu4j-for-host.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":core-icu4j-for-host.ravenwood-base{ravenwood.jar}",
+    ],
+    out: [
+        "core-icu4j-for-host.ravenwood.jar",
+    ],
+}
+
+///////////////////////////////////
+// framework-configinfrastructure
+///////////////////////////////////
+
+java_genrule {
+    name: "framework-configinfrastructure.ravenwood-base",
+    tools: ["hoststubgen"],
+    cmd: "$(location hoststubgen) " +
+        "@$(location :ravenwood-standard-options) " +
+
+        "--debug-log $(location framework-configinfrastructure.log) " +
+        "--stats-file $(location framework-configinfrastructure_stats.csv) " +
+        "--supported-api-list-file $(location framework-configinfrastructure_apis.csv) " +
+        "--gen-keep-all-file $(location framework-configinfrastructure_keep_all.txt) " +
+        "--gen-input-dump-file $(location framework-configinfrastructure_dump.txt) " +
+
+        "--out-impl-jar $(location ravenwood.jar) " +
+        "--in-jar $(location :framework-configinfrastructure.impl{.jar}) " +
+
+        "--policy-override-file $(location :ravenwood-common-policies) " +
+        "--policy-override-file $(location :framework-configinfrastructure-ravenwood-policies) ",
+    srcs: [
+        ":framework-configinfrastructure.impl{.jar}",
+
+        ":ravenwood-common-policies",
+        ":framework-configinfrastructure-ravenwood-policies",
+        ":ravenwood-standard-options",
+    ],
+    out: [
+        "ravenwood.jar",
+
+        // Following files are created just as FYI.
+        "framework-configinfrastructure_keep_all.txt",
+        "framework-configinfrastructure_dump.txt",
+
+        "framework-configinfrastructure.log",
+        "framework-configinfrastructure_stats.csv",
+        "framework-configinfrastructure_apis.csv",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+java_genrule {
+    name: "framework-configinfrastructure.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":framework-configinfrastructure.ravenwood-base{ravenwood.jar}",
+    ],
+    out: [
+        "framework-configinfrastructure.ravenwood.jar",
+    ],
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 644babb..e2d73d1 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -20,12 +20,18 @@
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
 
 import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.ResourcesManager;
+import android.app.UiAutomation;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.os.Build;
@@ -34,12 +40,14 @@
 import android.os.Looper;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.provider.DeviceConfig_host;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.hoststubgen.hosthelper.HostTestUtils;
 import com.android.internal.os.RuntimeInit;
 import com.android.ravenwood.RavenwoodRuntimeNative;
 import com.android.ravenwood.common.RavenwoodCommonUtils;
@@ -52,8 +60,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
@@ -74,6 +84,8 @@
     private static final String MAIN_THREAD_NAME = "RavenwoodMain";
     private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
     private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+    private static final String RAVENWOOD_BUILD_PROP =
+            getRavenwoodRuntimePath() + "ravenwood-data/build.prop";
 
     /**
      * When enabled, attempt to dump all thread stacks just before we hit the
@@ -125,6 +137,9 @@
 
     private static RavenwoodConfig sConfig;
     private static RavenwoodSystemProperties sProps;
+    // TODO: use the real UiAutomation class instead of a mock
+    private static UiAutomation sMockUiAutomation;
+    private static Set<String> sAdoptedPermissions = Collections.emptySet();
     private static boolean sInitialized = false;
 
     /**
@@ -148,7 +163,8 @@
         System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
 
         // Do the basic set up for the android sysprops.
-        setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+        RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP);
+        setSystemProperties(null);
 
         // Make sure libandroid_runtime is loaded.
         RavenwoodNativeLoader.loadFrameworkNativeCode();
@@ -171,6 +187,7 @@
                 "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");
 
         assertMockitoVersion();
+        sMockUiAutomation = createMockUiAutomation();
     }
 
     /**
@@ -209,14 +226,9 @@
 
         ActivityManager.init$ravenwood(config.mCurrentUser);
 
-        final HandlerThread main;
-        if (config.mProvideMainThread) {
-            main = new HandlerThread(MAIN_THREAD_NAME);
-            main.start();
-            Looper.setMainLooperForTest(main.getLooper());
-        } else {
-            main = null;
-        }
+        final var main = new HandlerThread(MAIN_THREAD_NAME);
+        main.start();
+        Looper.setMainLooperForTest(main.getLooper());
 
         final boolean isSelfInstrumenting =
                 Objects.equals(config.mTestPackageName, config.mTargetPackageName);
@@ -261,7 +273,7 @@
 
         // Prepare other fields.
         config.mInstrumentation = new Instrumentation();
-        config.mInstrumentation.basicInit(config.mInstContext, config.mTargetContext);
+        config.mInstrumentation.basicInit(instContext, targetContext, sMockUiAutomation);
         InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY);
 
         RavenwoodSystemServer.init(config);
@@ -300,30 +312,35 @@
         config.mInstrumentation = null;
         if (config.mInstContext != null) {
             ((RavenwoodContext) config.mInstContext).cleanUp();
+            config.mInstContext = null;
         }
         if (config.mTargetContext != null) {
             ((RavenwoodContext) config.mTargetContext).cleanUp();
+            config.mTargetContext = null;
         }
-        config.mInstContext = null;
-        config.mTargetContext = null;
+        sMockUiAutomation.dropShellPermissionIdentity();
 
-        if (config.mProvideMainThread) {
-            Looper.getMainLooper().quit();
-            Looper.clearMainLooperForTest();
-        }
+        Looper.getMainLooper().quit();
+        Looper.clearMainLooperForTest();
 
         ActivityManager.reset$ravenwood();
 
         LocalServices.removeAllServicesForTest();
         ServiceManager.reset$ravenwood();
 
-        setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+        setSystemProperties(null);
         if (sOriginalIdentityToken != -1) {
             Binder.restoreCallingIdentity(sOriginalIdentityToken);
         }
         android.os.Process.reset$ravenwood();
 
-        ResourcesManager.setInstance(null); // Better structure needed.
+        DeviceConfig_host.reset();
+
+        try {
+            ResourcesManager.setInstance(null); // Better structure needed.
+        } catch (Exception e) {
+            // AOSP-CHANGE: AOSP doesn't support resources yet.
+        }
 
         if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
             maybeThrowPendingUncaughtException(true);
@@ -372,9 +389,10 @@
     /**
      * Set the current configuration to the actual SystemProperties.
      */
-    private static void setSystemProperties(RavenwoodSystemProperties systemProperties) {
+    private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
         SystemProperties.clearChangeCallbacksForTest();
         RavenwoodRuntimeNative.clearSystemProperties();
+        if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
         sProps = new RavenwoodSystemProperties(systemProperties, true);
         for (var entry : systemProperties.getValues().entrySet()) {
             RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
@@ -403,6 +421,31 @@
                 () -> Class.forName("org.mockito.Matchers"));
     }
 
+    private static UiAutomation createMockUiAutomation() {
+        var mock = mock(UiAutomation.class, inv -> {
+            HostTestUtils.onThrowMethodCalled();
+            return null;
+        });
+        doAnswer(inv -> {
+            sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
+            return null;
+        }).when(mock).adoptShellPermissionIdentity();
+        doAnswer(inv -> {
+            if (inv.getArgument(0) == null) {
+                sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
+            } else {
+                sAdoptedPermissions = (Set) Set.of(inv.getArguments());
+            }
+            return null;
+        }).when(mock).adoptShellPermissionIdentity(any());
+        doAnswer(inv -> {
+            sAdoptedPermissions = Collections.emptySet();
+            return null;
+        }).when(mock).dropShellPermissionIdentity();
+        doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions();
+        return mock;
+    }
+
     @SuppressWarnings("unused")  // Called from native code (ravenwood_sysprop.cpp)
     private static void checkSystemPropertyAccess(String key, boolean write) {
         boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key);
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 446f819..1f6e11d 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -152,7 +152,10 @@
         /**
          * Configure a "main" thread to be available for the duration of the test, as defined
          * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
+         *
+         * @deprecated
          */
+        @Deprecated
         public Builder setProvideMainThread(boolean provideMainThread) {
             mConfig.mProvideMainThread = provideMainThread;
             return this;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 4196d8e..93a6806 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -139,7 +139,10 @@
         /**
          * Configure a "main" thread to be available for the duration of the test, as defined
          * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
+         *
+         * @deprecated
          */
+        @Deprecated
         public Builder setProvideMainThread(boolean provideMainThread) {
             mBuilder.setProvideMainThread(provideMainThread);
             return this;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index f1e1ef6..ced1519 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -18,12 +18,94 @@
 
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
 
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
 public class RavenwoodSystemProperties {
+    private static final String TAG = "RavenwoodSystemProperties";
+
+    private static final Map<String, String> sDefaultValues = new HashMap<>();
+
+    private static final String[] PARTITIONS = {
+            "bootimage",
+            "odm",
+            "product",
+            "system",
+            "system_ext",
+            "vendor",
+            "vendor_dlkm",
+    };
+
+    /**
+     * More info about property file loading: system/core/init/property_service.cpp
+     * In the following logic, the only partition we would need to consider is "system",
+     * since we only read from system-build.prop
+     */
+    static void initialize(String propFile) {
+        // Load all properties from build.prop
+        try {
+            Files.readAllLines(Path.of(propFile)).stream()
+                    .map(String::trim)
+                    .filter(s -> !s.startsWith("#"))
+                    .map(s -> s.split("\\s*=\\s*", 2))
+                    .filter(a -> a.length == 2)
+                    .forEach(a -> sDefaultValues.put(a[0], a[1]));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        // If ro.product.${name} is not set, derive from ro.product.${partition}.${name}
+        // If ro.product.cpu.abilist* is not set, derive from ro.${partition}.product.cpu.abilist*
+        for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+            final String key;
+            if (entry.getKey().startsWith("ro.product.system.")) {
+                var name = entry.getKey().substring(18);
+                key = "ro.product." + name;
+
+            } else if (entry.getKey().startsWith("ro.system.product.cpu.abilist")) {
+                var name = entry.getKey().substring(22);
+                key = "ro.product.cpu." + name;
+            } else {
+                continue;
+            }
+            if (!sDefaultValues.containsKey(key)) {
+                sDefaultValues.put(key, entry.getValue());
+            }
+        }
+
+        // Some other custom values
+        sDefaultValues.put("ro.board.first_api_level", "1");
+        sDefaultValues.put("ro.product.first_api_level", "1");
+        sDefaultValues.put("ro.soc.manufacturer", "Android");
+        sDefaultValues.put("ro.soc.model", "Ravenwood");
+        sDefaultValues.put(RAVENWOOD_SYSPROP, "1");
+
+        // Log all values
+        sDefaultValues.forEach((key, value) -> RavenwoodCommonUtils.log(TAG, key + "=" + value));
+
+        // Copy ro.product.* and ro.build.* to all partitions, just in case
+        // We don't want to log these because these are just a lot of duplicate values
+        for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+            var key = entry.getKey();
+            if (key.startsWith("ro.product.") || key.startsWith("ro.build.")) {
+                var name = key.substring(3);
+                for (String partition : PARTITIONS) {
+                    var newKey = "ro." + partition + "." + name;
+                    if (!sDefaultValues.containsKey(newKey)) {
+                        sDefaultValues.put(newKey, entry.getValue());
+                    }
+                }
+            }
+        }
+    }
+
     private volatile boolean mIsImmutable;
 
     private final Map<String, String> mValues = new HashMap<>();
@@ -35,47 +117,15 @@
     private final Set<String> mKeyWritable = new HashSet<>();
 
     public RavenwoodSystemProperties() {
-        // TODO: load these values from build.prop generated files
-        setValueForPartitions("product.brand", "Android");
-        setValueForPartitions("product.device", "Ravenwood");
-        setValueForPartitions("product.manufacturer", "Android");
-        setValueForPartitions("product.model", "Ravenwood");
-        setValueForPartitions("product.name", "Ravenwood");
-
-        setValueForPartitions("product.cpu.abilist", "x86_64");
-        setValueForPartitions("product.cpu.abilist32", "");
-        setValueForPartitions("product.cpu.abilist64", "x86_64");
-
-        setValueForPartitions("build.date", "Thu Jan 01 00:00:00 GMT 2024");
-        setValueForPartitions("build.date.utc", "1704092400");
-        setValueForPartitions("build.id", "MAIN");
-        setValueForPartitions("build.tags", "dev-keys");
-        setValueForPartitions("build.type", "userdebug");
-        setValueForPartitions("build.version.all_codenames", "REL");
-        setValueForPartitions("build.version.codename", "REL");
-        setValueForPartitions("build.version.incremental", "userdebug.ravenwood.20240101");
-        setValueForPartitions("build.version.known_codenames", "REL");
-        setValueForPartitions("build.version.release", "14");
-        setValueForPartitions("build.version.release_or_codename", "VanillaIceCream");
-        setValueForPartitions("build.version.sdk", "34");
-
-        setValue("ro.board.first_api_level", "1");
-        setValue("ro.product.first_api_level", "1");
-
-        setValue("ro.soc.manufacturer", "Android");
-        setValue("ro.soc.model", "Ravenwood");
-
-        setValue("ro.debuggable", "1");
-
-        setValue(RAVENWOOD_SYSPROP, "1");
+        mValues.putAll(sDefaultValues);
     }
 
     /** Copy constructor */
     public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
-        this.mKeyReadable.addAll(source.mKeyReadable);
-        this.mKeyWritable.addAll(source.mKeyWritable);
-        this.mValues.putAll(source.mValues);
-        this.mIsImmutable = immutable;
+        mKeyReadable.addAll(source.mKeyReadable);
+        mKeyWritable.addAll(source.mKeyWritable);
+        mValues.putAll(source.mValues);
+        mIsImmutable = immutable;
     }
 
     public Map<String, String> getValues() {
@@ -123,36 +173,12 @@
         return mKeyWritable.contains(key);
     }
 
-    private static final String[] PARTITIONS = {
-            "bootimage",
-            "odm",
-            "product",
-            "system",
-            "system_ext",
-            "vendor",
-            "vendor_dlkm",
-    };
-
     private void ensureNotImmutable() {
         if (mIsImmutable) {
             throw new RuntimeException("Unable to update immutable instance");
         }
     }
 
-    /**
-     * Set the given property for all possible partitions where it could be defined. For
-     * example, the value of {@code ro.build.type} is typically also mirrored under
-     * {@code ro.system.build.type}, etc.
-     */
-    private void setValueForPartitions(String key, String value) {
-        ensureNotImmutable();
-
-        setValue("ro." + key, value);
-        for (String partition : PARTITIONS) {
-            setValue("ro." + partition + "." + key, value);
-        }
-    }
-
     public void setValue(String key, Object value) {
         ensureNotImmutable();
 
@@ -195,11 +221,4 @@
             return key;
         }
     }
-
-    /**
-     * Return an immutable, default instance.
-     */
-    // Create a default instance, and make an immutable copy of it.
-    public static final RavenwoodSystemProperties DEFAULT_VALUES =
-            new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true);
 }
diff --git a/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java b/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java
new file mode 100644
index 0000000..9c2188f
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.provider;
+
+public class DeviceConfig_host {
+
+    /**
+     * Called by Ravenwood runtime to reset all local changes.
+     */
+    public static void reset() {
+        RavenwoodConfigDataStore.getInstance().clearAll();
+    }
+
+    /**
+     * Called by {@link DeviceConfig#newDataStore()}
+     */
+    public static DeviceConfigDataStore newDataStore() {
+        return RavenwoodConfigDataStore.getInstance();
+    }
+}
diff --git a/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java b/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java
new file mode 100644
index 0000000..4bc3de7
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.DeviceConfig.BadConfigException;
+import android.provider.DeviceConfig.MonitorCallback;
+import android.provider.DeviceConfig.Properties;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link DeviceConfigDataStore} used only on Ravenwood.
+ *
+ * TODO(b/368591527) Support monitor callback related features
+ * TODO(b/368591527) Support "default" related features
+ */
+final class RavenwoodConfigDataStore implements DeviceConfigDataStore {
+    private static final RavenwoodConfigDataStore sInstance = new RavenwoodConfigDataStore();
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private int mSyncDisabledMode = DeviceConfig.SYNC_DISABLED_MODE_NONE;
+
+    @GuardedBy("mLock")
+    private final HashMap<String, HashMap<String, String>> mStore = new HashMap<>();
+
+    private record ObserverInfo(String namespace, ContentObserver observer) {
+    }
+
+    @GuardedBy("mLock")
+    private final ArrayList<ObserverInfo> mObservers = new ArrayList<>();
+
+    static RavenwoodConfigDataStore getInstance() {
+        return sInstance;
+    }
+
+    private static void shouldNotBeCalled() {
+        throw new RuntimeException("shouldNotBeCalled");
+    }
+
+    void clearAll() {
+        synchronized (mLock) {
+            mSyncDisabledMode = DeviceConfig.SYNC_DISABLED_MODE_NONE;
+            mStore.clear();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private HashMap<String, String> getNamespaceLocked(@NonNull String namespace) {
+        Objects.requireNonNull(namespace);
+        return mStore.computeIfAbsent(namespace, k -> new HashMap<>());
+    }
+
+    /** {@inheritDoc} */
+    @NonNull
+    @Override
+    public Map<String, String> getAllProperties() {
+        synchronized (mLock) {
+            var ret = new HashMap<String, String>();
+
+            for (var namespaceAndMap : mStore.entrySet()) {
+                for (var nameAndValue : namespaceAndMap.getValue().entrySet()) {
+                    ret.put(namespaceAndMap.getKey() + "/" + nameAndValue.getKey(),
+                            nameAndValue.getValue());
+                }
+            }
+            return ret;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @NonNull
+    @Override
+    public Properties getProperties(@NonNull String namespace, @NonNull String... names) {
+        Objects.requireNonNull(namespace);
+
+        synchronized (mLock) {
+            var namespaceMap = getNamespaceLocked(namespace);
+
+            if (names.length == 0) {
+                return new Properties(namespace, Map.copyOf(namespaceMap));
+            } else {
+                var map = new HashMap<String, String>();
+                for (var name : names) {
+                    Objects.requireNonNull(name);
+                    map.put(name, namespaceMap.get(name));
+                }
+                return new Properties(namespace, map);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean setProperties(@NonNull Properties properties) throws BadConfigException {
+        Objects.requireNonNull(properties);
+
+        synchronized (mLock) {
+            var namespaceMap = getNamespaceLocked(properties.getNamespace());
+            for (var kv : properties.getPropertyValues().entrySet()) {
+                namespaceMap.put(
+                        Objects.requireNonNull(kv.getKey()),
+                        Objects.requireNonNull(kv.getValue())
+                );
+            }
+            notifyObserversLock(properties.getNamespace(),
+                    properties.getKeyset().toArray(new String[0]));
+        }
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean setProperty(@NonNull String namespace, @NonNull String name,
+            @Nullable String value, boolean makeDefault) {
+        Objects.requireNonNull(namespace);
+        Objects.requireNonNull(name);
+
+        synchronized (mLock) {
+            var namespaceMap = getNamespaceLocked(namespace);
+            namespaceMap.put(name, value);
+
+            // makeDefault not supported.
+            notifyObserversLock(namespace, new String[]{name});
+        }
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
+        Objects.requireNonNull(namespace);
+        Objects.requireNonNull(name);
+
+        synchronized (mLock) {
+            var namespaceMap = getNamespaceLocked(namespace);
+            if (namespaceMap.remove(name) != null) {
+                notifyObserversLock(namespace, new String[]{name});
+            }
+        }
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void resetToDefaults(int resetMode, @Nullable String namespace) {
+        // not supported in DeviceConfig.java
+        shouldNotBeCalled();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setSyncDisabledMode(int syncDisabledMode) {
+        synchronized (mLock) {
+            mSyncDisabledMode = syncDisabledMode;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getSyncDisabledMode() {
+        synchronized (mLock) {
+            return mSyncDisabledMode;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setMonitorCallback(@NonNull ContentResolver resolver, @NonNull Executor executor,
+            @NonNull MonitorCallback callback) {
+        // not supported in DeviceConfig.java
+        shouldNotBeCalled();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void clearMonitorCallback(@NonNull ContentResolver resolver) {
+        // not supported in DeviceConfig.java
+        shouldNotBeCalled();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void registerContentObserver(@NonNull String namespace, boolean notifyForescendants,
+            ContentObserver contentObserver) {
+        synchronized (mLock) {
+            mObservers.add(new ObserverInfo(
+                    Objects.requireNonNull(namespace),
+                    Objects.requireNonNull(contentObserver)
+            ));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void unregisterContentObserver(@NonNull ContentObserver contentObserver) {
+        synchronized (mLock) {
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                if (mObservers.get(i).observer == contentObserver) {
+                    mObservers.remove(i);
+                }
+            }
+        }
+    }
+
+    private static final Uri CONTENT_URI = Uri.parse("content://settings/config");
+
+    @GuardedBy("mLock")
+    private void notifyObserversLock(@NonNull String namespace, String[] keys) {
+        var urib = CONTENT_URI.buildUpon().appendPath(namespace);
+        for (var key : keys) {
+            urib.appendEncodedPath(key);
+        }
+        var uri = urib.build();
+
+        final var copy = List.copyOf(mObservers);
+        new Handler(Looper.getMainLooper()).post(() -> {
+            for (int i = copy.size() - 1; i >= 0; i--) {
+                if (copy.get(i).namespace.equals(namespace)) {
+                    copy.get(i).observer.dispatchChange(false, uri);
+                }
+            }
+        });
+    }
+}
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index ac499b9..d7f4b3e 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -54,34 +54,36 @@
     auto_gen_config: true,
 }
 
-// TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
+android_test {
+    name: "RavenwoodBivalentTest_device",
 
-// android_test {
-//     name: "RavenwoodBivalentTest_device",
-//
-//     srcs: [
-//         "test/**/*.java",
-//     ],
-//     static_libs: [
-//         "junit",
-//         "truth",
-//
-//         "androidx.annotation_annotation",
-//         "androidx.test.ext.junit",
-//         "androidx.test.rules",
-//
-//         "junit-params",
-//         "platform-parametric-runner-lib",
-//
-//         "ravenwood-junit",
-//     ],
-//     jni_libs: [
-//         "libravenwoodbivalenttest_jni",
-//     ],
-//     test_suites: [
-//         "device-tests",
-//     ],
-//     optimize: {
-//         enabled: false,
-//     },
-// }
+    srcs: [
+        "test/**/*.java",
+    ],
+    // TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
+    exclude_srcs: [
+        "test/**/ravenizer/*.java",
+    ],
+    static_libs: [
+        "junit",
+        "truth",
+
+        "androidx.annotation_annotation",
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+
+        "junit-params",
+        "platform-parametric-runner-lib",
+
+        "ravenwood-junit",
+    ],
+    jni_libs: [
+        "libravenwoodbivalenttest_jni",
+    ],
+    test_suites: [
+        "device-tests",
+    ],
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodUiAutomationTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodUiAutomationTest.java
new file mode 100644
index 0000000..eb94827
--- /dev/null
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodUiAutomationTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.bivalenttest;
+
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodUiAutomationTest {
+
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Test
+    public void testGetUiAutomation() {
+        assertNotNull(mInstrumentation.getUiAutomation());
+    }
+
+    @Test
+    public void testGetUiAutomationWithFlags() {
+        assertNotNull(mInstrumentation.getUiAutomation(UiAutomation.FLAG_DONT_USE_ACCESSIBILITY));
+    }
+
+    @Test
+    public void testShellPermissionApis() {
+        var uiAutomation = mInstrumentation.getUiAutomation();
+        assertTrue(uiAutomation.getAdoptedShellPermissions().isEmpty());
+        uiAutomation.adoptShellPermissionIdentity();
+        assertEquals(uiAutomation.getAdoptedShellPermissions(), UiAutomation.ALL_PERMISSIONS);
+        uiAutomation.adoptShellPermissionIdentity((String[]) null);
+        assertEquals(uiAutomation.getAdoptedShellPermissions(), UiAutomation.ALL_PERMISSIONS);
+        uiAutomation.adoptShellPermissionIdentity(
+                OVERRIDE_COMPAT_CHANGE_CONFIG, READ_COMPAT_CHANGE_CONFIG);
+        assertEquals(uiAutomation.getAdoptedShellPermissions(),
+                Set.of(OVERRIDE_COMPAT_CHANGE_CONFIG, READ_COMPAT_CHANGE_CONFIG));
+        uiAutomation.dropShellPermissionIdentity();
+        assertTrue(uiAutomation.getAdoptedShellPermissions().isEmpty());
+    }
+
+    @Test
+    public void testUnsupportedMethod() {
+        // Only unsupported on Ravenwood
+        assumeTrue(RavenwoodCommonUtils.isOnRavenwood());
+        assertThrows(RuntimeException.class,
+                () -> mInstrumentation.getUiAutomation().executeShellCommand("echo ok"));
+    }
+}
diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/tests/minimum-test/Android.bp
similarity index 100%
rename from ravenwood/minimum-test/Android.bp
rename to ravenwood/tests/minimum-test/Android.bp
diff --git a/ravenwood/minimum-test/README.md b/ravenwood/tests/minimum-test/README.md
similarity index 100%
rename from ravenwood/minimum-test/README.md
rename to ravenwood/tests/minimum-test/README.md
diff --git a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
similarity index 100%
rename from ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
rename to ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
diff --git a/ravenwood/mockito/Android.bp b/ravenwood/tests/mockito/Android.bp
similarity index 100%
rename from ravenwood/mockito/Android.bp
rename to ravenwood/tests/mockito/Android.bp
diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/tests/mockito/AndroidManifest.xml
similarity index 100%
rename from ravenwood/mockito/AndroidManifest.xml
rename to ravenwood/tests/mockito/AndroidManifest.xml
diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/tests/mockito/AndroidTest.xml
similarity index 100%
rename from ravenwood/mockito/AndroidTest.xml
rename to ravenwood/tests/mockito/AndroidTest.xml
diff --git a/ravenwood/mockito/README.md b/ravenwood/tests/mockito/README.md
similarity index 100%
rename from ravenwood/mockito/README.md
rename to ravenwood/tests/mockito/README.md
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
similarity index 100%
rename from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
rename to ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
similarity index 100%
rename from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
rename to ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
similarity index 100%
rename from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
rename to ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
diff --git a/ravenwood/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp
similarity index 100%
rename from ravenwood/resapk_test/Android.bp
rename to ravenwood/tests/resapk_test/Android.bp
diff --git a/ravenwood/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp
similarity index 100%
rename from ravenwood/resapk_test/apk/Android.bp
rename to ravenwood/tests/resapk_test/apk/Android.bp
diff --git a/ravenwood/resapk_test/apk/AndroidManifest.xml b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml
similarity index 100%
rename from ravenwood/resapk_test/apk/AndroidManifest.xml
rename to ravenwood/tests/resapk_test/apk/AndroidManifest.xml
diff --git a/ravenwood/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
similarity index 100%
rename from ravenwood/resapk_test/apk/res/values/strings.xml
rename to ravenwood/tests/resapk_test/apk/res/values/strings.xml
diff --git a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
similarity index 100%
rename from ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
rename to ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp
similarity index 100%
rename from ravenwood/runtime-test/Android.bp
rename to ravenwood/tests/runtime-test/Android.bp
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
similarity index 100%
rename from ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
rename to ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
similarity index 100%
rename from ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
rename to ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
diff --git a/ravenwood/services-test/Android.bp b/ravenwood/tests/services-test/Android.bp
similarity index 100%
rename from ravenwood/services-test/Android.bp
rename to ravenwood/tests/services-test/Android.bp
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
similarity index 100%
rename from ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
rename to ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
similarity index 100%
rename from ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
rename to ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
diff --git a/ravenwood/texts/ravenwood-common-policies.txt b/ravenwood/texts/ravenwood-common-policies.txt
new file mode 100644
index 0000000..08f53977
--- /dev/null
+++ b/ravenwood/texts/ravenwood-common-policies.txt
@@ -0,0 +1,20 @@
+# Ravenwood "policy" that should apply to all code.
+
+# Keep all AIDL interfaces
+class :aidl keepclass
+
+# Keep all feature flag implementations
+class :feature_flags keepclass
+
+# Keep all sysprops generated code implementations
+class :sysprops keepclass
+
+# Keep all resource R classes
+class :r keepclass
+
+# Support APIs not available in standard JRE
+class java.io.FileDescriptor keep
+    method getInt$ ()I @com.android.ravenwood.RavenwoodJdkPatch.getInt$
+    method setInt$ (I)V @com.android.ravenwood.RavenwoodJdkPatch.setInt$
+class java.util.LinkedHashMap keep
+    method eldest ()Ljava/util/Map$Entry; @com.android.ravenwood.RavenwoodJdkPatch.eldest
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index d962c82..3649f0e 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -1,29 +1,10 @@
 # Ravenwood "policy" file for framework-minus-apex.
 
-# Keep all AIDL interfaces
-class :aidl keepclass
-
-# Keep all feature flag implementations
-class :feature_flags keepclass
-
-# Keep all sysprops generated code implementations
-class :sysprops keepclass
-
-# Keep all resource R classes
-class :r keepclass
-
 # To avoid VerifyError on nano proto files (b/324063814), we rename nano proto classes.
 # Note: The "rename" directive must use slashes (/) as a package name separator.
 rename com/.*/nano/   devicenano/
 rename android/.*/nano/   devicenano/
 
-# Support APIs not available in standard JRE
-class java.io.FileDescriptor keep
-    method getInt$ ()I @com.android.ravenwood.RavenwoodJdkPatch.getInt$
-    method setInt$ (I)V @com.android.ravenwood.RavenwoodJdkPatch.setInt$
-class java.util.LinkedHashMap keep
-    method eldest ()Ljava/util/Map$Entry; @com.android.ravenwood.RavenwoodJdkPatch.eldest
-
 # Exported to Mainline modules; cannot use annotations
 class com.android.internal.util.FastXmlSerializer keepclass
 class com.android.internal.util.FileRotator keepclass
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index 5cdb4f7..cc2fa60 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1,7 +1 @@
 # Ravenwood "policy" file for services.core.
-
-# Keep all AIDL interfaces
-class :aidl keepclass
-
-# Keep all feature flag implementations
-class :feature_flags keepclass
diff --git a/ravenwood/tools/hoststubgen/.gitignore b/ravenwood/tools/hoststubgen/.gitignore
new file mode 100644
index 0000000..82158c9
--- /dev/null
+++ b/ravenwood/tools/hoststubgen/.gitignore
@@ -0,0 +1,4 @@
+framework-all-stub-out
+out/
+*-out/
+*.log
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp
similarity index 98%
rename from tools/hoststubgen/hoststubgen/Android.bp
rename to ravenwood/tools/hoststubgen/Android.bp
index 4920f7b4..a5ff496 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/ravenwood/tools/hoststubgen/Android.bp
@@ -8,7 +8,7 @@
 
     // OWNER: g/ravenwood
     // Bug component: 25698
-    default_team: "trendy_team_framework_backstage_power",
+    default_team: "trendy_team_ravenwood",
 }
 
 // Visibility only for ravenwood prototype uses.
diff --git a/tools/hoststubgen/README.md b/ravenwood/tools/hoststubgen/README.md
similarity index 84%
rename from tools/hoststubgen/README.md
rename to ravenwood/tools/hoststubgen/README.md
index 1a895dc..615e767 100644
--- a/tools/hoststubgen/README.md
+++ b/ravenwood/tools/hoststubgen/README.md
@@ -11,7 +11,7 @@
 
 - HostStubGen itself is design to be agnostic to Android. It doesn't use any Android APIs
 (hidden or not). But it may use Android specific knowledge -- e.g. as of now,
-AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes. 
+AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes.
 
 - `test-tiny-framework/` contains basic tests that are agnostic to Android.
 
@@ -20,19 +20,16 @@
 
 ## Directories and files
 
-- `hoststubgen/`
-  Contains source code of the "hoststubgen" tool and relevant code
+- `src/`
 
-  - `src/`
+  HostStubGen tool source code.
 
-    HostStubGen tool source code.
+- `annotations-src/` See `Android.bp`.
+- `helper-framework-buildtime-src/` See `Android.bp`.
+- `helper-framework-runtime-src/` See `Android.bp`.
+- `helper-runtime-src/` See `Android.bp`.
 
-  - `annotations-src/` See `Android.bp`.
-  - `helper-framework-buildtime-src/` See `Android.bp`.
-  - `helper-framework-runtime-src/` See `Android.bp`.
-  - `helper-runtime-src/` See `Android.bp`.
-
-  - `test-tiny-framework/` See `README.md` in it.
+- `test-tiny-framework/` See `README.md` in it.
 
 - `scripts`
   - `dump-jar.sh`
@@ -78,4 +75,4 @@
 
 - At some point, we can move or delete all Android specific code to `frameworks/base/ravenwood`.
   - `helper-framework-*-src` should be moved to `frameworks/base/ravenwood`
-  - `test-framework` should be deleted.
\ No newline at end of file
+  - `test-framework` should be deleted.
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
rename to ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
diff --git a/tools/hoststubgen/common.sh b/ravenwood/tools/hoststubgen/common.sh
similarity index 100%
rename from tools/hoststubgen/common.sh
rename to ravenwood/tools/hoststubgen/common.sh
diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/ravenwood/tools/hoststubgen/framework-policy-override.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/framework-policy-override.txt
rename to ravenwood/tools/hoststubgen/framework-policy-override.txt
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
rename to ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
rename to ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
diff --git a/tools/hoststubgen/hoststubgen/invoketest/Android.bp b/ravenwood/tools/hoststubgen/invoketest/Android.bp
similarity index 100%
rename from tools/hoststubgen/hoststubgen/invoketest/Android.bp
rename to ravenwood/tools/hoststubgen/invoketest/Android.bp
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh
similarity index 100%
rename from tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
rename to ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh
diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/ravenwood/tools/hoststubgen/jarjar-rules.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/jarjar-rules.txt
rename to ravenwood/tools/hoststubgen/jarjar-rules.txt
diff --git a/tools/hoststubgen/scripts/Android.bp b/ravenwood/tools/hoststubgen/scripts/Android.bp
similarity index 100%
rename from tools/hoststubgen/scripts/Android.bp
rename to ravenwood/tools/hoststubgen/scripts/Android.bp
diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
similarity index 100%
rename from tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
rename to ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
diff --git a/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar
similarity index 100%
rename from tools/hoststubgen/scripts/dump-jar
rename to ravenwood/tools/hoststubgen/scripts/dump-jar
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
similarity index 97%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 165bb57..6d8d7b7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -27,7 +27,7 @@
 import com.android.hoststubgen.filters.KeepNativeFilter
 import com.android.hoststubgen.filters.OutputFilter
 import com.android.hoststubgen.filters.SanitizationFilter
-import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
+import com.android.hoststubgen.filters.TextFileFilterPolicyParser
 import com.android.hoststubgen.filters.printAsTextPolicy
 import com.android.hoststubgen.utils.ClassFilter
 import com.android.hoststubgen.visitors.BaseAdapter
@@ -178,8 +178,10 @@
 
         // Next, "text based" filter, which allows to override polices without touching
         // the target code.
-        options.policyOverrideFile.ifSet {
-            filter = createFilterFromTextPolicyFile(it, allClasses, filter)
+        if (options.policyOverrideFiles.isNotEmpty()) {
+            val parser = TextFileFilterPolicyParser(allClasses, filter)
+            options.policyOverrideFiles.forEach(parser::parse)
+            filter = parser.createOutputFilter()
         }
 
         // Apply the implicit filter.
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
similarity index 97%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index b083d89..55e853e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -100,7 +100,7 @@
         var defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
         var defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
 
-        var policyOverrideFile: SetOnce<String?> = SetOnce(null),
+        var policyOverrideFiles: MutableList<String> = mutableListOf(),
 
         var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
 
@@ -164,7 +164,7 @@
                         "--out-jar", "--out-impl-jar" -> ret.outJar.set(nextArg())
 
                         "--policy-override-file" ->
-                            ret.policyOverrideFile.set(nextArg())!!.ensureFileExists()
+                            ret.policyOverrideFiles.add(nextArg().ensureFileExists())
 
                         "--clean-up-on-error" -> ret.cleanUpOnError.set(true)
                         "--no-clean-up-on-error" -> ret.cleanUpOnError.set(false)
@@ -291,7 +291,7 @@
               annotationAllowedClassesFile=$annotationAllowedClassesFile,
               defaultClassLoadHook=$defaultClassLoadHook,
               defaultMethodCallHook=$defaultMethodCallHook,
-              policyOverrideFile=$policyOverrideFile,
+              policyOverrideFiles=${policyOverrideFiles.toTypedArray().contentToString()},
               defaultPolicy=$defaultPolicy,
               cleanUpOnError=$cleanUpOnError,
               enableClassChecker=$enableClassChecker,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
new file mode 100644
index 0000000..caf80eb
--- /dev/null
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.filters
+
+import com.android.hoststubgen.ParseException
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.splitWithLastPeriod
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.asm.toJvmClassName
+import com.android.hoststubgen.log
+import com.android.hoststubgen.normalizeTextLine
+import com.android.hoststubgen.whitespaceRegex
+import java.io.File
+import java.io.PrintWriter
+import java.util.regex.Pattern
+import org.objectweb.asm.tree.ClassNode
+
+/**
+ * Print a class node as a "keep" policy.
+ */
+fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) {
+    pw.printf("class %s %s\n", cn.name.toHumanReadableClassName(), "keep")
+
+    cn.fields?.let {
+        for (f in it.sortedWith(compareBy({ it.name }))) {
+            pw.printf("    field %s %s\n", f.name, "keep")
+        }
+    }
+    cn.methods?.let {
+        for (m in it.sortedWith(compareBy({ it.name }, { it.desc }))) {
+            pw.printf("    method %s %s %s\n", m.name, m.desc, "keep")
+        }
+    }
+}
+
+private const val FILTER_REASON = "file-override"
+
+private enum class SpecialClass {
+    NotSpecial,
+    Aidl,
+    FeatureFlags,
+    Sysprops,
+    RFile,
+}
+
+class TextFileFilterPolicyParser(
+    private val classes: ClassNodes,
+    fallback: OutputFilter
+) {
+    private val subclassFilter = SubclassFilter(classes, fallback)
+    private val packageFilter = PackageFilter(subclassFilter)
+    private val imf = InMemoryOutputFilter(classes, packageFilter)
+    private var aidlPolicy: FilterPolicyWithReason? = null
+    private var featureFlagsPolicy: FilterPolicyWithReason? = null
+    private var syspropsPolicy: FilterPolicyWithReason? = null
+    private var rFilePolicy: FilterPolicyWithReason? = null
+    private val typeRenameSpec = mutableListOf<TextFilePolicyRemapperFilter.TypeRenameSpec>()
+    private val methodReplaceSpec =
+        mutableListOf<TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec>()
+
+    private lateinit var currentClassName: String
+
+    /**
+     * Read a given "policy" file and return as an [OutputFilter]
+     */
+    fun parse(file: String) {
+        log.i("Loading offloaded annotations from $file ...")
+        log.withIndent {
+            var lineNo = 0
+            try {
+                File(file).forEachLine {
+                    lineNo++
+                    val line = normalizeTextLine(it)
+                    if (line.isEmpty()) {
+                        return@forEachLine // skip empty lines.
+                    }
+                    parseLine(line)
+                }
+            } catch (e: ParseException) {
+                throw e.withSourceInfo(file, lineNo)
+            }
+        }
+    }
+
+    fun createOutputFilter(): OutputFilter {
+        var ret: OutputFilter = imf
+        if (typeRenameSpec.isNotEmpty()) {
+            ret = TextFilePolicyRemapperFilter(typeRenameSpec, ret)
+        }
+        if (methodReplaceSpec.isNotEmpty()) {
+            ret = TextFilePolicyMethodReplaceFilter(methodReplaceSpec, classes, ret)
+        }
+
+        // Wrap the in-memory-filter with AHF.
+        ret = AndroidHeuristicsFilter(
+            classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, ret
+        )
+
+        return ret
+    }
+
+    private fun parseLine(line: String) {
+        val fields = line.split(whitespaceRegex).toTypedArray()
+        when (fields[0].lowercase()) {
+            "p", "package" -> parsePackage(fields)
+            "c", "class" -> parseClass(fields)
+            "f", "field" -> parseField(fields)
+            "m", "method" -> parseMethod(fields)
+            "r", "rename" -> parseRename(fields)
+            else -> throw ParseException("Unknown directive \"${fields[0]}\"")
+        }
+    }
+
+    private fun resolveSpecialClass(className: String): SpecialClass {
+        if (!className.startsWith(":")) {
+            return SpecialClass.NotSpecial
+        }
+        when (className.lowercase()) {
+            ":aidl" -> return SpecialClass.Aidl
+            ":feature_flags" -> return SpecialClass.FeatureFlags
+            ":sysprops" -> return SpecialClass.Sysprops
+            ":r" -> return SpecialClass.RFile
+        }
+        throw ParseException("Invalid special class name \"$className\"")
+    }
+
+    private fun resolveExtendingClass(className: String): String? {
+        if (!className.startsWith("*")) {
+            return null
+        }
+        return className.substring(1)
+    }
+
+    private fun parsePolicy(s: String): FilterPolicy {
+        return when (s.lowercase()) {
+            "k", "keep" -> FilterPolicy.Keep
+            "t", "throw" -> FilterPolicy.Throw
+            "r", "remove" -> FilterPolicy.Remove
+            "kc", "keepclass" -> FilterPolicy.KeepClass
+            "i", "ignore" -> FilterPolicy.Ignore
+            "rdr", "redirect" -> FilterPolicy.Redirect
+            else -> {
+                if (s.startsWith("@")) {
+                    FilterPolicy.Substitute
+                } else {
+                    throw ParseException("Invalid policy \"$s\"")
+                }
+            }
+        }
+    }
+
+    private fun parsePackage(fields: Array<String>) {
+        if (fields.size < 3) {
+            throw ParseException("Package ('p') expects 2 fields.")
+        }
+        val name = fields[1]
+        val rawPolicy = fields[2]
+        if (resolveExtendingClass(name) != null) {
+            throw ParseException("Package can't be a super class type")
+        }
+        if (resolveSpecialClass(name) != SpecialClass.NotSpecial) {
+            throw ParseException("Package can't be a special class type")
+        }
+        if (rawPolicy.startsWith("!")) {
+            throw ParseException("Package can't have a substitution")
+        }
+        if (rawPolicy.startsWith("~")) {
+            throw ParseException("Package can't have a class load hook")
+        }
+        val policy = parsePolicy(rawPolicy)
+        if (!policy.isUsableWithClasses) {
+            throw ParseException("Package can't have policy '$policy'")
+        }
+        packageFilter.addPolicy(name, policy.withReason(FILTER_REASON))
+    }
+
+    private fun parseClass(fields: Array<String>) {
+        if (fields.size < 3) {
+            throw ParseException("Class ('c') expects 2 fields.")
+        }
+        currentClassName = fields[1]
+
+        // superClass is set when the class name starts with a "*".
+        val superClass = resolveExtendingClass(currentClassName)
+
+        // :aidl, etc?
+        val classType = resolveSpecialClass(currentClassName)
+
+        if (fields[2].startsWith("!")) {
+            if (classType != SpecialClass.NotSpecial) {
+                // We could support it, but not needed at least for now.
+                throw ParseException(
+                    "Special class can't have a substitution"
+                )
+            }
+            // It's a redirection class.
+            val toClass = fields[2].substring(1)
+            imf.setRedirectionClass(currentClassName, toClass)
+        } else if (fields[2].startsWith("~")) {
+            if (classType != SpecialClass.NotSpecial) {
+                // We could support it, but not needed at least for now.
+                throw ParseException(
+                    "Special class can't have a class load hook"
+                )
+            }
+            // It's a class-load hook
+            val callback = fields[2].substring(1)
+            imf.setClassLoadHook(currentClassName, callback)
+        } else {
+            val policy = parsePolicy(fields[2])
+            if (!policy.isUsableWithClasses) {
+                throw ParseException("Class can't have policy '$policy'")
+            }
+
+            when (classType) {
+                SpecialClass.NotSpecial -> {
+                    // TODO: Duplicate check, etc
+                    if (superClass == null) {
+                        imf.setPolicyForClass(
+                            currentClassName, policy.withReason(FILTER_REASON)
+                        )
+                    } else {
+                        subclassFilter.addPolicy(
+                            superClass,
+                            policy.withReason("extends $superClass")
+                        )
+                    }
+                }
+
+                SpecialClass.Aidl -> {
+                    if (aidlPolicy != null) {
+                        throw ParseException(
+                            "Policy for AIDL classes already defined"
+                        )
+                    }
+                    aidlPolicy = policy.withReason(
+                        "$FILTER_REASON (special-class AIDL)"
+                    )
+                }
+
+                SpecialClass.FeatureFlags -> {
+                    if (featureFlagsPolicy != null) {
+                        throw ParseException(
+                            "Policy for feature flags already defined"
+                        )
+                    }
+                    featureFlagsPolicy = policy.withReason(
+                        "$FILTER_REASON (special-class feature flags)"
+                    )
+                }
+
+                SpecialClass.Sysprops -> {
+                    if (syspropsPolicy != null) {
+                        throw ParseException(
+                            "Policy for sysprops already defined"
+                        )
+                    }
+                    syspropsPolicy = policy.withReason(
+                        "$FILTER_REASON (special-class sysprops)"
+                    )
+                }
+
+                SpecialClass.RFile -> {
+                    if (rFilePolicy != null) {
+                        throw ParseException(
+                            "Policy for R file already defined"
+                        )
+                    }
+                    rFilePolicy = policy.withReason(
+                        "$FILTER_REASON (special-class R file)"
+                    )
+                }
+            }
+        }
+    }
+
+    private fun parseField(fields: Array<String>) {
+        if (fields.size < 3) {
+            throw ParseException("Field ('f') expects 2 fields.")
+        }
+        val name = fields[1]
+        val policy = parsePolicy(fields[2])
+        if (!policy.isUsableWithFields) {
+            throw ParseException("Field can't have policy '$policy'")
+        }
+        require(this::currentClassName.isInitialized)
+
+        // TODO: Duplicate check, etc
+        imf.setPolicyForField(currentClassName, name, policy.withReason(FILTER_REASON))
+    }
+
+    private fun parseMethod(fields: Array<String>) {
+        if (fields.size < 4) {
+            throw ParseException("Method ('m') expects 3 fields.")
+        }
+        val name = fields[1]
+        val signature = fields[2]
+        val policy = parsePolicy(fields[3])
+
+        if (!policy.isUsableWithMethods) {
+            throw ParseException("Method can't have policy '$policy'")
+        }
+
+        require(this::currentClassName.isInitialized)
+
+        imf.setPolicyForMethod(
+            currentClassName, name, signature,
+            policy.withReason(FILTER_REASON)
+        )
+        if (policy == FilterPolicy.Substitute) {
+            val fromName = fields[3].substring(1)
+
+            if (fromName == name) {
+                throw ParseException(
+                    "Substitution must have a different name"
+                )
+            }
+
+            // Set the policy for the "from" method.
+            imf.setPolicyForMethod(
+                currentClassName, fromName, signature,
+                FilterPolicy.Keep.withReason(FILTER_REASON)
+            )
+
+            val classAndMethod = splitWithLastPeriod(fromName)
+            if (classAndMethod != null) {
+                // If the substitution target contains a ".", then
+                // it's a method call redirect.
+                methodReplaceSpec.add(
+                    TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec(
+                        currentClassName.toJvmClassName(),
+                        name,
+                        signature,
+                        classAndMethod.first.toJvmClassName(),
+                        classAndMethod.second,
+                    )
+                )
+            } else {
+                // It's an in-class replace.
+                // ("@RavenwoodReplace" equivalent)
+                imf.setRenameTo(currentClassName, fromName, signature, name)
+            }
+        }
+    }
+
+    private fun parseRename(fields: Array<String>) {
+        if (fields.size < 3) {
+            throw ParseException("Rename ('r') expects 2 fields.")
+        }
+        // Add ".*" to make it a prefix match.
+        val pattern = Pattern.compile(fields[1] + ".*")
+
+        // Removing the leading /'s from the prefix. This allows
+        // using a single '/' as an empty suffix, which is useful to have a
+        // "negative" rename rule to avoid subsequent raname's from getting
+        // applied. (Which is needed for services.jar)
+        val prefix = fields[2].trimStart('/')
+
+        typeRenameSpec += TextFilePolicyRemapperFilter.TypeRenameSpec(
+            pattern, prefix
+        )
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
rename to ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
similarity index 98%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
rename to ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
index ba2c869..1570549 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
@@ -16,7 +16,7 @@
     static_libs: [
         "hoststubgen-annotations",
     ],
-    visibility: ["//frameworks/base/tools/hoststubgen:__subpackages__"],
+    visibility: ["//frameworks/base/ravenwood/tools/hoststubgen:__subpackages__"],
 }
 
 // Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules.
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml
rename to ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
rename to ravenwood/tools/hoststubgen/test-tiny-framework/README.md
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
rename to ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
rename to ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
rename to ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
rename to ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
similarity index 100%
rename from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
rename to ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
diff --git a/services/Android.bp b/services/Android.bp
index 653cd3c3..f04c692 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -195,7 +195,17 @@
     module_type: "java_library",
     config_namespace: "system_services",
     bool_variables: ["without_vibrator"],
-    properties: ["vintf_fragments"],
+    properties: ["vintf_fragment_modules"],
+}
+
+vintf_fragment {
+    name: "manifest_services.xml",
+    src: "manifest_services.xml",
+}
+
+vintf_fragment {
+    name: "manifest_services_android.frameworks.vibrator.xml",
+    src: "manifest_services_android.frameworks.vibrator.xml",
 }
 
 system_java_library {
@@ -264,11 +274,11 @@
 
     soong_config_variables: {
         without_vibrator: {
-            vintf_fragments: [
+            vintf_fragment_modules: [
                 "manifest_services.xml",
             ],
             conditions_default: {
-                vintf_fragments: [
+                vintf_fragment_modules: [
                     "manifest_services.xml",
                     "manifest_services_android.frameworks.vibrator.xml",
                 ],
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
index 0e35a40..4e11750 100644
--- a/services/accessibility/OWNERS
+++ b/services/accessibility/OWNERS
@@ -2,10 +2,11 @@
 
 # Android Accessibility Framework owners
 danielnorman@google.com
-aarmaly@google.com
 chunkulin@google.com
 fuego@google.com
 sallyyuen@google.com
+yingleiw@google.com
+caseyburkhardt@google.com
 
 # Android Accessibility members who have OWNERS but should not be sent
 # day-to-day changes for code review:
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 034127c..cb4e994 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -45,6 +45,16 @@
 }
 
 flag {
+    name: "clear_shortcuts_when_activity_updates_to_service"
+    namespace: "accessibility"
+    description: "When an a11y activity is updated to an a11y service, clears the associated shortcuts so that we don't skip the AccessibilityServiceWarning."
+    bug: "358092445"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "compute_window_changes_on_a11y_v2"
     namespace: "accessibility"
     description: "Computes accessibility window changes in accessibility instead of wm package."
@@ -114,10 +124,13 @@
 }
 
 flag {
-    name: "enable_magnification_follows_mouse"
+    name: "enable_magnification_follows_mouse_bugfix"
     namespace: "accessibility"
     description: "Whether to enable mouse following for fullscreen magnification"
     bug: "354696546"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
 }
 
 flag {
@@ -179,6 +192,16 @@
 }
 
 flag {
+    name: "package_monitor_dedicated_thread"
+    namespace: "accessibility"
+    description: "Runs the A11yManagerService PackageMonitor on a dedicated thread"
+    bug: "348138695"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "manager_package_monitor_logic_fix"
     namespace: "accessibility"
     description: "Corrects the return values of the HandleForceStop function"
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 73b7b35..3441d94 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -35,7 +35,6 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
 
 import static com.android.server.pm.UserManagerService.enforceCurrentUserIfVisibleBackgroundEnabled;
-import static com.android.window.flags.Flags.deleteCaptureDisplay;
 
 import android.accessibilityservice.AccessibilityGestureEvent;
 import android.accessibilityservice.AccessibilityService;
@@ -62,7 +61,6 @@
 import android.graphics.Region;
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerInternal;
 import android.hardware.usb.UsbDevice;
 import android.os.Binder;
 import android.os.Build;
@@ -104,7 +102,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
 import com.android.server.accessibility.magnification.MagnificationProcessor;
 import com.android.server.wm.WindowManagerInternal;
@@ -1513,68 +1510,31 @@
             return;
         }
         final long identity = Binder.clearCallingIdentity();
-        if (deleteCaptureDisplay()) {
-            try {
-                ScreenCapture.ScreenCaptureListener screenCaptureListener = new
-                        ScreenCapture.ScreenCaptureListener(
-                        (screenshotBuffer, result) -> {
-                            if (screenshotBuffer != null && result == 0) {
-                                sendScreenshotSuccess(screenshotBuffer, callback);
-                            } else {
-                                sendScreenshotFailure(
-                                        AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
-                                        callback);
-                            }
+        try {
+            ScreenCapture.ScreenCaptureListener screenCaptureListener = new
+                    ScreenCapture.ScreenCaptureListener(
+                    (screenshotBuffer, result) -> {
+                        if (screenshotBuffer != null && result == 0) {
+                            sendScreenshotSuccess(screenshotBuffer, callback);
+                        } else {
+                            sendScreenshotFailure(
+                                    AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+                                    callback);
                         }
-                );
-                mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
-            } catch (Exception e) {
-                sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
-                        callback);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        } else {
-            try {
-                mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
-                    final ScreenshotHardwareBuffer screenshotBuffer = LocalServices
-                            .getService(DisplayManagerInternal.class).userScreenshot(displayId);
-                    if (screenshotBuffer != null) {
-                        sendScreenshotSuccess(screenshotBuffer, callback);
-                    } else {
-                        sendScreenshotFailure(
-                                AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
-                                callback);
                     }
-                }, null).recycleOnUse());
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
+            );
+            mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
+        } catch (Exception e) {
+            sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
+                    callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
     private void sendScreenshotSuccess(ScreenshotHardwareBuffer screenshotBuffer,
             RemoteCallback callback) {
-        if (deleteCaptureDisplay()) {
-            mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
-                final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
-                final ParcelableColorSpace colorSpace =
-                        new ParcelableColorSpace(screenshotBuffer.getColorSpace());
-
-                final Bundle payload = new Bundle();
-                payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
-                        AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
-                payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
-                        hardwareBuffer);
-                payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
-                payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
-                        SystemClock.uptimeMillis());
-
-                // Send back the result.
-                callback.sendResult(payload);
-                hardwareBuffer.close();
-            }, null).recycleOnUse());
-        } else {
+        mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
             final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
             final ParcelableColorSpace colorSpace =
                     new ParcelableColorSpace(screenshotBuffer.getColorSpace());
@@ -1591,7 +1551,7 @@
             // Send back the result.
             callback.sendResult(payload);
             hardwareBuffer.close();
-        }
+        }, null).recycleOnUse());
     }
 
     private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d595d02..c6fe497 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -116,6 +116,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -901,7 +902,19 @@
     private void registerBroadcastReceivers() {
         // package changes
         mPackageMonitor = new ManagerPackageMonitor(this);
-        mPackageMonitor.register(mContext, null,  UserHandle.ALL, true);
+        final Looper packageMonitorLooper;
+        if (Flags.packageMonitorDedicatedThread()) {
+            // Use a dedicated thread because the default BackgroundThread used by PackageMonitor
+            // is shared by other components and can get busy, causing a delay and eventual ANR when
+            // responding to broadcasts sent to this PackageMonitor.
+            HandlerThread packageMonitorThread = new HandlerThread(LOG_TAG + " PackageMonitor",
+                    Process.THREAD_PRIORITY_BACKGROUND);
+            packageMonitorThread.start();
+            packageMonitorLooper = packageMonitorThread.getLooper();
+        } else {
+            packageMonitorLooper = null;
+        }
+        mPackageMonitor.register(mContext, packageMonitorLooper,  UserHandle.ALL, true);
 
         // user change and unlock
         IntentFilter intentFilter = new IntentFilter();
@@ -2513,6 +2526,19 @@
     private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState,
             List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) {
         if (!parsedAccessibilityShortcutInfos.equals(userState.mInstalledShortcuts)) {
+            if (Flags.clearShortcutsWhenActivityUpdatesToService()) {
+                List<String> componentNames = userState.mInstalledShortcuts.stream()
+                        .filter(a11yActivity ->
+                                !parsedAccessibilityShortcutInfos.contains(a11yActivity))
+                        .map(a11yActivity -> a11yActivity.getComponentName().flattenToString())
+                        .toList();
+                if (!componentNames.isEmpty()) {
+                    enableShortcutsForTargets(
+                            /* enable= */ false, UserShortcutType.ALL,
+                            componentNames, userState.mUserId);
+                }
+            }
+
             userState.mInstalledShortcuts.clear();
             userState.mInstalledShortcuts.addAll(parsedAccessibilityShortcutInfos);
             userState.updateTileServiceMapForAccessibilityActivityLocked();
@@ -3653,6 +3679,12 @@
             return;
         }
 
+        // Magnification connection should not be requested for visible background users.
+        // (b/332222893)
+        if (mUmi.isVisibleBackgroundFullUser(userState.mUserId)) {
+            return;
+        }
+
         final boolean shortcutEnabled = (userState.isShortcutMagnificationEnabledLocked()
                 || userState.isMagnificationSingleFingerTripleTapEnabledLocked()
                 || (Flags.enableMagnificationMultipleFingerMultipleTapGesture()
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 4b97745..1df5d1a 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -138,8 +138,8 @@
         DIAGONAL_UP_LEFT_MOVE(KeyEvent.KEYCODE_7),
         UP_MOVE_OR_SCROLL(KeyEvent.KEYCODE_8),
         DIAGONAL_UP_RIGHT_MOVE(KeyEvent.KEYCODE_9),
-        LEFT_MOVE(KeyEvent.KEYCODE_U),
-        RIGHT_MOVE(KeyEvent.KEYCODE_O),
+        LEFT_MOVE_OR_SCROLL(KeyEvent.KEYCODE_U),
+        RIGHT_MOVE_OR_SCROLL(KeyEvent.KEYCODE_O),
         DIAGONAL_DOWN_LEFT_MOVE(KeyEvent.KEYCODE_J),
         DOWN_MOVE_OR_SCROLL(KeyEvent.KEYCODE_K),
         DIAGONAL_DOWN_RIGHT_MOVE(KeyEvent.KEYCODE_L),
@@ -267,6 +267,16 @@
         );
     }
 
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    private void sendVirtualMouseScrollEvent(float x, float y) {
+        waitForVirtualMouseCreation();
+        mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder()
+                .setXAxisMovement(x)
+                .setYAxisMovement(y)
+                .build()
+        );
+    }
+
     /**
      * Performs a mouse scroll action based on the provided key code.
      * The scroll action will only be performed if the scroll toggle is on.
@@ -284,19 +294,31 @@
     private void performMouseScrollAction(int keyCode) {
         MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
                 keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
-        float y = switch (mouseKeyEvent) {
-            case UP_MOVE_OR_SCROLL -> MOUSE_SCROLL_STEP;
-            case DOWN_MOVE_OR_SCROLL -> -MOUSE_SCROLL_STEP;
-            default -> 0.0f;
-        };
-        waitForVirtualMouseCreation();
-        mVirtualMouse.sendScrollEvent(new VirtualMouseScrollEvent.Builder()
-                .setYAxisMovement(y)
-                .build()
-        );
+        float x = 0f;
+        float y = 0f;
+
+        switch (mouseKeyEvent) {
+            case UP_MOVE_OR_SCROLL -> {
+                y = MOUSE_SCROLL_STEP;
+            }
+            case DOWN_MOVE_OR_SCROLL -> {
+                y = -MOUSE_SCROLL_STEP;
+            }
+            case LEFT_MOVE_OR_SCROLL -> {
+                x = MOUSE_SCROLL_STEP;
+            }
+            case RIGHT_MOVE_OR_SCROLL -> {
+                x = -MOUSE_SCROLL_STEP;
+            }
+            default -> {
+                x = 0.0f;
+                y = 0.0f;
+            }
+        }
+        sendVirtualMouseScrollEvent(x, y);
         if (DEBUG) {
             Slog.d(LOG_TAG, "Performed mouse key event: " + mouseKeyEvent.name()
-                    + " for scroll action with axis movement (y=" + y + ")");
+                    + " for scroll action with axis movement (x=" + x + ", y=" + y + ")");
         }
     }
 
@@ -344,8 +366,8 @@
      * The method calculates the relative movement of the mouse pointer
      * and sends the corresponding event to the virtual mouse.
      *
-     * The UP and DOWN pointer actions will only take place for their respective keys
-     * if the scroll toggle is off.
+     * The UP, DOWN, LEFT, RIGHT  pointer actions will only take place for their
+     * respective keys if the scroll toggle is off.
      *
      * @param keyCode The key code representing the direction or button press.
      *                Supported keys are:
@@ -353,8 +375,8 @@
      *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_DOWN_LEFT_MOVE}
      *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#DOWN_MOVE_OR_SCROLL}
      *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_DOWN_RIGHT_MOVE}
-     *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#LEFT_MOVE}
-     *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#RIGHT_MOVE}
+     *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#LEFT_MOVE_OR_SCROLL}
+     *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#RIGHT_MOVE_OR_SCROLL}
      *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_UP_LEFT_MOVE}
      *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#UP_MOVE_OR_SCROLL}
      *                  <li>{@link MouseKeysInterceptor.MouseKeyEvent#DIAGONAL_UP_RIGHT_MOVE}
@@ -381,10 +403,10 @@
                 x = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2);
                 y = MOUSE_POINTER_MOVEMENT_STEP / sqrt(2);
             }
-            case LEFT_MOVE -> {
+            case LEFT_MOVE_OR_SCROLL -> {
                 x = -MOUSE_POINTER_MOVEMENT_STEP;
             }
-            case RIGHT_MOVE -> {
+            case RIGHT_MOVE_OR_SCROLL -> {
                 x = MOUSE_POINTER_MOVEMENT_STEP;
             }
             case DIAGONAL_UP_LEFT_MOVE -> {
@@ -424,7 +446,9 @@
 
     private boolean isMouseScrollKey(int keyCode, InputDevice inputDevice) {
         return keyCode == MouseKeyEvent.UP_MOVE_OR_SCROLL.getKeyCode(inputDevice)
-                || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCode(inputDevice);
+                || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCode(inputDevice)
+                || keyCode == MouseKeyEvent.LEFT_MOVE_OR_SCROLL.getKeyCode(inputDevice)
+                || keyCode == MouseKeyEvent.RIGHT_MOVE_OR_SCROLL.getKeyCode(inputDevice);
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index a19fddd..963334b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -345,7 +345,7 @@
 
     @Override
     void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        if (Flags.enableMagnificationFollowsMouse()) {
+        if (Flags.enableMagnificationFollowsMouseBugfix()) {
             if (mFullScreenMagnificationController.isActivated(mDisplayId)) {
                 // TODO(b/354696546): Allow mouse/stylus to activate whichever display they are
                 // over, rather than only interacting with the current display.
@@ -1206,7 +1206,7 @@
 
         protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
                 int policyFlags) {
-            if (Flags.enableMagnificationFollowsMouse()
+            if (Flags.enableMagnificationFollowsMouseBugfix()
                     && !event.isFromSource(SOURCE_TOUCHSCREEN)) {
                 // Only touch events need to be cached and sent later.
                 return;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 19e3e69..fe06406 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -19,6 +19,7 @@
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION;
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION_CALLBACK;
 import static android.os.Build.HW_TIMEOUT_MULTIPLIER;
+import static android.os.UserHandle.getCallingUserId;
 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
 
 import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
@@ -54,6 +55,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -209,6 +211,7 @@
     private final Callback mCallback;
     private final AccessibilityTraceManager mTrace;
     private final MagnificationScaleProvider mScaleProvider;
+    private final UserManagerInternal mUserManagerInternal;
 
     public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback,
             AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
@@ -217,6 +220,7 @@
         mCallback = callback;
         mTrace = trace;
         mScaleProvider = scaleProvider;
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
     }
 
     /**
@@ -280,12 +284,18 @@
      * Requests {@link IMagnificationConnection} through
      * {@link StatusBarManagerInternal#requestMagnificationConnection(boolean)} and
      * destroys all window magnifications if necessary.
+     * NOTE: Currently, this is not allowed to call from visible background users.(b/332222893)
      *
      * @param connect {@code true} if needs connection, otherwise set the connection to null and
      *                destroy all window magnifications.
      * @return {@code true} if {@link IMagnificationConnection} state is going to change.
      */
     public boolean requestConnection(boolean connect) {
+        final int callingUserId = getCallingUserId();
+        if (mUserManagerInternal.isVisibleBackgroundFullUser(callingUserId)) {
+            throw new SecurityException("Visible background user(u" + callingUserId
+                    + " is not permitted to request magnification connection.");
+        }
         if (DBG) {
             Slog.d(TAG, "requestConnection :" + connect);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index 446123f..fa86ba3 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -146,7 +146,8 @@
             } break;
             case SOURCE_MOUSE:
             case SOURCE_STYLUS: {
-                if (magnificationShortcutExists() && Flags.enableMagnificationFollowsMouse()) {
+                if (magnificationShortcutExists()
+                        && Flags.enableMagnificationFollowsMouseBugfix()) {
                     handleMouseOrStylusEvent(event, rawEvent, policyFlags);
                 }
             }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionDumpHelper.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionDumpHelper.java
new file mode 100644
index 0000000..a832545
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionDumpHelper.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appfunctions;
+
+import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_INDEXER_PACKAGE;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.PROPERTY_FUNCTION_ID;
+
+import android.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.RequiresPermission;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.util.IndentingPrintWriter;
+import android.app.appfunctions.AppFunctionRuntimeMetadata;
+import android.app.appfunctions.AppFunctionStaticMetadataHelper;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchManager.SearchContext;
+import android.app.appsearch.JoinSpec;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchSpec;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public final class AppFunctionDumpHelper {
+    private static final String TAG = AppFunctionDumpHelper.class.getSimpleName();
+
+    private AppFunctionDumpHelper() {}
+
+    /** Dumps the state of all app functions for all users. */
+    @BinderThread
+    @RequiresPermission(
+            anyOf = {Manifest.permission.CREATE_USERS, Manifest.permission.MANAGE_USERS})
+    public static void dumpAppFunctionsState(@NonNull Context context, @NonNull PrintWriter w) {
+        UserManager userManager = context.getSystemService(UserManager.class);
+        if (userManager == null) {
+            w.println("Couldn't retrieve UserManager.");
+            return;
+        }
+
+        IndentingPrintWriter pw = new IndentingPrintWriter(w);
+
+        List<UserInfo> userInfos = userManager.getAliveUsers();
+        for (UserInfo userInfo : userInfos) {
+            pw.println(
+                    "AppFunction state for user " + userInfo.getUserHandle().getIdentifier() + ":");
+            pw.increaseIndent();
+            dumpAppFunctionsStateForUser(
+                    context.createContextAsUser(userInfo.getUserHandle(), /* flags= */ 0), pw);
+            pw.decreaseIndent();
+        }
+    }
+
+    private static void dumpAppFunctionsStateForUser(
+            @NonNull Context context, @NonNull IndentingPrintWriter pw) {
+        AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
+        if (appSearchManager == null) {
+            pw.println("Couldn't retrieve AppSearchManager.");
+            return;
+        }
+
+        try (FutureGlobalSearchSession searchSession =
+                new FutureGlobalSearchSession(appSearchManager, Runnable::run)) {
+            pw.println();
+
+            try (FutureSearchResults futureSearchResults =
+                    searchSession.search("", buildAppFunctionMetadataSearchSpec()).get(); ) {
+                List<SearchResult> searchResultsList;
+                do {
+                    searchResultsList = futureSearchResults.getNextPage().get();
+                    for (SearchResult searchResult : searchResultsList) {
+                        dumpAppFunctionMetadata(pw, searchResult);
+                    }
+                } while (!searchResultsList.isEmpty());
+            }
+
+        } catch (Exception e) {
+            pw.println("Failed to dump AppFunction state: " + e);
+        }
+    }
+
+    private static SearchSpec buildAppFunctionMetadataSearchSpec() {
+        SearchSpec runtimeMetadataSearchSpec =
+                new SearchSpec.Builder()
+                        .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+                        .addFilterSchemas(AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE)
+                        .build();
+        JoinSpec joinSpec =
+                new JoinSpec.Builder(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+                        .setNestedSearch(/* queryExpression= */ "", runtimeMetadataSearchSpec)
+                        .build();
+
+        return new SearchSpec.Builder()
+                .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+                .addFilterSchemas(AppFunctionStaticMetadataHelper.STATIC_SCHEMA_TYPE)
+                .setJoinSpec(joinSpec)
+                .build();
+    }
+
+    private static void dumpAppFunctionMetadata(
+            IndentingPrintWriter pw, SearchResult joinedSearchResult) {
+        pw.println(
+                "AppFunctionMetadata for: "
+                        + joinedSearchResult
+                                .getGenericDocument()
+                                .getPropertyString(PROPERTY_FUNCTION_ID));
+        pw.increaseIndent();
+
+        pw.println("Static Metadata:");
+        pw.increaseIndent();
+        writeGenericDocumentProperties(pw, joinedSearchResult.getGenericDocument());
+        pw.decreaseIndent();
+
+        pw.println("Runtime Metadata:");
+        pw.increaseIndent();
+        if (!joinedSearchResult.getJoinedResults().isEmpty()) {
+            writeGenericDocumentProperties(
+                    pw, joinedSearchResult.getJoinedResults().getFirst().getGenericDocument());
+        } else {
+            pw.println("No runtime metadata found.");
+        }
+        pw.decreaseIndent();
+
+        pw.decreaseIndent();
+    }
+
+    private static void writeGenericDocumentProperties(
+            IndentingPrintWriter pw, GenericDocument genericDocument) {
+        Set<String> propertyNames = genericDocument.getPropertyNames();
+        pw.println("{");
+        pw.increaseIndent();
+        for (String propertyName : propertyNames) {
+            Object propertyValue = genericDocument.getProperty(propertyName);
+            pw.print("\"" + propertyName + "\"" + ": [");
+
+            if (propertyValue instanceof GenericDocument[]) {
+                GenericDocument[] documentValues = (GenericDocument[]) propertyValue;
+                for (int i = 0; i < documentValues.length; i++) {
+                    GenericDocument documentValue = documentValues[i];
+                    writeGenericDocumentProperties(pw, documentValue);
+                    if (i != documentValues.length - 1) {
+                        pw.print(", ");
+                    }
+                    pw.println();
+                }
+            } else {
+                int propertyArrLength = Array.getLength(propertyValue);
+                for (int i = 0; i < propertyArrLength; i++) {
+                    Object propertyElement = Array.get(propertyValue, i);
+                    if (propertyElement instanceof String) {
+                        pw.print("\"" + propertyElement + "\"");
+                    } else if (propertyElement instanceof byte[]) {
+                        pw.print(Arrays.toString((byte[]) propertyElement));
+                    } else if (propertyElement != null) {
+                        pw.print(propertyElement.toString());
+                    }
+                    if (i != propertyArrLength - 1) {
+                        pw.print(", ");
+                    }
+                }
+            }
+            pw.println("]");
+        }
+        pw.decreaseIndent();
+        pw.println("}");
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
index c3b7087..44ae1d1 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
@@ -31,7 +31,8 @@
                     /* maxConcurrency= */ Runtime.getRuntime().availableProcessors(),
                     /* keepAliveTime= */ 0L,
                     /* unit= */ TimeUnit.SECONDS,
-                    /* workQueue= */ new LinkedBlockingQueue<>());
+                    /* workQueue= */ new LinkedBlockingQueue<>(),
+                    new NamedThreadFactory("AppFunctionExecutors"));
 
     private AppFunctionExecutors() {}
 }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index d31ced3..89f14b0 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -63,11 +63,15 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.DumpUtils;
 import com.android.server.SystemService.TargetUser;
-import com.android.server.appfunctions.RemoteServiceCaller.RunServiceCallCallback;
-import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
+import java.util.WeakHashMap;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.Executor;
 
@@ -80,7 +84,8 @@
     private final ServiceHelper mInternalServiceHelper;
     private final ServiceConfig mServiceConfig;
     private final Context mContext;
-    private final Object mLock = new Object();
+    private final Map<String, Object> mLocks = new WeakHashMap<>();
+
 
     public AppFunctionManagerServiceImpl(@NonNull Context context) {
         this(
@@ -122,6 +127,20 @@
     }
 
     @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            AppFunctionDumpHelper.dumpAppFunctionsState(mContext, pw);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
     public ICancellationSignal executeAppFunction(
             @NonNull ExecuteAppFunctionAidlRequest requestInternal,
             @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) {
@@ -210,12 +229,9 @@
                 .thenAccept(
                         canExecute -> {
                             if (!canExecute) {
-                                safeExecuteAppFunctionCallback.onResult(
-                                        ExecuteAppFunctionResponse.newFailure(
-                                                ExecuteAppFunctionResponse.RESULT_DENIED,
-                                                "Caller does not have permission to execute the"
-                                                        + " appfunction",
-                                                /* extras= */ null));
+                                throw new SecurityException(
+                                        "Caller does not have permission to execute the"
+                                                + " appfunction");
                             }
                         })
                 .thenCompose(
@@ -304,9 +320,7 @@
         THREAD_POOL_EXECUTOR.execute(
                 () -> {
                     try {
-                        // TODO(357551503): Instead of holding a global lock, hold a per-package
-                        //  lock.
-                        synchronized (mLock) {
+                        synchronized (getLockForPackage(callingPackage)) {
                             setAppFunctionEnabledInternalLocked(
                                     callingPackage, functionIdentifier, userHandle, enabledState);
                         }
@@ -334,7 +348,7 @@
      * process.
      */
     @WorkerThread
-    @GuardedBy("mLock")
+    @GuardedBy("getLockForPackage(callingPackage)")
     private void setAppFunctionEnabledInternalLocked(
             @NonNull String callingPackage,
             @NonNull String functionIdentifier,
@@ -363,7 +377,8 @@
                                     runtimeMetadataSearchSession));
             AppFunctionRuntimeMetadata newMetadata =
                     new AppFunctionRuntimeMetadata.Builder(existingMetadata)
-                            .setEnabled(enabledState).build();
+                            .setEnabled(enabledState)
+                            .build();
             AppSearchBatchResult<String, Void> putDocumentBatchResult =
                     runtimeMetadataSearchSession
                             .put(
@@ -424,7 +439,7 @@
                         targetUser,
                         mServiceConfig.getExecuteAppFunctionCancellationTimeoutMillis(),
                         cancellationSignal,
-                        RunAppFunctionServiceCallback.create(
+                        new RunAppFunctionServiceCallback(
                                 requestInternal,
                                 cancellationCallback,
                                 safeExecuteAppFunctionCallback),
@@ -528,6 +543,26 @@
                                     });
         }
     }
+    /**
+     * Retrieves the lock object associated with the given package name.
+     *
+     * This method returns the lock object from the {@code mLocks} map if it exists.
+     * If no lock is found for the given package name, a new lock object is created,
+     * stored in the map, and returned.
+     */
+    @VisibleForTesting
+    @NonNull
+    Object getLockForPackage(String callingPackage) {
+        // Synchronized the access to mLocks to prevent race condition.
+        synchronized (mLocks) {
+            // By using a WeakHashMap, we allow the garbage collector to reclaim memory by removing
+            // entries associated with unused callingPackage keys. Therefore, we remove the null
+            // values before getting/computing a new value. The goal is to not let the size of this
+            // map grow without an upper bound.
+            mLocks.values().removeAll(Collections.singleton(null)); // Remove null values
+            return mLocks.computeIfAbsent(callingPackage, k -> new Object());
+        }
+    }
 
     private static class AppFunctionMetadataObserver implements ObserverCallback {
         @Nullable private final MetadataSyncAdapter mPerUserMetadataSyncAdapter;
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
index de2034b..b89348c 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
@@ -39,20 +39,6 @@
 /** A future API wrapper of {@link AppSearchSession} APIs. */
 public interface FutureAppSearchSession extends Closeable {
 
-    /** Converts a failed app search result codes into an exception. */
-    @NonNull
-    static Exception failedResultToException(@NonNull AppSearchResult<?> appSearchResult) {
-        return switch (appSearchResult.getResultCode()) {
-            case AppSearchResult.RESULT_INVALID_ARGUMENT ->
-                    new IllegalArgumentException(appSearchResult.getErrorMessage());
-            case AppSearchResult.RESULT_IO_ERROR ->
-                    new IOException(appSearchResult.getErrorMessage());
-            case AppSearchResult.RESULT_SECURITY_ERROR ->
-                    new SecurityException(appSearchResult.getErrorMessage());
-            default -> new IllegalStateException(appSearchResult.getErrorMessage());
-        };
-    }
-
     /**
      * Sets the schema that represents the organizational structure of data within the AppSearch
      * database.
@@ -86,17 +72,4 @@
 
     @Override
     void close();
-
-    /** A future API wrapper of {@link android.app.appsearch.SearchResults}. */
-    interface FutureSearchResults {
-
-        /**
-         * Retrieves the next page of {@link SearchResult} objects from the {@link AppSearchSession}
-         * database.
-         *
-         * <p>Continue calling this method to access results until it returns an empty list,
-         * signifying there are no more results.
-         */
-        AndroidFuture<List<SearchResult>> getNextPage();
-    }
 }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
index d24bb87..87589f5 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
@@ -16,7 +16,7 @@
 
 package com.android.server.appfunctions;
 
-import static com.android.server.appfunctions.FutureAppSearchSession.failedResultToException;
+import static com.android.server.appfunctions.FutureSearchResults.failedResultToException;
 
 import android.annotation.NonNull;
 import android.app.appsearch.AppSearchBatchResult;
@@ -192,33 +192,6 @@
                         });
     }
 
-    private static final class FutureSearchResultsImpl implements FutureSearchResults {
-        private final SearchResults mSearchResults;
-        private final Executor mExecutor;
-
-        private FutureSearchResultsImpl(
-                @NonNull SearchResults searchResults, @NonNull Executor executor) {
-            this.mSearchResults = searchResults;
-            this.mExecutor = executor;
-        }
-
-        @Override
-        public AndroidFuture<List<SearchResult>> getNextPage() {
-            AndroidFuture<AppSearchResult<List<SearchResult>>> nextPageFuture =
-                    new AndroidFuture<>();
-
-            mSearchResults.getNextPage(mExecutor, nextPageFuture::complete);
-            return nextPageFuture.thenApply(
-                    result -> {
-                        if (result.isSuccess()) {
-                            return result.getResultValue();
-                        } else {
-                            throw new RuntimeException(failedResultToException(result));
-                        }
-                    });
-        }
-    }
-
     private static final class BatchResultCallbackAdapter<K, V>
             implements BatchResultCallback<K, V> {
         private final AndroidFuture<AppSearchBatchResult<K, V>> mFuture;
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
index 874c5da..4cc0817 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
@@ -20,6 +20,7 @@
 import android.app.appsearch.AppSearchManager;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.GlobalSearchSession;
+import android.app.appsearch.SearchSpec;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.app.appsearch.observer.ObserverCallback;
 import android.app.appsearch.observer.ObserverSpec;
@@ -49,12 +50,23 @@
                         return result.getResultValue();
                     } else {
                         throw new RuntimeException(
-                                FutureAppSearchSession.failedResultToException(result));
+                                FutureSearchResults.failedResultToException(result));
                     }
                 });
     }
 
     /**
+     * Retrieves documents from the open {@link GlobalSearchSession} that match a given query string
+     * and type of search provided.
+     */
+    public AndroidFuture<FutureSearchResults> search(
+            String queryExpression, SearchSpec searchSpec) {
+        return getSessionAsync()
+                .thenApply(session -> session.search(queryExpression, searchSpec))
+                .thenApply(result -> new FutureSearchResultsImpl(result, mExecutor));
+    }
+
+    /**
      * Registers an observer callback for the given target package name.
      *
      * @param targetPackageName The package name of the target app.
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureSearchResults.java b/services/appfunctions/java/com/android/server/appfunctions/FutureSearchResults.java
new file mode 100644
index 0000000..c38ff14
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureSearchResults.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.List;
+
+/** A future API wrapper of {@link android.app.appsearch.SearchResults}. */
+public interface FutureSearchResults extends Closeable {
+
+    /** Converts a failed app search result codes into an exception. */
+    @NonNull
+    public static Exception failedResultToException(@NonNull AppSearchResult<?> appSearchResult) {
+        return switch (appSearchResult.getResultCode()) {
+            case AppSearchResult.RESULT_INVALID_ARGUMENT ->
+                    new IllegalArgumentException(appSearchResult.getErrorMessage());
+            case AppSearchResult.RESULT_IO_ERROR ->
+                    new IOException(appSearchResult.getErrorMessage());
+            case AppSearchResult.RESULT_SECURITY_ERROR ->
+                    new SecurityException(appSearchResult.getErrorMessage());
+            default -> new IllegalStateException(appSearchResult.getErrorMessage());
+        };
+    }
+
+    /**
+     * Retrieves the next page of {@link SearchResult} objects from the {@link AppSearchSession}
+     * database.
+     *
+     * <p>Continue calling this method to access results until it returns an empty list, signifying
+     * there are no more results.
+     */
+    AndroidFuture<List<SearchResult>> getNextPage();
+
+    @Override
+    void close();
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureSearchResultsImpl.java b/services/appfunctions/java/com/android/server/appfunctions/FutureSearchResultsImpl.java
new file mode 100644
index 0000000..c8bc538
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureSearchResultsImpl.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class FutureSearchResultsImpl implements FutureSearchResults {
+    private final SearchResults mSearchResults;
+    private final Executor mExecutor;
+
+    public FutureSearchResultsImpl(
+            @NonNull SearchResults searchResults, @NonNull Executor executor) {
+        this.mSearchResults = searchResults;
+        this.mExecutor = executor;
+    }
+
+    @Override
+    public AndroidFuture<List<SearchResult>> getNextPage() {
+        AndroidFuture<AppSearchResult<List<SearchResult>>> nextPageFuture = new AndroidFuture<>();
+
+        mSearchResults.getNextPage(mExecutor, nextPageFuture::complete);
+        return nextPageFuture
+                .thenApply(
+                        result -> {
+                            if (result.isSuccess()) {
+                                return result.getResultValue();
+                            } else {
+                                throw new RuntimeException(
+                                        FutureSearchResults.failedResultToException(result));
+                            }
+                        });
+    }
+
+    @Override
+    public void close() {
+        mSearchResults.close();
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index d84b205..cc73288 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -45,7 +45,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
-import com.android.server.appfunctions.FutureAppSearchSession.FutureSearchResults;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -85,7 +84,9 @@
             @NonNull PackageManager packageManager, @NonNull AppSearchManager appSearchManager) {
         mPackageManager = Objects.requireNonNull(packageManager);
         mAppSearchManager = Objects.requireNonNull(appSearchManager);
-        mExecutor = Executors.newSingleThreadExecutor();
+        mExecutor =
+                Executors.newSingleThreadExecutor(
+                        new NamedThreadFactory("AppFunctionSyncExecutors"));
     }
 
     /**
@@ -421,26 +422,29 @@
         Objects.requireNonNull(propertyPackageName);
         ArrayMap<String, ArraySet<String>> packageToFunctionIds = new ArrayMap<>();
 
-        FutureSearchResults futureSearchResults =
+        try (FutureSearchResults futureSearchResults =
                 searchSession
                         .search(
                                 "",
                                 buildMetadataSearchSpec(
                                         schemaType, propertyFunctionId, propertyPackageName))
-                        .get();
-        List<SearchResult> searchResultsList = futureSearchResults.getNextPage().get();
-        // TODO(b/357551503): This could be expensive if we have more functions
-        while (!searchResultsList.isEmpty()) {
-            for (SearchResult searchResult : searchResultsList) {
-                String packageName =
-                        searchResult.getGenericDocument().getPropertyString(propertyPackageName);
-                String functionId =
-                        searchResult.getGenericDocument().getPropertyString(propertyFunctionId);
-                packageToFunctionIds
-                        .computeIfAbsent(packageName, k -> new ArraySet<>())
-                        .add(functionId);
+                        .get(); ) {
+            List<SearchResult> searchResultsList = futureSearchResults.getNextPage().get();
+            // TODO(b/357551503): This could be expensive if we have more functions
+            while (!searchResultsList.isEmpty()) {
+                for (SearchResult searchResult : searchResultsList) {
+                    String packageName =
+                            searchResult
+                                    .getGenericDocument()
+                                    .getPropertyString(propertyPackageName);
+                    String functionId =
+                            searchResult.getGenericDocument().getPropertyString(propertyFunctionId);
+                    packageToFunctionIds
+                            .computeIfAbsent(packageName, k -> new ArraySet<>())
+                            .add(functionId);
+                }
+                searchResultsList = futureSearchResults.getNextPage().get();
             }
-            searchResultsList = futureSearchResults.getNextPage().get();
         }
         return packageToFunctionIds;
     }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/NamedThreadFactory.java b/services/appfunctions/java/com/android/server/appfunctions/NamedThreadFactory.java
new file mode 100644
index 0000000..7adcc48
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/NamedThreadFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appfunctions;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** A {@link ThreadFactory} that creates threads with a given base name. */
+public class NamedThreadFactory implements ThreadFactory {
+    private final ThreadFactory mDefaultThreadFactory;
+    private final String mBaseName;
+    private final AtomicInteger mCount = new AtomicInteger(0);
+
+    public NamedThreadFactory(final String baseName) {
+        mDefaultThreadFactory = Executors.defaultThreadFactory();
+        mBaseName = baseName;
+    }
+
+    @Override
+    public Thread newThread(Runnable runnable) {
+        final Thread thread = mDefaultThreadFactory.newThread(runnable);
+        thread.setName(mBaseName + "-" + mCount.getAndIncrement());
+        return thread;
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
index 7820390..129be65 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
@@ -27,17 +27,17 @@
 import com.android.server.appfunctions.RemoteServiceCaller.RunServiceCallCallback;
 import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
 
-
 /**
  * A callback to forward a request to the {@link IAppFunctionService} and report back the result.
  */
 public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAppFunctionService> {
+    private static final String TAG = RunAppFunctionServiceCallback.class.getSimpleName();
 
     private final ExecuteAppFunctionAidlRequest mRequestInternal;
     private final SafeOneTimeExecuteAppFunctionCallback mSafeExecuteAppFunctionCallback;
     private final ICancellationCallback mCancellationCallback;
 
-    private RunAppFunctionServiceCallback(
+    public RunAppFunctionServiceCallback(
             ExecuteAppFunctionAidlRequest requestInternal,
             ICancellationCallback cancellationCallback,
             SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
@@ -46,21 +46,6 @@
         this.mCancellationCallback = cancellationCallback;
     }
 
-    /**
-     * Creates a new instance of {@link RunAppFunctionServiceCallback}.
-     *
-     * @param requestInternal a request to send to the service.
-     * @param cancellationCallback a callback to forward cancellation signal to the service.
-     * @param safeExecuteAppFunctionCallback a callback to report back the result of the operation.
-     */
-    public static RunAppFunctionServiceCallback create(
-            ExecuteAppFunctionAidlRequest requestInternal,
-            ICancellationCallback cancellationCallback,
-            SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
-        return new RunAppFunctionServiceCallback(
-                requestInternal, cancellationCallback, safeExecuteAppFunctionCallback);
-    }
-
     @Override
     public void onServiceConnected(
             @NonNull IAppFunctionService service,
@@ -68,6 +53,7 @@
         try {
             service.executeAppFunction(
                     mRequestInternal.getClientRequest(),
+                    mRequestInternal.getCallingPackage(),
                     mCancellationCallback,
                     new IExecuteAppFunctionCallback.Stub() {
                         @Override
@@ -88,7 +74,7 @@
 
     @Override
     public void onFailedToConnect() {
-        Slog.e("AppFunctionManagerServiceImpl", "Failed to connect to service");
+        Slog.e(TAG, "Failed to connect to service");
         mSafeExecuteAppFunctionCallback.onResult(
                 ExecuteAppFunctionResponse.newFailure(
                         ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ba84485..1803424 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -49,3 +49,10 @@
   description: "Include the session id into the FillEventHistory events as part of ClientState"
   bug: "333927465"
 }
+
+flag {
+  name: "highlight_autofill_single_field"
+  namespace: "autofill"
+  description: "Highlight single field after autofill selection"
+  bug: "41496744"
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 2fa0e0d..8f12b1d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -39,6 +39,7 @@
 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
 import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+import static android.service.autofill.Flags.highlightAutofillSingleField;
 import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
 import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
@@ -49,7 +50,6 @@
 import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
-
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED;
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
@@ -104,7 +104,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Activity;
 import android.app.ActivityTaskManager;
 import android.app.IAssistDataReceiver;
 import android.app.PendingIntent;
@@ -143,7 +142,6 @@
 import android.service.assist.classification.FieldClassificationRequest;
 import android.service.assist.classification.FieldClassificationResponse;
 import android.service.autofill.AutofillFieldClassificationService.Scores;
-import android.service.autofill.AutofillService;
 import android.service.autofill.CompositeUserData;
 import android.service.autofill.ConvertCredentialResponse;
 import android.service.autofill.Dataset;
@@ -186,7 +184,6 @@
 import android.view.autofill.IAutofillWindowPresenter;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.widget.RemoteViews;
-
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -198,7 +195,6 @@
 import com.android.server.autofill.ui.PendingUi;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
-
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -222,18 +218,21 @@
 /**
  * A session for a given activity.
  *
- * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track
- * of the current {@link ViewState} to display the appropriate UI.
+ * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track of
+ * the current {@link ViewState} to display the appropriate UI.
  *
- * <p>Although the autofill requests and callbacks are stateless from the service's point of
- * view, we need to keep state in the framework side for cases such as authentication. For
- * example, when service return a {@link FillResponse} that contains all the fields needed
- * to fill the activity but it requires authentication first, that response need to be held
- * until the user authenticates or it times out.
+ * <p>Although the autofill requests and callbacks are stateless from the service's point of view,
+ * we need to keep state in the framework side for cases such as authentication. For example, when
+ * service return a {@link FillResponse} that contains all the fields needed to fill the activity
+ * but it requires authentication first, that response need to be held until the user authenticates
+ * or it times out.
  */
-final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
-        AutoFillUI.AutoFillUiCallback, ValueFinder,
-        RemoteFieldClassificationService.FieldClassificationServiceCallbacks {
+final class Session
+        implements RemoteFillService.FillServiceCallbacks,
+                ViewState.Listener,
+                AutoFillUI.AutoFillUiCallback,
+                ValueFinder,
+                RemoteFieldClassificationService.FieldClassificationServiceCallbacks {
     private static final String TAG = "AutofillSession";
 
     // This should never be true in production. This is only for local debugging.
@@ -257,6 +256,7 @@
     private final AutofillManagerServiceImpl mService;
     private final Handler mHandler;
     private final AutoFillUI mUi;
+
     /**
      * Context associated with the session, it has the same {@link Context#getDisplayId() displayId}
      * of the activity being autofilled.
@@ -286,14 +286,11 @@
     /** Session is destroyed and removed from the manager service. */
     public static final int STATE_REMOVED = 3;
 
-    @IntDef(prefix = { "STATE_" }, value = {
-            STATE_UNKNOWN,
-            STATE_ACTIVE,
-            STATE_FINISHED,
-            STATE_REMOVED
-    })
+    @IntDef(
+            prefix = {"STATE_"},
+            value = {STATE_UNKNOWN, STATE_ACTIVE, STATE_FINISHED, STATE_REMOVED})
     @Retention(RetentionPolicy.SOURCE)
-    @interface SessionState{}
+    @interface SessionState {}
 
     @GuardedBy("mLock")
     private final SessionFlags mSessionFlags;
@@ -318,7 +315,8 @@
     public final int mFlags;
 
     @GuardedBy("mLock")
-    @NonNull private IBinder mActivityToken;
+    @NonNull
+    private IBinder mActivityToken;
 
     /** The app activity that's being autofilled */
     @NonNull private final ComponentName mComponentName;
@@ -341,11 +339,10 @@
      * autofill.
      */
     @GuardedBy("mLock")
-    @Nullable private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest;
+    @Nullable
+    private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest;
 
-    /**
-     * Id of the View currently being displayed.
-     */
+    /** Id of the View currently being displayed. */
     @GuardedBy("mLock")
     private @Nullable AutofillId mCurrentViewId;
 
@@ -363,8 +360,7 @@
      *
      * <p>Only {@code null} when the session is for augmented autofill only.
      */
-    @Nullable
-    private final RemoteFillService mRemoteFillService;
+    @Nullable private final RemoteFillService mRemoteFillService;
 
     /**
      * With the credman integration, Autofill Framework handles two types of autofill flows -
@@ -379,8 +375,7 @@
      * service the session was initially created with, the secondary provider handler will contain
      * the remaining autofill service.
      */
-    @Nullable
-    private final SecondaryProviderHandler mSecondaryProviderHandler;
+    @Nullable private final SecondaryProviderHandler mSecondaryProviderHandler;
 
     @GuardedBy("mLock")
     private SparseArray<FillResponse> mResponses;
@@ -395,9 +390,7 @@
     @GuardedBy("mLock")
     private ArrayList<FillContext> mContexts;
 
-    /**
-     * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}.
-     */
+    /** Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. */
     private boolean mHasCallback;
 
     /** Whether the session has credential manager provider as the primary provider. */
@@ -426,32 +419,24 @@
     @GuardedBy("mLock")
     private PendingUi mPendingSaveUi;
 
-    /**
-     * List of dataset ids selected by the user.
-     */
+    /** List of dataset ids selected by the user. */
     @GuardedBy("mLock")
     private ArrayList<String> mSelectedDatasetIds;
 
-    /**
-     * When the session started (using elapsed time since boot).
-     */
+    /** When the session started (using elapsed time since boot). */
     private final long mStartTime;
 
-    /**
-     * Count of FillRequests in the session.
-     */
+    /** Count of FillRequests in the session. */
     private int mRequestCount;
 
     /**
-     * Starting timestamp of latency logger.
-     * This is set when Session created or when the view is reset.
+     * Starting timestamp of latency logger. This is set when Session created or when the view is
+     * reset.
      */
     @GuardedBy("mLock")
     private long mLatencyBaseTime;
 
-    /**
-     * When the UI was shown for the first time (using elapsed time since boot).
-     */
+    /** When the UI was shown for the first time (using elapsed time since boot). */
     @GuardedBy("mLock")
     private long mUiShownTime;
 
@@ -475,15 +460,11 @@
     @GuardedBy("mLock")
     private final LocalLog mWtfHistory;
 
-    /**
-     * Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id.
-     */
+    /** Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. */
     @GuardedBy("mLock")
     private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1);
 
-    /**
-     * Destroys the augmented Autofill UI.
-     */
+    /** Destroys the augmented Autofill UI. */
     // TODO(b/123099468): this runnable is called when the Autofill session is destroyed, the
     // main reason being the cases where user tap HOME.
     // Right now it's completely destroying the UI, but we need to decide whether / how to
@@ -494,13 +475,10 @@
     @Nullable
     private Runnable mAugmentedAutofillDestroyer;
 
-    /**
-     * List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics.
-     */
+    /** List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. */
     @GuardedBy("mLock")
     private ArrayList<LogMaker> mAugmentedRequestsLogs;
 
-
     /**
      * List of autofill ids of autofillable fields present in the AssistStructure that can be used
      * to trigger new augmented autofill requests (because the "standard" service was not interested
@@ -509,23 +487,17 @@
     @GuardedBy("mLock")
     private ArrayList<AutofillId> mAugmentedAutofillableIds;
 
-    @NonNull
-    final AutofillInlineSessionController mInlineSessionController;
+    @NonNull final AutofillInlineSessionController mInlineSessionController;
 
-    /**
-     * Receiver of assist data from the app's {@link Activity}.
-     */
+    /** Receiver of assist data from the app's {@link Activity}. */
     private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
 
-    /**
-     * Receiver of assist data for pcc purpose
-     */
+    /** Receiver of assist data for pcc purpose */
     private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl();
 
     private final ClassificationState mClassificationState = new ClassificationState();
 
-    @Nullable
-    private final ComponentName mCredentialAutofillService;
+    @Nullable private final ComponentName mCredentialAutofillService;
 
     // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
     // new one per Session.
@@ -547,7 +519,10 @@
                     Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received");
                     synchronized (mLock) {
                         int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0);
-                        FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE, android.service.autofill.FillResponse.class);
+                        FillResponse response =
+                                intent.getParcelableExtra(
+                                        EXTRA_FILL_RESPONSE,
+                                        android.service.autofill.FillResponse.class);
                         mFillRequestEventLogger.maybeSetRequestTriggerReason(
                                 TRIGGER_REASON_RETRIGGER);
                         mAssistReceiver.processDelayedFillLocked(requestId, response);
@@ -575,28 +550,25 @@
     @GuardedBy("mLock")
     private SessionCommittedEventLogger mSessionCommittedEventLogger;
 
-    /**
-     * Fill dialog request would likely be sent slightly later.
-     */
+    /** Fill dialog request would likely be sent slightly later. */
     @NonNull
     @GuardedBy("mLock")
     private boolean mPreviouslyFillDialogPotentiallyStarted;
 
     /**
-     * Keeps track of if the user entered view, this is used to
-     * distinguish Fill Request that did not have user interaction
-     * with ones that did.
+     * Keeps track of if the user entered view, this is used to distinguish Fill Request that did
+     * not have user interaction with ones that did.
      *
-     * This is set to true when entering view - after FillDialog FillRequest
-     * or on plain user tap.
+     * <p>This is set to true when entering view - after FillDialog FillRequest or on plain user
+     * tap.
      */
     @NonNull
     @GuardedBy("mLock")
     private boolean mLogViewEntered;
 
     /**
-     * Keeps the fill dialog trigger ids of the last response. This invalidates
-     * the trigger ids of the previous response.
+     * Keeps the fill dialog trigger ids of the last response. This invalidates the trigger ids of
+     * the previous response.
      */
     @Nullable
     @GuardedBy("mLock")
@@ -662,9 +634,7 @@
         return false;
     }
 
-    /**
-     * Collection of flags/booleans that helps determine Session behaviors.
-     */
+    /** Collection of flags/booleans that helps determine Session behaviors. */
     private final class SessionFlags {
         /** Whether autofill is disabled by the service */
         private boolean mAutofillDisabled;
@@ -695,31 +665,35 @@
     final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
         @GuardedBy("mLock")
         private boolean mWaitForInlineRequest;
+
         @GuardedBy("mLock")
         private InlineSuggestionsRequest mPendingInlineSuggestionsRequest;
+
         @GuardedBy("mLock")
         private FillRequest mPendingFillRequest;
+
         @GuardedBy("mLock")
         private FillRequest mLastFillRequest;
 
-        @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState,
-                boolean isInlineRequest) {
+        @Nullable
+        Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(
+                ViewState viewState, boolean isInlineRequest) {
             mPendingFillRequest = null;
             mWaitForInlineRequest = isInlineRequest;
             mPendingInlineSuggestionsRequest = null;
             if (isInlineRequest) {
                 WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference =
-                    new WeakReference<AssistDataReceiverImpl>(this);
+                        new WeakReference<AssistDataReceiverImpl>(this);
                 WeakReference<ViewState> viewStateWeakReference =
-                    new WeakReference<ViewState>(viewState);
-                return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference,
-                    viewStateWeakReference);
+                        new WeakReference<ViewState>(viewState);
+                return new InlineSuggestionRequestConsumer(
+                        assistDataReceiverWeakReference, viewStateWeakReference);
             }
             return null;
         }
 
-        void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest,
-                ViewState viewState) {
+        void handleInlineSuggestionRequest(
+                InlineSuggestionsRequest inlineSuggestionsRequest, ViewState viewState) {
             if (sVerbose) {
                 Slog.v(TAG, "handleInlineSuggestionRequest(): inline suggestion request received");
             }
@@ -738,8 +712,10 @@
         void maybeRequestFillLocked() {
             if (mPendingFillRequest == null) {
                 if (sVerbose) {
-                        Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request "
-                            + "due to empty pending fill request");
+                    Slog.v(
+                            TAG,
+                            "maybeRequestFillLocked(): cancelling calling fill request "
+                                    + "due to empty pending fill request");
                 }
                 return;
             }
@@ -748,27 +724,35 @@
             if (mWaitForInlineRequest) {
                 if (mPendingInlineSuggestionsRequest == null) {
                     if (sVerbose) {
-                        Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request "
-                            + "due to waiting for inline request and pending inline request is "
-                            + "currently empty");
+                        Slog.v(
+                                TAG,
+                                "maybeRequestFillLocked(): cancelling calling fill request due to"
+                                    + " waiting for inline request and pending inline request is"
+                                    + " currently empty");
                     }
                     return;
                 }
                 if (sVerbose) {
-                    Slog.v(TAG, "maybeRequestFillLocked(): adding inline request to pending "
-                        + "fill request");
+                    Slog.v(
+                            TAG,
+                            "maybeRequestFillLocked(): adding inline request to pending "
+                                    + "fill request");
                 }
-                mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
-                        mPendingFillRequest.getFillContexts(),
-                        mPendingFillRequest.getHints(),
-                        mPendingFillRequest.getClientState(),
-                        mPendingFillRequest.getFlags(),
-                        mPendingInlineSuggestionsRequest,
-                        mPendingFillRequest.getDelayedFillIntentSender());
+                mPendingFillRequest =
+                        new FillRequest(
+                                mPendingFillRequest.getId(),
+                                mPendingFillRequest.getFillContexts(),
+                                mPendingFillRequest.getHints(),
+                                mPendingFillRequest.getClientState(),
+                                mPendingFillRequest.getFlags(),
+                                mPendingInlineSuggestionsRequest,
+                                mPendingFillRequest.getDelayedFillIntentSender());
             } else {
                 if (sVerbose) {
-                    Slog.v(TAG, "maybeRequestFillLocked(): not adding inline request to pending "
-                        + "fill request");
+                    Slog.v(
+                            TAG,
+                            "maybeRequestFillLocked(): not adding inline request to pending "
+                                    + "fill request");
                 }
             }
 
@@ -780,19 +764,19 @@
                     && mSecondaryProviderHandler != null) {
                 Slog.v(TAG, "Requesting fill response to secondary provider.");
                 if (!mIsPrimaryCredential) {
-                    mPendingFillRequest = addCredentialManagerDataToClientState(
-                            mPendingFillRequest,
-                            mPendingInlineSuggestionsRequest, id);
+                    mPendingFillRequest =
+                            addCredentialManagerDataToClientState(
+                                    mPendingFillRequest, mPendingInlineSuggestionsRequest, id);
                 }
-                mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
-                        mPendingFillRequest.getFlags(), mClient.asBinder());
+                mSecondaryProviderHandler.onFillRequest(
+                        mPendingFillRequest, mPendingFillRequest.getFlags(), mClient.asBinder());
             } else if (mRemoteFillService != null) {
                 if (mIsPrimaryCredential) {
-                    mPendingFillRequest = addCredentialManagerDataToClientState(
-                            mPendingFillRequest,
-                            mPendingInlineSuggestionsRequest, id);
-                    mRemoteFillService.onFillCredentialRequest(mPendingFillRequest,
-                            mClient.asBinder());
+                    mPendingFillRequest =
+                            addCredentialManagerDataToClientState(
+                                    mPendingFillRequest, mPendingInlineSuggestionsRequest, id);
+                    mRemoteFillService.onFillCredentialRequest(
+                            mPendingFillRequest, mClient.asBinder());
                 } else {
                     mRemoteFillService.onFillRequest(mPendingFillRequest);
                 }
@@ -813,8 +797,11 @@
         @Override
         public void onHandleAssistData(Bundle resultData) throws RemoteException {
             if (mRemoteFillService == null) {
-                wtf(null, "onHandleAssistData() called without a remote service. "
-                        + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+                wtf(
+                        null,
+                        "onHandleAssistData() called without a remote service. "
+                                + "mForAugmentedAutofillOnly: %s",
+                        mSessionFlags.mAugmentedAutofillOnly);
                 return;
             }
             // Keeps to prevent it is cleared on multiple threads.
@@ -824,7 +811,9 @@
                 return;
             }
 
-            final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class);
+            final AssistStructure structure =
+                    resultData.getParcelable(
+                            ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class);
             if (structure == null) {
                 Slog.e(TAG, "No assist structure - app might have crashed providing it");
                 return;
@@ -852,13 +841,16 @@
                 try {
                     structure.ensureDataForAutofill();
                 } catch (RuntimeException e) {
-                    wtf(e, "Exception lazy loading assist structure for %s: %s",
-                            structure.getActivityComponent(), e);
+                    wtf(
+                            e,
+                            "Exception lazy loading assist structure for %s: %s",
+                            structure.getActivityComponent(),
+                            e);
                     return;
                 }
 
-                final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure,
-                        /* autofillableOnly= */false);
+                final ArrayList<AutofillId> ids =
+                        Helper.getAutofillIds(structure, /* autofillableOnly= */ false);
                 for (int i = 0; i < ids.size(); i++) {
                     ids.get(i).setSessionId(Session.this.id);
                 }
@@ -868,8 +860,9 @@
 
                 if (mCompatMode) {
                     // Sanitize URL bar, if needed
-                    final String[] urlBarIds = mService.getUrlBarResourceIdsForCompatMode(
-                            mComponentName.getPackageName());
+                    final String[] urlBarIds =
+                            mService.getUrlBarResourceIdsForCompatMode(
+                                    mComponentName.getPackageName());
                     if (sDebug) {
                         Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds));
                     }
@@ -878,11 +871,19 @@
                         if (mUrlBar != null) {
                             final AutofillId urlBarId = mUrlBar.getAutofillId();
                             if (sDebug) {
-                                Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain "
-                                        + mUrlBar.getWebDomain());
+                                Slog.d(
+                                        TAG,
+                                        "Setting urlBar as id="
+                                                + urlBarId
+                                                + " and domain "
+                                                + mUrlBar.getWebDomain());
                             }
-                            final ViewState viewState = new ViewState(urlBarId, Session.this,
-                                    ViewState.STATE_URL_BAR, mIsPrimaryCredential);
+                            final ViewState viewState =
+                                    new ViewState(
+                                            urlBarId,
+                                            Session.this,
+                                            ViewState.STATE_URL_BAR,
+                                            mIsPrimaryCredential);
                             mViewStates.put(urlBarId, viewState);
                         }
                     }
@@ -907,11 +908,17 @@
                 final List<String> hints = getTypeHintsForProvider();
 
                 mDelayedFillPendingIntent = createPendingIntent(requestId);
-                request = new FillRequest(requestId, contexts, hints, mClientState, flags,
-                        /*inlineSuggestionsRequest=*/ null,
-                        /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null
-                            ? null
-                            : mDelayedFillPendingIntent.getIntentSender());
+                request =
+                        new FillRequest(
+                                requestId,
+                                contexts,
+                                hints,
+                                mClientState,
+                                flags,
+                                /* inlineSuggestionsRequest= */ null,
+                                /* delayedFillIntentSender= */ mDelayedFillPendingIntent == null
+                                        ? null
+                                        : mDelayedFillPendingIntent.getIntentSender());
 
                 mPendingFillRequest = request;
                 maybeRequestFillLocked();
@@ -930,39 +937,49 @@
         @GuardedBy("mLock")
         void processDelayedFillLocked(int requestId, FillResponse response) {
             if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) {
-                Slog.v(TAG, "processDelayedFillLocked: "
-                        + "calling onFillRequestSuccess with new response");
-                onFillRequestSuccess(requestId, response,
-                        mService.getServicePackageName(), mLastFillRequest.getFlags());
+                Slog.v(
+                        TAG,
+                        "processDelayedFillLocked: "
+                                + "calling onFillRequestSuccess with new response");
+                onFillRequestSuccess(
+                        requestId,
+                        response,
+                        mService.getServicePackageName(),
+                        mLastFillRequest.getFlags());
             }
         }
     }
 
-    private FillRequest addCredentialManagerDataToClientState(FillRequest pendingFillRequest,
-            InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) {
+    private FillRequest addCredentialManagerDataToClientState(
+            FillRequest pendingFillRequest,
+            InlineSuggestionsRequest pendingInlineSuggestionsRequest,
+            int sessionId) {
 
         if (pendingFillRequest.getClientState() == null) {
-            pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
-                    pendingFillRequest.getFillContexts(),
-                    pendingFillRequest.getHints(),
-                    new Bundle(),
-                    pendingFillRequest.getFlags(),
-                    pendingInlineSuggestionsRequest,
-                    pendingFillRequest.getDelayedFillIntentSender());
+            pendingFillRequest =
+                    new FillRequest(
+                            pendingFillRequest.getId(),
+                            pendingFillRequest.getFillContexts(),
+                            pendingFillRequest.getHints(),
+                            new Bundle(),
+                            pendingFillRequest.getFlags(),
+                            pendingInlineSuggestionsRequest,
+                            pendingFillRequest.getDelayedFillIntentSender());
         }
         pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId);
         pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId());
-        ResultReceiver resultReceiver = constructCredentialManagerCallback(
-                pendingFillRequest.getId());
-        pendingFillRequest.getClientState().putParcelable(
-                CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver);
+        ResultReceiver resultReceiver =
+                constructCredentialManagerCallback(pendingFillRequest.getId());
+        pendingFillRequest
+                .getClientState()
+                .putParcelable(CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver);
         return pendingFillRequest;
     }
 
     /**
-     * Get the list of valid autofill hint types from Device flags
-     * Returns empty list if PCC is off or no types available
-    */
+     * Get the list of valid autofill hint types from Device flags Returns empty list if PCC is off
+     * or no types available
+     */
     private List<String> getTypeHintsForProvider() {
         if (!mService.isPccClassificationEnabled()) {
             return Collections.EMPTY_LIST;
@@ -978,9 +995,7 @@
         return List.of(typeHints.split(PCC_HINTS_DELIMITER));
     }
 
-    /**
-     * Assist Data Receiver for PCC
-     */
+    /** Assist Data Receiver for PCC */
     private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub {
 
         @GuardedBy("mLock")
@@ -998,7 +1013,7 @@
                                 new WeakReference<>(Session.this);
                 remoteFieldClassificationService.onFieldClassificationRequest(
                         mClassificationState.mPendingFieldClassificationRequest,
-                                fieldClassificationServiceCallbacksWeakRef);
+                        fieldClassificationServiceCallbacksWeakRef);
             }
             mClassificationState.onFieldClassificationRequestSent();
         }
@@ -1006,26 +1021,35 @@
         @Override
         public void onHandleAssistData(Bundle resultData) throws RemoteException {
             // TODO: add a check if pcc field classification service is present
-            final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE,
-                android.app.assist.AssistStructure.class);
+            final AssistStructure structure =
+                    resultData.getParcelable(
+                            ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class);
             if (structure == null) {
-                Slog.e(TAG, "No assist structure for pcc detection - "
-                    + "app might have crashed providing it");
+                Slog.e(
+                        TAG,
+                        "No assist structure for pcc detection - "
+                                + "app might have crashed providing it");
                 return;
             }
 
             final Bundle receiverExtras = resultData.getBundle(ASSIST_KEY_RECEIVER_EXTRAS);
             if (receiverExtras == null) {
-                Slog.e(TAG, "No receiver extras for pcc detection - "
-                    + "app might have crashed providing it");
+                Slog.e(
+                        TAG,
+                        "No receiver extras for pcc detection - "
+                                + "app might have crashed providing it");
                 return;
             }
 
             final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID);
 
             if (sVerbose) {
-                Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": "
-                        + structure);
+                Slog.v(
+                        TAG,
+                        "New structure for PCC Detection: requestId "
+                                + requestId
+                                + ": "
+                                + structure);
             }
 
             synchronized (mLock) {
@@ -1037,13 +1061,16 @@
                 try {
                     structure.ensureDataForAutofill();
                 } catch (RuntimeException e) {
-                    wtf(e, "Exception lazy loading assist structure for %s: %s",
-                        structure.getActivityComponent(), e);
+                    wtf(
+                            e,
+                            "Exception lazy loading assist structure for %s: %s",
+                            structure.getActivityComponent(),
+                            e);
                     return;
                 }
 
-                final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure,
-                    /* autofillableOnly= */false);
+                final ArrayList<AutofillId> ids =
+                        Helper.getAutofillIds(structure, /* autofillableOnly= */ false);
                 for (int i = 0; i < ids.size(); i++) {
                     ids.get(i).setSessionId(Session.this.id);
                 }
@@ -1066,13 +1093,18 @@
         PendingIntent pendingIntent;
         final long identity = Binder.clearCallingIdentity();
         try {
-            Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android")
-                    .putExtra(EXTRA_REQUEST_ID, requestId);
-            pendingIntent = PendingIntent.getBroadcast(
-                    mContext, this.id, intent,
-                    PendingIntent.FLAG_MUTABLE
-                        | PendingIntent.FLAG_ONE_SHOT
-                        | PendingIntent.FLAG_CANCEL_CURRENT);
+            Intent intent =
+                    new Intent(ACTION_DELAYED_FILL)
+                            .setPackage("android")
+                            .putExtra(EXTRA_REQUEST_ID, requestId);
+            pendingIntent =
+                    PendingIntent.getBroadcast(
+                            mContext,
+                            this.id,
+                            intent,
+                            PendingIntent.FLAG_MUTABLE
+                                    | PendingIntent.FLAG_ONE_SHOT
+                                    | PendingIntent.FLAG_CANCEL_CURRENT);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1113,9 +1145,7 @@
         }
     }
 
-    /**
-     * Returns the ids of all entries in {@link #mViewStates} in the same order.
-     */
+    /** Returns the ids of all entries in {@link #mViewStates} in the same order. */
     @GuardedBy("mLock")
     private AutofillId[] getIdsOfAllViewStatesLocked() {
         final int numViewState = mViewStates.size();
@@ -1164,8 +1194,8 @@
     }
 
     /**
-     * <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts},
-     * or {@code null} when not found on either of them.
+     * Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, or
+     * {@code null} when not found on either of them.
      */
     @GuardedBy("mLock")
     @Nullable
@@ -1180,16 +1210,22 @@
         final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this);
         if (previousSessions != null) {
             if (sDebug) {
-                Slog.d(TAG, "findValueLocked(): looking on " + previousSessions.size()
-                        + " previous sessions for autofillId " + autofillId);
+                Slog.d(
+                        TAG,
+                        "findValueLocked(): looking on "
+                                + previousSessions.size()
+                                + " previous sessions for autofillId "
+                                + autofillId);
             }
             for (int i = 0; i < previousSessions.size(); i++) {
                 final Session previousSession = previousSessions.get(i);
-                final AutofillValue previousValue = previousSession
-                        .findValueFromThisSessionOnlyLocked(autofillId);
+                final AutofillValue previousValue =
+                        previousSession.findValueFromThisSessionOnlyLocked(autofillId);
                 if (previousValue != null) {
-                    return getSanitizedValue(createSanitizers(previousSession.getSaveInfoLocked()),
-                            autofillId, previousValue);
+                    return getSanitizedValue(
+                            createSanitizers(previousSession.getSaveInfoLocked()),
+                            autofillId,
+                            previousValue);
                 }
             }
         }
@@ -1212,16 +1248,22 @@
             AutofillValue candidateSaveValue = state.getCandidateSaveValue();
             if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
                 if (sDebug) {
-                    Slog.d(TAG, "findValueLocked(): current value for " + autofillId
-                            + " is empty, using candidateSaveValue instead.");
+                    Slog.d(
+                            TAG,
+                            "findValueLocked(): current value for "
+                                    + autofillId
+                                    + " is empty, using candidateSaveValue instead.");
                 }
                 return candidateSaveValue;
             }
         }
         if (value == null) {
             if (sDebug) {
-                Slog.d(TAG, "findValueLocked(): no current value for " + autofillId
-                        + ", checking value from previous fill contexts");
+                Slog.d(
+                        TAG,
+                        "findValueLocked(): no current value for "
+                                + autofillId
+                                + ", checking value from previous fill contexts");
                 value = getValueFromContextsLocked(autofillId);
             }
         }
@@ -1231,17 +1273,16 @@
     /**
      * Updates values of the nodes in the context's structure so that:
      *
-     * - proper node is focused
-     * - autofillValue is sent back to service when it was previously autofilled
-     * - autofillValue is sent in the view used to force a request
+     * <p>- proper node is focused - autofillValue is sent back to service when it was previously
+     * autofilled - autofillValue is sent in the view used to force a request
      *
      * @param fillContext The context to be filled
      * @param flags The flags that started the session
      */
     @GuardedBy("mLock")
     private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) {
-        final ViewNode[] nodes = fillContext
-                .findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
+        final ViewNode[] nodes =
+                fillContext.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
 
         final int numViewState = mViewStates.size();
         for (int i = 0; i < numViewState; i++) {
@@ -1250,7 +1291,8 @@
             final ViewNode node = nodes[i];
             if (node == null) {
                 if (sVerbose) {
-                    Slog.v(TAG,
+                    Slog.v(
+                            TAG,
                             "fillContextWithAllowedValuesLocked(): no node for " + viewState.id);
                 }
                 continue;
@@ -1277,14 +1319,15 @@
         }
     }
 
-    /**
-     * Cancels the last request sent to the {@link #mRemoteFillService}.
-     */
+    /** Cancels the last request sent to the {@link #mRemoteFillService}. */
     @GuardedBy("mLock")
     private void cancelCurrentRequestLocked() {
         if (mRemoteFillService == null) {
-            wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
-                    + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+            wtf(
+                    null,
+                    "cancelCurrentRequestLocked() called without a remote service. "
+                            + "mForAugmentedAutofillOnly: %s",
+                    mSessionFlags.mAugmentedAutofillOnly);
             return;
         }
         final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
@@ -1318,21 +1361,20 @@
     private Optional<Integer> requestNewFillResponseLocked(
             @NonNull ViewState viewState, int newState, int flags) {
         boolean isSecondary = shouldRequestSecondaryProvider(flags);
-        final FillResponse existingResponse = isSecondary
-                ? viewState.getSecondaryResponse() : viewState.getResponse();
+        final FillResponse existingResponse =
+                isSecondary ? viewState.getSecondaryResponse() : viewState.getResponse();
         mFillRequestEventLogger.startLogForNewRequest();
         mRequestCount++;
         mFillRequestEventLogger.maybeSetAppPackageUid(uid);
         mFillRequestEventLogger.maybeSetFlags(mFlags);
-        if(mPreviouslyFillDialogPotentiallyStarted) {
+        if (mPreviouslyFillDialogPotentiallyStarted) {
             mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_PRE_TRIGGER);
         } else {
             if ((flags & FLAG_MANUAL_REQUEST) != 0) {
                 mFillRequestEventLogger.maybeSetRequestTriggerReason(
                         TRIGGER_REASON_EXPLICITLY_REQUESTED);
             } else {
-                mFillRequestEventLogger.maybeSetRequestTriggerReason(
-                        TRIGGER_REASON_NORMAL_TRIGGER);
+                mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_NORMAL_TRIGGER);
             }
         }
         if (existingResponse != null) {
@@ -1348,9 +1390,14 @@
         mSessionState = STATE_ACTIVE;
         if (mSessionFlags.mAugmentedAutofillOnly || mRemoteFillService == null) {
             if (sVerbose) {
-                Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead "
-                        + "(mForAugmentedAutofillOnly=" + mSessionFlags.mAugmentedAutofillOnly
-                        + ", flags=" + flags + ")");
+                Slog.v(
+                        TAG,
+                        "requestNewFillResponse(): triggering augmented autofill instead "
+                                + "(mForAugmentedAutofillOnly="
+                                + mSessionFlags.mAugmentedAutofillOnly
+                                + ", flags="
+                                + flags
+                                + ")");
             }
             mSessionFlags.mAugmentedAutofillOnly = true;
             mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID);
@@ -1365,16 +1412,23 @@
 
         // Create a metrics log for the request
         final int ordinal = mRequestLogs.size() + 1;
-        final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST)
-                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal);
+        final LogMaker log =
+                newLogMaker(MetricsEvent.AUTOFILL_REQUEST)
+                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal);
         if (flags != 0) {
             log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags);
         }
         mRequestLogs.put(requestId, log);
 
         if (sVerbose) {
-            Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
-                    + ", flags=" + flags);
+            Slog.v(
+                    TAG,
+                    "Requesting structure for request #"
+                            + ordinal
+                            + " ,requestId="
+                            + requestId
+                            + ", flags="
+                            + flags);
         }
         boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
         mFillRequestEventLogger.maybeSetRequestId(requestId);
@@ -1406,11 +1460,12 @@
         // is also not focused.
         final RemoteInlineSuggestionRenderService remoteRenderService =
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
-        if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null
-            && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
+        if (mSessionFlags.mInlineSupportedByService
+                && remoteRenderService != null
+                && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
             Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
-                mAssistReceiver.newAutofillRequestLocked(viewState,
-                    /* isInlineRequest= */ true);
+                    mAssistReceiver.newAutofillRequestLocked(
+                            viewState, /* isInlineRequest= */ true);
             if (inlineSuggestionsRequestConsumer != null) {
                 final int requestIdCopy = requestId;
                 final AutofillId focusedId = mCurrentViewId;
@@ -1423,8 +1478,9 @@
                                         requestIdCopy,
                                         inlineSuggestionsRequestConsumer,
                                         focusedId);
-                RemoteCallback inlineSuggestionRendorInfoCallback = new RemoteCallback(
-                        inlineSuggestionRendorInfoCallbackOnResultListener, mHandler);
+                RemoteCallback inlineSuggestionRendorInfoCallback =
+                        new RemoteCallback(
+                                inlineSuggestionRendorInfoCallbackOnResultListener, mHandler);
 
                 remoteRenderService.getInlineSuggestionsRendererInfo(
                         inlineSuggestionRendorInfoCallback);
@@ -1465,8 +1521,9 @@
             receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
             final long identity = Binder.clearCallingIdentity();
             try {
-                if (!ActivityTaskManager.getService().requestAutofillData(mPccAssistReceiver,
-                        receiverExtras, mActivityToken, flags)) {
+                if (!ActivityTaskManager.getService()
+                        .requestAutofillData(
+                                mPccAssistReceiver, receiverExtras, mActivityToken, flags)) {
                     Slog.w(TAG, "failed to request autofill data for " + mActivityToken);
                 }
             } finally {
@@ -1483,8 +1540,9 @@
             receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
             final long identity = Binder.clearCallingIdentity();
             try {
-                if (!ActivityTaskManager.getService().requestAutofillData(mAssistReceiver,
-                        receiverExtras, mActivityToken, flags)) {
+                if (!ActivityTaskManager.getService()
+                        .requestAutofillData(
+                                mAssistReceiver, receiverExtras, mActivityToken, flags)) {
                     Slog.w(TAG, "failed to request autofill data for " + mActivityToken);
                 }
             } finally {
@@ -1495,13 +1553,27 @@
         }
     }
 
-    Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
-            @NonNull Context context, @NonNull Handler handler, int userId, @NonNull Object lock,
-            int sessionId, int taskId, int uid, @NonNull IBinder activityToken,
-            @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
-            @NonNull LocalLog wtfHistory, @Nullable ComponentName serviceComponentName,
-            @NonNull ComponentName componentName, boolean compatMode,
-            boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags,
+    Session(
+            @NonNull AutofillManagerServiceImpl service,
+            @NonNull AutoFillUI ui,
+            @NonNull Context context,
+            @NonNull Handler handler,
+            int userId,
+            @NonNull Object lock,
+            int sessionId,
+            int taskId,
+            int uid,
+            @NonNull IBinder activityToken,
+            @NonNull IBinder client,
+            boolean hasCallback,
+            @NonNull LocalLog uiLatencyHistory,
+            @NonNull LocalLog wtfHistory,
+            @Nullable ComponentName serviceComponentName,
+            @NonNull ComponentName componentName,
+            boolean compatMode,
+            boolean bindInstantServiceAllowed,
+            boolean forAugmentedAutofillOnly,
+            int flags,
             @NonNull InputMethodManagerInternal inputMethodManagerInternal,
             boolean isPrimaryCredential) {
         if (sessionId < 0) {
@@ -1533,22 +1605,40 @@
             primaryServiceComponentName = serviceComponentName;
             secondaryServiceComponentName = mCredentialAutofillService;
         }
-        Slog.v(TAG, "Primary service component name: " + primaryServiceComponentName
-                + ", secondary service component name: " + secondaryServiceComponentName);
+        Slog.v(
+                TAG,
+                "Primary service component name: "
+                        + primaryServiceComponentName
+                        + ", secondary service component name: "
+                        + secondaryServiceComponentName);
 
-        mRemoteFillService = primaryServiceComponentName == null ? null
-                : new RemoteFillService(context, primaryServiceComponentName, userId, this,
-                        bindInstantServiceAllowed, mCredentialAutofillService);
-        mSecondaryProviderHandler = secondaryServiceComponentName == null ? null
-                : new SecondaryProviderHandler(context, userId, bindInstantServiceAllowed,
-                this::onSecondaryFillResponse, secondaryServiceComponentName,
-                        mCredentialAutofillService);
+        mRemoteFillService =
+                primaryServiceComponentName == null
+                        ? null
+                        : new RemoteFillService(
+                                context,
+                                primaryServiceComponentName,
+                                userId,
+                                this,
+                                bindInstantServiceAllowed,
+                                mCredentialAutofillService);
+        mSecondaryProviderHandler =
+                secondaryServiceComponentName == null
+                        ? null
+                        : new SecondaryProviderHandler(
+                                context,
+                                userId,
+                                bindInstantServiceAllowed,
+                                this::onSecondaryFillResponse,
+                                secondaryServiceComponentName,
+                                mCredentialAutofillService);
         mActivityToken = activityToken;
         mHasCallback = hasCallback;
         mUiLatencyHistory = uiLatencyHistory;
         mWtfHistory = wtfHistory;
-        int displayId = LocalServices.getService(ActivityTaskManagerInternal.class)
-                .getDisplayId(activityToken);
+        int displayId =
+                LocalServices.getService(ActivityTaskManagerInternal.class)
+                        .getDisplayId(activityToken);
         mContext = Helper.getDisplayContext(context, displayId);
         mComponentName = componentName;
         mCompatMode = compatMode;
@@ -1557,8 +1647,9 @@
         mStartTime = SystemClock.elapsedRealtime();
         mLatencyBaseTime = mStartTime;
         mRequestCount = 0;
-        mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog(
-                sessionId, uid, mLatencyBaseTime);
+        mPresentationStatsEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(
+                        sessionId, uid, mLatencyBaseTime);
         mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
         mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
         mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
@@ -1574,33 +1665,39 @@
             setClientLocked(client);
         }
 
-        mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal,
-                userId, componentName, handler, mLock,
-                new InlineFillUi.InlineUiEventCallback() {
-                    @Override
-                    public void notifyInlineUiShown(AutofillId autofillId) {
-                        notifyFillUiShown(autofillId);
-                    }
+        mInlineSessionController =
+                new AutofillInlineSessionController(
+                        inputMethodManagerInternal,
+                        userId,
+                        componentName,
+                        handler,
+                        mLock,
+                        new InlineFillUi.InlineUiEventCallback() {
+                            @Override
+                            public void notifyInlineUiShown(AutofillId autofillId) {
+                                notifyFillUiShown(autofillId);
+                            }
 
-                    @Override
-                    public void notifyInlineUiHidden(AutofillId autofillId) {
-                        notifyFillUiHidden(autofillId);
-                    }
-                });
+                            @Override
+                            public void notifyInlineUiHidden(AutofillId autofillId) {
+                                notifyFillUiHidden(autofillId);
+                            }
+                        });
 
-        mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
-                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
+        mMetricsLogger.write(
+                newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
+                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
         mLogViewEntered = false;
     }
 
     private ComponentName getCredentialAutofillService(Context context) {
         ComponentName componentName = null;
-        String credentialManagerAutofillCompName = context.getResources().getString(
-                R.string.config_defaultCredentialManagerAutofillService);
+        String credentialManagerAutofillCompName =
+                context.getResources()
+                        .getString(R.string.config_defaultCredentialManagerAutofillService);
         if (credentialManagerAutofillCompName != null
                 && !credentialManagerAutofillCompName.isEmpty()) {
-            componentName = ComponentName.unflattenFromString(
-                    credentialManagerAutofillCompName);
+            componentName = ComponentName.unflattenFromString(credentialManagerAutofillCompName);
         }
         if (componentName == null) {
             Slog.w(TAG, "Invalid CredentialAutofillService");
@@ -1614,7 +1711,8 @@
      * @return The activity token
      */
     @GuardedBy("mLock")
-    @NonNull IBinder getActivityTokenLocked() {
+    @NonNull
+    IBinder getActivityTokenLocked() {
         return mActivityToken;
     }
 
@@ -1627,8 +1725,11 @@
     void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#switchActivity() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#switchActivity() rejected - session: "
+                                + id
+                                + " destroyed");
                 return;
             }
             mActivityToken = newActivity;
@@ -1643,17 +1744,22 @@
     private void setClientLocked(@NonNull IBinder client) {
         unlinkClientVultureLocked();
         mClient = IAutoFillManagerClient.Stub.asInterface(client);
-        mClientVulture = () -> {
-            synchronized (mLock) {
-                Slog.d(TAG, "handling death of " + mActivityToken + " when saving="
-                        + mSessionFlags.mShowingSaveUi);
-                if (mSessionFlags.mShowingSaveUi) {
-                    mUi.hideFillUi(this);
-                } else {
-                    mUi.destroyAll(mPendingSaveUi, this, false);
-                }
-            }
-        };
+        mClientVulture =
+                () -> {
+                    synchronized (mLock) {
+                        Slog.d(
+                                TAG,
+                                "handling death of "
+                                        + mActivityToken
+                                        + " when saving="
+                                        + mSessionFlags.mShowingSaveUi);
+                        if (mSessionFlags.mShowingSaveUi) {
+                            mUi.hideFillUi(this);
+                        } else {
+                            mUi.destroyAll(mPendingSaveUi, this, false);
+                        }
+                    }
+                };
         try {
             mClient.asBinder().linkToDeath(mClientVulture, 0);
         } catch (RemoteException e) {
@@ -1676,8 +1782,11 @@
     // FillServiceCallbacks
     @Override
     @SuppressWarnings("GuardedBy")
-    public void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
-            @NonNull String servicePackageName, int requestFlags) {
+    public void onFillRequestSuccess(
+            int requestId,
+            @Nullable FillResponse response,
+            @NonNull String servicePackageName,
+            int requestFlags) {
         final AutofillId[] fieldClassificationIds;
 
         final LogMaker requestLog;
@@ -1701,8 +1810,11 @@
                     getDetectionPreferenceForLogging());
 
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#onFillRequestSuccess() rejected - session: "
+                                + id
+                                + " destroyed");
                 mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
                 mFillResponseEventLogger.logAndEndEvent();
                 return;
@@ -1713,8 +1825,11 @@
                 // saveUi gets closed, the session will be destroyed and AutofillManager will reset
                 // its state. Processing the fill request will result in a great chance of corrupt
                 // state in Autofill.
-                Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
-                        + id + " is showing saveUi");
+                Slog.w(
+                        TAG,
+                        "Call to Session#onFillRequestSuccess() rejected - session: "
+                                + id
+                                + " is showing saveUi");
                 mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
                 mFillResponseEventLogger.logAndEndEvent();
                 return;
@@ -1760,22 +1875,21 @@
             }
         }
 
-
         final long disableDuration = response.getDisableDuration();
         final boolean autofillDisabled = disableDuration > 0;
         if (autofillDisabled) {
             final int flags = response.getFlags();
             final boolean disableActivityOnly =
                     (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0;
-            notifyDisableAutofillToClient(disableDuration,
-                    disableActivityOnly ? mComponentName : null);
+            notifyDisableAutofillToClient(
+                    disableDuration, disableActivityOnly ? mComponentName : null);
 
             if (disableActivityOnly) {
-                mService.disableAutofillForActivity(mComponentName, disableDuration,
-                        id, mCompatMode);
+                mService.disableAutofillForActivity(
+                        mComponentName, disableDuration, id, mCompatMode);
             } else {
-                mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
-                        id, mCompatMode);
+                mService.disableAutofillForApp(
+                        mComponentName.getPackageName(), disableDuration, id, mCompatMode);
             }
 
             synchronized (mLock) {
@@ -1786,17 +1900,22 @@
                 if (triggerAugmentedAutofillLocked(requestFlags) != null) {
                     mSessionFlags.mAugmentedAutofillOnly = true;
                     if (sDebug) {
-                        Slog.d(TAG, "Service disabled autofill for " + mComponentName
-                                + ", but session is kept for augmented autofill only");
+                        Slog.d(
+                                TAG,
+                                "Service disabled autofill for "
+                                        + mComponentName
+                                        + ", but session is kept for augmented autofill only");
                     }
                     return;
                 }
             }
 
             if (sDebug) {
-                final StringBuilder message = new StringBuilder("Service disabled autofill for ")
+                final StringBuilder message =
+                        new StringBuilder("Service disabled autofill for ")
                                 .append(mComponentName)
-                                .append(": flags=").append(flags)
+                                .append(": flags=")
+                                .append(flags)
                                 .append(", duration=");
                 TimeUtils.formatDuration(disableDuration, message);
                 Slog.d(TAG, message.toString());
@@ -1816,8 +1935,9 @@
         }
 
         if (requestLog != null) {
-            requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
-                            response.getDatasets() == null ? 0 : response.getDatasets().size());
+            requestLog.addTaggedData(
+                    MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+                    response.getDatasets() == null ? 0 : response.getDatasets().size());
             if (fieldClassificationIds != null) {
                 requestLog.addTaggedData(
                         MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS,
@@ -1840,10 +1960,9 @@
         }
     }
 
-
     @GuardedBy("mLock")
-    private void processResponseLockedForPcc(@NonNull FillResponse response,
-            @Nullable Bundle newClientState, int flags) {
+    private void processResponseLockedForPcc(
+            @NonNull FillResponse response, @Nullable Bundle newClientState, int flags) {
         if (DBG) {
             Slog.d(TAG, "DBG: Initial response: " + response);
         }
@@ -1851,9 +1970,7 @@
             response = getEffectiveFillResponse(response);
             if (isEmptyResponse(response)) {
                 // Treat it as a null response.
-                processNullResponseLocked(
-                        response != null ? response.getRequestId() : 0,
-                        flags);
+                processNullResponseLocked(response != null ? response.getRequestId() : 0, flags);
                 return;
             }
             if (DBG) {
@@ -1870,9 +1987,9 @@
             return ((response.getDatasets() == null || response.getDatasets().isEmpty())
                     && response.getAuthentication() == null
                     && (saveInfo == null
-                        || (ArrayUtils.isEmpty(saveInfo.getOptionalIds())
-                            && ArrayUtils.isEmpty(saveInfo.getRequiredIds())
-                            && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0)))
+                            || (ArrayUtils.isEmpty(saveInfo.getOptionalIds())
+                                    && ArrayUtils.isEmpty(saveInfo.getRequiredIds())
+                                    && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0)))
                     && (ArrayUtils.isEmpty(response.getFieldClassificationIds())));
         }
     }
@@ -1884,10 +2001,12 @@
         computeDatasetsForProviderAndUpdateContainer(response, autofillProviderContainer);
 
         if (DBG) {
-            Slog.d(TAG, "DBG: computeDatasetsForProviderAndUpdateContainer: "
-                    + autofillProviderContainer);
+            Slog.d(
+                    TAG,
+                    "DBG: computeDatasetsForProviderAndUpdateContainer: "
+                            + autofillProviderContainer);
         }
-        if (!mService.isPccClassificationEnabled())  {
+        if (!mService.isPccClassificationEnabled()) {
             if (sVerbose) {
                 Slog.v(TAG, "PCC classification is disabled");
             }
@@ -1897,10 +2016,14 @@
             if (mClassificationState.mState != ClassificationState.STATE_RESPONSE
                     || mClassificationState.mLastFieldClassificationResponse == null) {
                 if (sVerbose) {
-                    Slog.v(TAG, "PCC classification no last response:"
-                            + (mClassificationState.mLastFieldClassificationResponse == null)
-                            +   " ,ineligible state="
-                            + (mClassificationState.mState != ClassificationState.STATE_RESPONSE));
+                    Slog.v(
+                            TAG,
+                            "PCC classification no last response:"
+                                    + (mClassificationState.mLastFieldClassificationResponse
+                                            == null)
+                                    + " ,ineligible state="
+                                    + (mClassificationState.mState
+                                            != ClassificationState.STATE_RESPONSE));
                 }
                 return createShallowCopy(response, autofillProviderContainer);
             }
@@ -1966,8 +2089,11 @@
             mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
                     (int) (fillRequestReceivedRelativeTimestamp));
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#onSecondaryFillResponse() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#onSecondaryFillResponse() rejected - session: "
+                                + id
+                                + " destroyed");
                 mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
                 mFillResponseEventLogger.logAndEndEvent();
                 return;
@@ -1981,7 +2107,10 @@
                 mSecondaryResponses = new SparseArray<>(2);
             }
             mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse);
-            setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false,
+            setViewStatesLocked(
+                    fillResponse,
+                    ViewState.STATE_FILLABLE,
+                    /* clearResponse= */ false,
                     /* isPrimary= */ false);
 
             // Updates the UI, if necessary.
@@ -1997,16 +2126,15 @@
     private FillResponse createShallowCopy(
             FillResponse response, DatasetComputationContainer container) {
         return FillResponse.shallowCopy(
-                response,
-                new ArrayList<>(container.mDatasets),
-                getEligibleSaveInfo(response));
+                response, new ArrayList<>(container.mDatasets), getEligibleSaveInfo(response));
     }
 
     private SaveInfo getEligibleSaveInfo(FillResponse response) {
         SaveInfo saveInfo = response.getSaveInfo();
-        if (saveInfo == null || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds())
-                || !ArrayUtils.isEmpty(saveInfo.getRequiredIds())
-                || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) {
+        if (saveInfo == null
+                || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds())
+                        || !ArrayUtils.isEmpty(saveInfo.getRequiredIds())
+                        || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) {
             return saveInfo;
         }
         synchronized (mLock) {
@@ -2019,12 +2147,12 @@
             ArraySet<AutofillId> ids = new ArraySet<>();
             int saveType = saveInfo.getType();
             if (saveType == SaveInfo.SAVE_DATA_TYPE_GENERIC) {
-                for (Set<AutofillId> autofillIds: hintsToAutofillIdMap.values()) {
+                for (Set<AutofillId> autofillIds : hintsToAutofillIdMap.values()) {
                     ids.addAll(autofillIds);
                 }
             } else {
                 Set<String> hints = HintsHelper.getHintsForSaveType(saveType);
-                for (Map.Entry<String, Set<AutofillId>> entry: hintsToAutofillIdMap.entrySet()) {
+                for (Map.Entry<String, Set<AutofillId>> entry : hintsToAutofillIdMap.entrySet()) {
                     String hint = entry.getKey();
                     if (hints.contains(hint)) {
                         ids.addAll(entry.getValue());
@@ -2039,9 +2167,7 @@
         }
     }
 
-    /**
-     * A private class to hold & compute datasets to be shown
-     */
+    /** A private class to hold & compute datasets to be shown */
     private static class DatasetComputationContainer {
         // List of all autofill ids that have a corresponding datasets
         Set<AutofillId> mAutofillIds = new LinkedHashSet<>();
@@ -2103,9 +2229,10 @@
     }
 
     /**
-     * Computes datasets that are eligible to be shown based on provider detections.
-     * Datasets are populated in the provided container for them to be later merged with the
-     * PCC eligible datasets based on preference strategy.
+     * Computes datasets that are eligible to be shown based on provider detections. Datasets are
+     * populated in the provided container for them to be later merged with the PCC eligible
+     * datasets based on preference strategy.
+     *
      * @param response
      * @param container
      */
@@ -2210,9 +2337,10 @@
     }
 
     /**
-     * Computes datasets that are eligible to be shown based on PCC detections.
-     * Datasets are populated in the provided container for them to be later merged with the
-     * provider eligible datasets based on preference strategy.
+     * Computes datasets that are eligible to be shown based on PCC detections. Datasets are
+     * populated in the provided container for them to be later merged with the provider eligible
+     * datasets based on preference strategy.
+     *
      * @param response
      * @param container
      */
@@ -2267,14 +2395,21 @@
                         // For that, there has to be a datatype detected by PCC, and the dataset
                         // for that datatype provided by the provider.
                         AutofillId autofillId = dataset.getFieldIds().get(j);
-                        if (!mClassificationState.mClassificationCombinedHintsMap
-                                .containsKey(autofillId)) {
+                        if (!mClassificationState.mClassificationCombinedHintsMap.containsKey(
+                                autofillId)) {
                             additionalEligibleAutofillIds.add(autofillId);
                             additionalDatasetAutofillIds.add(autofillId);
                             // For each of the field, copy over values.
-                            copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues,
-                                    fieldPresentations, fieldDialogPresentations,
-                                    fieldInlinePresentations, fieldInlineTooltipPresentations,
+                            copyFieldsFromDataset(
+                                    dataset,
+                                    j,
+                                    autofillId,
+                                    fieldIds,
+                                    fieldValues,
+                                    fieldPresentations,
+                                    fieldDialogPresentations,
+                                    fieldInlinePresentations,
+                                    fieldInlineTooltipPresentations,
                                     fieldFilters);
                         }
                         continue;
@@ -2292,9 +2427,16 @@
                             eligibleAutofillIds.add(autofillId);
                             datasetAutofillIds.add(autofillId);
                             // For each of the field, copy over values.
-                            copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues,
-                                    fieldPresentations, fieldDialogPresentations,
-                                    fieldInlinePresentations, fieldInlineTooltipPresentations,
+                            copyFieldsFromDataset(
+                                    dataset,
+                                    j,
+                                    autofillId,
+                                    fieldIds,
+                                    fieldValues,
+                                    fieldPresentations,
+                                    fieldDialogPresentations,
+                                    fieldInlinePresentations,
+                                    fieldInlineTooltipPresentations,
                                     fieldFilters);
                         }
                     }
@@ -2364,8 +2506,7 @@
         fieldPresentations.add(dataset.getFieldPresentation(index));
         fieldDialogPresentations.add(dataset.getFieldDialogPresentation(index));
         fieldInlinePresentations.add(dataset.getFieldInlinePresentation(index));
-        fieldInlineTooltipPresentations.add(
-                dataset.getFieldInlineTooltipPresentation(index));
+        fieldInlineTooltipPresentations.add(dataset.getFieldInlineTooltipPresentation(index));
         fieldFilters.add(dataset.getFilter(index));
     }
 
@@ -2395,16 +2536,22 @@
 
             unregisterDelayedFillBroadcastLocked();
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
-                        + ") rejected - session: " + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#onFillRequestFailureOrTimeout(req="
+                                + requestId
+                                + ") rejected - session: "
+                                + id
+                                + " destroyed");
                 mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
                 mFillResponseEventLogger.logAndEndEvent();
 
                 return;
             }
             if (sDebug) {
-                Slog.d(TAG, "finishing session due to service "
-                        + (timedOut ? "timeout" : "failure"));
+                Slog.d(
+                        TAG,
+                        "finishing session due to service " + (timedOut ? "timeout" : "failure"));
             }
             mService.resetLastResponse();
             mLastFillDialogTriggerIds = null;
@@ -2418,12 +2565,16 @@
                 final int targetSdk = mService.getTargedSdkLocked();
                 if (targetSdk >= Build.VERSION_CODES.Q) {
                     showMessage = false;
-                    Slog.w(TAG, "onFillRequestFailureOrTimeout(): not showing '" + message
-                            + "' because service's targetting API " + targetSdk);
+                    Slog.w(
+                            TAG,
+                            "onFillRequestFailureOrTimeout(): not showing '"
+                                    + message
+                                    + "' because service's targeting API "
+                                    + targetSdk);
                 }
                 if (message != null) {
-                    requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN,
-                            message.length());
+                    requestLog.addTaggedData(
+                            MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length());
                 }
             }
 
@@ -2445,8 +2596,8 @@
             mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
             mFillResponseEventLogger.logAndEndEvent();
         }
-        notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED,
-                /* autofillableIds= */ null);
+        notifyUnavailableToClient(
+                AutofillManager.STATE_UNKNOWN_FAILED, /* autofillableIds= */ null);
         if (showMessage) {
             getUiForShowing().showError(message, this);
         }
@@ -2455,8 +2606,8 @@
 
     // FillServiceCallbacks
     @Override
-    public void onSaveRequestSuccess(@NonNull String servicePackageName,
-            @Nullable IntentSender intentSender) {
+    public void onSaveRequestSuccess(
+            @NonNull String servicePackageName, @Nullable IntentSender intentSender) {
         synchronized (mLock) {
             mSessionFlags.mShowingSaveUi = false;
             // Log onSaveRequest result.
@@ -2464,16 +2615,22 @@
             mSaveEventLogger.maybeSetLatencySaveFinishMillis();
             mSaveEventLogger.logAndEndEvent();
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#onSaveRequestSuccess() rejected - session: "
+                                + id
+                                + " destroyed");
                 return;
             }
         }
-        LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
-                .setType(intentSender == null ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_OPEN);
+        LogMaker log =
+                newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
+                        .setType(
+                                intentSender == null
+                                        ? MetricsEvent.TYPE_SUCCESS
+                                        : MetricsEvent.TYPE_OPEN);
         mMetricsLogger.write(log);
 
-
         if (intentSender != null) {
             if (sDebug) Slog.d(TAG, "Starting intent sender on save()");
             startIntentSenderAndFinishSession(intentSender);
@@ -2485,8 +2642,8 @@
 
     // FillServiceCallbacks
     @Override
-    public void onSaveRequestFailure(@Nullable CharSequence message,
-            @NonNull String servicePackageName) {
+    public void onSaveRequestFailure(
+            @Nullable CharSequence message, @NonNull String servicePackageName) {
         boolean showMessage = !TextUtils.isEmpty(message);
 
         synchronized (mLock) {
@@ -2495,28 +2652,34 @@
             mSaveEventLogger.maybeSetLatencySaveFinishMillis();
             mSaveEventLogger.logAndEndEvent();
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#onSaveRequestFailure() rejected - session: "
+                                + id
+                                + " destroyed");
                 return;
             }
             if (showMessage) {
                 final int targetSdk = mService.getTargedSdkLocked();
                 if (targetSdk >= Build.VERSION_CODES.Q) {
                     showMessage = false;
-                    Slog.w(TAG, "onSaveRequestFailure(): not showing '" + message
-                            + "' because service's targetting API " + targetSdk);
+                    Slog.w(
+                            TAG,
+                            "onSaveRequestFailure(): not showing '"
+                                    + message
+                                    + "' because service's targeting API "
+                                    + targetSdk);
                 }
             }
         }
         final LogMaker log =
                 newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
-                .setType(MetricsEvent.TYPE_FAILURE);
+                        .setType(MetricsEvent.TYPE_FAILURE);
         if (message != null) {
             log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length());
         }
         mMetricsLogger.write(log);
 
-
         if (showMessage) {
             getUiForShowing().showError(message, this);
         }
@@ -2525,8 +2688,8 @@
 
     // FillServiceCallbacks
     @Override
-    public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
-            convertCredentialResponse) {
+    public void onConvertCredentialRequestSuccess(
+            @NonNull ConvertCredentialResponse convertCredentialResponse) {
         Dataset dataset = convertCredentialResponse.getDataset();
         Bundle clientState = convertCredentialResponse.getClientState();
         if (dataset != null) {
@@ -2534,15 +2697,18 @@
             if (clientState != null) {
                 requestId = clientState.getInt(EXTRA_AUTOFILL_REQUEST_ID);
             } else {
-                Slog.e(TAG, "onConvertCredentialRequestSuccess(): client state is null, this "
-                        + "would cause loss in logging.");
+                Slog.e(
+                        TAG,
+                        "onConvertCredentialRequestSuccess(): client state is null, this "
+                                + "would cause loss in logging.");
             }
             // TODO: Add autofill related logging; consider whether to log the index
-            fill(requestId, /* datasetIndex=*/ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET);
+            fill(requestId, /* datasetIndex= */ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET);
         } else {
             // TODO: Add logging to log this error case
-            Slog.e(TAG, "onConvertCredentialRequestSuccess(): dataset inside response is "
-                    + "null");
+            Slog.e(
+                    TAG,
+                    "onConvertCredentialRequestSuccess(): dataset inside response is " + "null");
         }
     }
 
@@ -2550,11 +2716,11 @@
      * Gets the {@link FillContext} for a request.
      *
      * @param requestId The id of the request
-     *
      * @return The context or {@code null} if there is no context
      */
     @GuardedBy("mLock")
-    @Nullable private FillContext getFillContextByRequestIdLocked(int requestId) {
+    @Nullable
+    private FillContext getFillContextByRequestIdLocked(int requestId) {
         if (mContexts == null) {
             return null;
         }
@@ -2582,19 +2748,26 @@
 
     // AutoFillUiCallback
     @Override
-    public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras,
-            int uiType) {
+    public void authenticate(
+            int requestId, int datasetIndex, IntentSender intent, Bundle extras, int uiType) {
         if (sDebug) {
-            Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
-                    + "; intentSender=" + intent);
+            Slog.d(
+                    TAG,
+                    "authenticate(): requestId="
+                            + requestId
+                            + "; datasetIdx="
+                            + datasetIndex
+                            + "; intentSender="
+                            + intent);
         }
         final Intent fillInIntent;
         synchronized (mLock) {
             mPresentationStatsEventLogger.maybeSetAuthenticationType(
-                AUTHENTICATION_TYPE_FULL_AUTHENTICATION);
+                    AUTHENTICATION_TYPE_FULL_AUTHENTICATION);
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#authenticate() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#authenticate() rejected - session: " + id + " destroyed");
                 return;
             }
             fillInIntent = createAuthFillInIntentLocked(requestId, extras);
@@ -2607,10 +2780,14 @@
         mService.setAuthenticationSelected(id, mClientState, uiType);
 
         final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
-        mHandler.sendMessage(obtainMessage(
-                Session::startAuthentication,
-                this, authenticationId, intent, fillInIntent,
-                /* authenticateInline= */ uiType == UI_TYPE_INLINE));
+        mHandler.sendMessage(
+                obtainMessage(
+                        Session::startAuthentication,
+                        this,
+                        authenticationId,
+                        intent,
+                        fillInIntent,
+                        /* authenticateInline= */ uiType == UI_TYPE_INLINE));
     }
 
     // AutoFillUiCallback
@@ -2618,14 +2795,13 @@
     public void fill(int requestId, int datasetIndex, Dataset dataset, int uiType) {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#fill() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(TAG, "Call to Session#fill() rejected - session: " + id + " destroyed");
                 return;
             }
         }
-        mHandler.sendMessage(obtainMessage(
-                Session::autoFill,
-                this, requestId, datasetIndex, dataset, true, uiType));
+        mHandler.sendMessage(
+                obtainMessage(
+                        Session::autoFill, this, requestId, datasetIndex, dataset, true, uiType));
     }
 
     // AutoFillUiCallback
@@ -2633,15 +2809,13 @@
     public void save() {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#save() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(TAG, "Call to Session#save() rejected - session: " + id + " destroyed");
                 return;
             }
             mSaveEventLogger.maybeSetLatencySaveRequestMillis();
         }
-        mHandler.sendMessage(obtainMessage(
-                AutofillManagerServiceImpl::handleSessionSave,
-                mService, this));
+        mHandler.sendMessage(
+                obtainMessage(AutofillManagerServiceImpl::handleSessionSave, mService, this));
     }
 
     // AutoFillUiCallback
@@ -2650,13 +2824,13 @@
         synchronized (mLock) {
             mSessionFlags.mShowingSaveUi = false;
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#cancelSave() rejected - session: " + id + " destroyed");
                 return;
             }
         }
-        mHandler.sendMessage(obtainMessage(
-                Session::removeFromService, this));
+        mHandler.sendMessage(obtainMessage(Session::removeFromService, this));
     }
 
     // AutofillUiCallback
@@ -2692,26 +2866,34 @@
 
     // AutoFillUiCallback
     @Override
-    public void requestShowFillUi(AutofillId id, int width, int height,
-            IAutofillWindowPresenter presenter) {
+    public void requestShowFillUi(
+            AutofillId id, int width, int height, IAutofillWindowPresenter presenter) {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#requestShowFillUi() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#requestShowFillUi() rejected - session: "
+                                + id
+                                + " destroyed");
                 return;
             }
             if (id.equals(mCurrentViewId)) {
                 try {
                     final ViewState view = mViewStates.get(id);
-                    mClient.requestShowFillUi(this.id, id, width, height, view.getVirtualBounds(),
-                            presenter);
+                    mClient.requestShowFillUi(
+                            this.id, id, width, height, view.getVirtualBounds(), presenter);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Error requesting to show fill UI", e);
                 }
             } else {
                 if (sDebug) {
-                    Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view ("
-                            + mCurrentViewId + ") anymore");
+                    Slog.d(
+                            TAG,
+                            "Do not show full UI on "
+                                    + id
+                                    + " as it is not the current view ("
+                                    + mCurrentViewId
+                                    + ") anymore");
                 }
             }
         }
@@ -2722,8 +2904,11 @@
     public void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent) {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#dispatchUnhandledKey() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#dispatchUnhandledKey() rejected - session: "
+                                + id
+                                + " destroyed");
                 return;
             }
             if (id.equals(mCurrentViewId)) {
@@ -2733,8 +2918,13 @@
                     Slog.e(TAG, "Error requesting to dispatch unhandled key", e);
                 }
             } else {
-                Slog.w(TAG, "Do not dispatch unhandled key on " + id
-                        + " as it is not the current view (" + mCurrentViewId + ") anymore");
+                Slog.w(
+                        TAG,
+                        "Do not dispatch unhandled key on "
+                                + id
+                                + " as it is not the current view ("
+                                + mCurrentViewId
+                                + ") anymore");
             }
         }
     }
@@ -2790,17 +2980,19 @@
     public void startIntentSender(IntentSender intentSender, Intent intent) {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#startIntentSender() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#startIntentSender() rejected - session: "
+                                + id
+                                + " destroyed");
                 return;
             }
             if (intent == null) {
                 removeFromServiceLocked();
             }
         }
-        mHandler.sendMessage(obtainMessage(
-                Session::doStartIntentSender,
-                this, intentSender, intent));
+        mHandler.sendMessage(
+                obtainMessage(Session::doStartIntentSender, this, intentSender, intent));
     }
 
     // AutoFillUiCallback
@@ -2865,13 +3057,17 @@
     @GuardedBy("mLock")
     void setAuthenticationResultLocked(Bundle data, int authenticationId) {
         if (mDestroyed) {
-            Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: "
-                    + id + " destroyed");
+            Slog.w(
+                    TAG,
+                    "Call to Session#setAuthenticationResultLocked() rejected - session: "
+                            + id
+                            + " destroyed");
             return;
         }
         if (sDebug) {
-            Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId
-                    + ", data=" + data);
+            Slog.d(
+                    TAG,
+                    "setAuthenticationResultLocked(): id= " + authenticationId + ", data=" + data);
         }
         final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
         if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
@@ -2890,9 +3086,10 @@
             removeFromService();
             return;
         }
-        final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId)
-                ? mSecondaryResponses.get(requestId)
-                : mResponses.get(requestId);
+        final FillResponse authenticatedResponse =
+                mRequestId.isSecondaryProvider(requestId)
+                        ? mSecondaryResponses.get(requestId)
+                        : mResponses.get(requestId);
         if (authenticatedResponse == null || data == null) {
             Slog.w(TAG, "no authenticated response");
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
@@ -2902,8 +3099,7 @@
             return;
         }
 
-        final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId(
-                authenticationId);
+        final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId(authenticationId);
         Dataset dataset = null;
         // Authenticated a dataset - reset view state regardless if we got a response or a dataset
         if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
@@ -2922,33 +3118,45 @@
         mSessionFlags.mExpiredResponse = false;
 
         final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
-        final GetCredentialException exception = data.getSerializable(
-                CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
-                GetCredentialException.class);
+        final GetCredentialException exception =
+                data.getSerializable(
+                        CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+                        GetCredentialException.class);
 
         final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
         if (sDebug) {
-            Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result
-                    + ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
+            Slog.d(
+                    TAG,
+                    "setAuthenticationResultLocked(): result="
+                            + result
+                            + ", clientState="
+                            + newClientState
+                            + ", authenticationId="
+                            + authenticationId);
         }
-        if (Flags.autofillCredmanDevIntegration() && exception != null
+        if (Flags.autofillCredmanDevIntegration()
+                && exception != null
                 && !exception.getType().equals(GetCredentialException.TYPE_USER_CANCELED)) {
             if (dataset != null && dataset.getFieldIds().size() == 1) {
                 if (sDebug) {
-                    Slog.d(TAG, "setAuthenticationResultLocked(): result returns with"
-                            + "Credential Manager Exception");
+                    Slog.d(
+                            TAG,
+                            "setAuthenticationResultLocked(): result returns with"
+                                    + "Credential Manager Exception");
                 }
                 AutofillId autofillId = dataset.getFieldIds().get(0);
-                sendCredentialManagerResponseToApp(/*response=*/ null,
-                        (GetCredentialException) exception, autofillId);
+                sendCredentialManagerResponseToApp(
+                        /* response= */ null, (GetCredentialException) exception, autofillId);
             }
             return;
         }
 
         if (result instanceof FillResponse) {
             if (sDebug) {
-                Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from"
-                        + " authentication flow");
+                Slog.d(
+                        TAG,
+                        "setAuthenticationResultLocked(): received FillResponse from"
+                                + " authentication flow");
             }
             logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
@@ -2963,33 +3171,40 @@
                 if (dataset != null && dataset.getFieldIds().size() == 1) {
                     AutofillId autofillId = dataset.getFieldIds().get(0);
                     if (sDebug) {
-                        Slog.d(TAG, "Received GetCredentialResponse from authentication flow,"
-                                + "for autofillId: " + autofillId);
+                        Slog.d(
+                                TAG,
+                                "Received GetCredentialResponse from authentication flow,"
+                                        + "for autofillId: "
+                                        + autofillId);
                     }
-                    sendCredentialManagerResponseToApp(response,
-                            /*exception=*/ null, autofillId);
+                    sendCredentialManagerResponseToApp(response, /* exception= */ null, autofillId);
                 }
             } else if (Flags.autofillCredmanIntegration()) {
-                Dataset datasetFromCredentialResponse = getDatasetFromCredentialResponse(
-                        (GetCredentialResponse) result);
+                Dataset datasetFromCredentialResponse =
+                        getDatasetFromCredentialResponse((GetCredentialResponse) result);
                 if (datasetFromCredentialResponse != null) {
-                    autoFill(requestId, datasetIdx, datasetFromCredentialResponse,
-                            false, UI_TYPE_UNKNOWN);
+                    autoFill(
+                            requestId,
+                            datasetIdx,
+                            datasetFromCredentialResponse,
+                            false,
+                            UI_TYPE_UNKNOWN);
                 }
             }
         } else if (result instanceof Dataset) {
             if (sDebug) {
-                Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from"
-                        + " authentication flow");
+                Slog.d(
+                        TAG,
+                        "setAuthenticationResultLocked(): received Dataset from"
+                                + " authentication flow");
             }
             if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
-                logAuthenticationStatusLocked(requestId,
-                        MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
+                logAuthenticationStatusLocked(
+                        requestId, MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
                 mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                         AUTHENTICATION_RESULT_SUCCESS);
                 if (newClientState != null) {
-                    if (sDebug)
-                        Slog.d(TAG, "Updating client state from auth dataset");
+                    if (sDebug) Slog.d(TAG, "Updating client state from auth dataset");
                     mClientState = newClientState;
                 }
                 Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result);
@@ -2999,10 +3214,14 @@
                 }
                 autoFill(requestId, datasetIdx, datasetFromResult, false, UI_TYPE_UNKNOWN);
             } else {
-                Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id "
-                        + authenticationId);
-                logAuthenticationStatusLocked(requestId,
-                        MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
+                Slog.w(
+                        TAG,
+                        "invalid index ("
+                                + datasetIdx
+                                + ") for authentication id "
+                                + authenticationId);
+                logAuthenticationStatusLocked(
+                        requestId, MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
                 mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                         AUTHENTICATION_RESULT_FAILURE);
             }
@@ -3010,8 +3229,7 @@
             if (result != null) {
                 Slog.w(TAG, "service returned invalid auth type: " + result);
             }
-            logAuthenticationStatusLocked(requestId,
-                    MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
+            logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                     AUTHENTICATION_RESULT_FAILURE);
             processNullResponseLocked(requestId, 0);
@@ -3036,8 +3254,10 @@
             Slog.d(TAG, "DBG: authenticated effective response: " + response);
         }
         if (response == null || response.getDatasets().size() == 0) {
-            Log.wtf(TAG, "No datasets in fill response on authentication. response = "
-                    + (response == null ? "null" : response.toString()));
+            Log.wtf(
+                    TAG,
+                    "No datasets in fill response on authentication. response = "
+                            + (response == null ? "null" : response.toString()));
             return authenticatedDataset;
         }
         List<Dataset> datasets = response.getDatasets();
@@ -3047,8 +3267,10 @@
             for (Dataset dataset : datasets) {
                 if (!dataset.getFieldIds().isEmpty()) {
                     for (int i = 0; i < dataset.getFieldIds().size(); i++) {
-                        builder.setField(dataset.getFieldIds().get(i),
-                                new Field.Builder().setValue(dataset.getFieldValues().get(i))
+                        builder.setField(
+                                dataset.getFieldIds().get(i),
+                                new Field.Builder()
+                                        .setValue(dataset.getFieldValues().get(i))
                                         .build());
                     }
                 }
@@ -3063,12 +3285,11 @@
     }
 
     /**
-     * Returns whether the dataset returned from the authentication result is ephemeral or not.
-     * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more
-     * information.
+     * Returns whether the dataset returned from the authentication result is ephemeral or not. See
+     * {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more information.
      */
-    private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset,
-            @NonNull Bundle authResultData) {
+    private static boolean isAuthResultDatasetEphemeral(
+            @Nullable Dataset oldDataset, @NonNull Bundle authResultData) {
         if (authResultData.containsKey(
                 AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
             return authResultData.getBoolean(
@@ -3079,11 +3300,11 @@
 
     /**
      * A dataset can potentially have multiple fields, and it's possible that some of the fields'
-     * has inline presentation and some don't. It's also possible that some of the fields'
-     * inline presentation is pinned and some isn't. So the concept of whether a dataset is
-     * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a
-     * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient
-     * for most of the cases.
+     * has inline presentation and some don't. It's also possible that some of the fields' inline
+     * presentation is pinned and some isn't. So the concept of whether a dataset is pinned or not
+     * is ill-defined. Here we say a dataset is pinned if any of the field has a pinned inline
+     * presentation in the dataset. It's not ideal but hopefully it is sufficient for most of the
+     * cases.
      */
     private static boolean isPinnedDataset(@Nullable Dataset dataset) {
         if (dataset != null && dataset.getFieldIds() != null) {
@@ -3100,16 +3321,30 @@
 
     @GuardedBy("mLock")
     void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) {
-        final Dataset dataset = (data == null) ? null :
-                data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT, android.service.autofill.Dataset.class);
+        final Dataset dataset =
+                (data == null)
+                        ? null
+                        : data.getParcelable(
+                                AutofillManager.EXTRA_AUTHENTICATION_RESULT,
+                                android.service.autofill.Dataset.class);
         if (sDebug) {
-            Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id
-                    + ", authId=" + authId + ", dataset=" + dataset);
+            Slog.d(
+                    TAG,
+                    "Auth result for augmented autofill: sessionId="
+                            + id
+                            + ", authId="
+                            + authId
+                            + ", dataset="
+                            + dataset);
         }
-        final AutofillId fieldId = (dataset != null && dataset.getFieldIds().size() == 1)
-                ? dataset.getFieldIds().get(0) : null;
-        final AutofillValue value = (dataset != null && dataset.getFieldValues().size() == 1)
-                ? dataset.getFieldValues().get(0) : null;
+        final AutofillId fieldId =
+                (dataset != null && dataset.getFieldIds().size() == 1)
+                        ? dataset.getFieldIds().get(0)
+                        : null;
+        final AutofillValue value =
+                (dataset != null && dataset.getFieldValues().size() == 1)
+                        ? dataset.getFieldValues().get(0)
+                        : null;
         final ClipData content = (dataset != null) ? dataset.getFieldContent() : null;
         if (fieldId == null || (value == null && content == null)) {
             if (sDebug) {
@@ -3154,8 +3389,14 @@
 
         // Fill the value into the field.
         if (sDebug) {
-            Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value
-                    + ", content=" + content);
+            Slog.d(
+                    TAG,
+                    "Filling after auth: fieldId="
+                            + fieldId
+                            + ", value="
+                            + value
+                            + ", content="
+                            + content);
         }
         try {
             if (content != null) {
@@ -3164,8 +3405,15 @@
                 mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues(), true);
             }
         } catch (RemoteException e) {
-            Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value
-                    + ", content=" + content, e);
+            Slog.w(
+                    TAG,
+                    "Error filling after auth: fieldId="
+                            + fieldId
+                            + ", value="
+                            + value
+                            + ", content="
+                            + content,
+                    e);
         }
 
         // Clear the suggestions since the user already accepted one of them.
@@ -3175,8 +3423,11 @@
     @GuardedBy("mLock")
     void setHasCallbackLocked(boolean hasIt) {
         if (mDestroyed) {
-            Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: "
-                    + id + " destroyed");
+            Slog.w(
+                    TAG,
+                    "Call to Session#setHasCallbackLocked() rejected - session: "
+                            + id
+                            + " destroyed");
             return;
         }
         mHasCallback = hasIt;
@@ -3185,9 +3436,8 @@
     @GuardedBy("mLock")
     @Nullable
     private FillResponse getLastResponseLocked(@Nullable String logPrefixFmt) {
-        final String logPrefix = sDebug && logPrefixFmt != null
-                ? String.format(logPrefixFmt, this.id)
-                : null;
+        final String logPrefix =
+                sDebug && logPrefixFmt != null ? String.format(logPrefixFmt, this.id) : null;
         if (mContexts == null) {
             if (logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts");
             return null;
@@ -3204,16 +3454,28 @@
         final int lastResponseIdx = getLastResponseIndexLocked();
         if (lastResponseIdx < 0) {
             if (logPrefix != null) {
-                Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses
-                        + ", mViewStates=" + mViewStates);
+                Slog.w(
+                        TAG,
+                        logPrefix
+                                + ": did not get last response. mResponses="
+                                + mResponses
+                                + ", mViewStates="
+                                + mViewStates);
             }
             return null;
         }
 
         final FillResponse response = mResponses.valueAt(lastResponseIdx);
         if (sVerbose && logPrefix != null) {
-            Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts
-                    + ", mViewStates=" + mViewStates);
+            Slog.v(
+                    TAG,
+                    logPrefix
+                            + ": mResponses="
+                            + mResponses
+                            + ", mContexts="
+                            + mContexts
+                            + ", mViewStates="
+                            + mViewStates);
         }
         return response;
     }
@@ -3232,9 +3494,8 @@
     }
 
     /**
-     * Get statistic information of save info in current session. Specifically
-     *   1. how many save info the current session has.
-     *   2. How many distinct save data types current session has.
+     * Get statistic information of save info in current session. Specifically 1. how many save info
+     * the current session has. 2. How many distinct save data types current session has.
      *
      * @return SaveInfoStats returns the above two number in a SaveInfoStats object
      */
@@ -3255,12 +3516,21 @@
      */
     public void logContextCommitted() {
         if (sVerbose) {
-            Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN
-                    + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE);
+            Slog.v(
+                    TAG,
+                    "logContextCommitted ("
+                            + id
+                            + "): commit_reason:"
+                            + COMMIT_REASON_UNKNOWN
+                            + " no_save_reason:"
+                            + Event.NO_SAVE_UI_REASON_NONE);
         }
-        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
-                Event.NO_SAVE_UI_REASON_NONE,
-                COMMIT_REASON_UNKNOWN));
+        mHandler.sendMessage(
+                obtainMessage(
+                        Session::handleLogContextCommitted,
+                        this,
+                        Event.NO_SAVE_UI_REASON_NONE,
+                        COMMIT_REASON_UNKNOWN));
         synchronized (mLock) {
             logAllEventsLocked(COMMIT_REASON_UNKNOWN);
         }
@@ -3274,16 +3544,25 @@
      * @param saveDialogNotShowReason The reason why a save dialog was not shown.
      * @param commitReason The reason why context is committed.
      */
-
     @GuardedBy("mLock")
-    public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason,
-            @AutofillCommitReason int commitReason) {
+    public void logContextCommittedLocked(
+            @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) {
         if (sVerbose) {
-            Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
-                    + " no_save_reason:" + saveDialogNotShowReason);
+            Slog.v(
+                    TAG,
+                    "logContextCommittedLocked ("
+                            + id
+                            + "): commit_reason:"
+                            + commitReason
+                            + " no_save_reason:"
+                            + saveDialogNotShowReason);
         }
-        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
-                saveDialogNotShowReason, commitReason));
+        mHandler.sendMessage(
+                obtainMessage(
+                        Session::handleLogContextCommitted,
+                        this,
+                        saveDialogNotShowReason,
+                        commitReason));
 
         mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
         mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
@@ -3295,8 +3574,8 @@
         mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
     }
 
-    private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason,
-            @AutofillCommitReason int commitReason) {
+    private void handleLogContextCommitted(
+            @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) {
         final FillResponse lastResponse;
         synchronized (mLock) {
             lastResponse = getLastResponseLocked("logContextCommited(%s)");
@@ -3326,31 +3605,42 @@
 
         // Sets field classification scores
         if (userData != null && fcStrategy != null) {
-            logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason,
-                    commitReason);
+            logFieldClassificationScore(
+                    fcStrategy, userData, saveDialogNotShowReason, commitReason);
         } else {
             logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
         }
     }
 
-    private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
+    private void logContextCommitted(
+            @Nullable ArrayList<AutofillId> detectedFieldIds,
             @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
             @NoSaveReason int saveDialogNotShowReason,
             @AutofillCommitReason int commitReason) {
         synchronized (mLock) {
-            logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications,
-                    saveDialogNotShowReason, commitReason);
+            logContextCommittedLocked(
+                    detectedFieldIds,
+                    detectedFieldClassifications,
+                    saveDialogNotShowReason,
+                    commitReason);
         }
     }
 
     @GuardedBy("mLock")
-    private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
+    private void logContextCommittedLocked(
+            @Nullable ArrayList<AutofillId> detectedFieldIds,
             @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
             @NoSaveReason int saveDialogNotShowReason,
             @AutofillCommitReason int commitReason) {
         if (sVerbose) {
-            Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
-                    + " no_save_reason:" + saveDialogNotShowReason);
+            Slog.v(
+                    TAG,
+                    "logContextCommittedLocked ("
+                            + id
+                            + "): commit_reason:"
+                            + commitReason
+                            + " no_save_reason:"
+                            + saveDialogNotShowReason);
         }
         final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
         if (lastResponse == null) return;
@@ -3423,8 +3713,11 @@
                     final AutofillValue currentValue = viewState.getCurrentValue();
                     if (autofilledValue != null && autofilledValue.equals(currentValue)) {
                         if (sDebug) {
-                            Slog.d(TAG, "logContextCommitted(): ignoring changed " + viewState
-                                    + " because it has same value that was autofilled");
+                            Slog.d(
+                                    TAG,
+                                    "logContextCommitted(): ignoring changed "
+                                            + viewState
+                                            + " because it has same value that was autofilled");
                         }
                         continue;
                     }
@@ -3442,8 +3735,12 @@
                     final AutofillValue currentValue = viewState.getCurrentValue();
                     if (currentValue == null) {
                         if (sDebug) {
-                            Slog.d(TAG, "logContextCommitted(): skipping view without current "
-                                    + "value ( " + viewState + ")");
+                            Slog.d(
+                                    TAG,
+                                    "logContextCommitted(): skipping view without current "
+                                            + "value ( "
+                                            + viewState
+                                            + ")");
                         }
                         continue;
                     }
@@ -3455,7 +3752,7 @@
                             final List<Dataset> datasets = response.getDatasets();
                             if (datasets == null || datasets.isEmpty()) {
                                 if (sVerbose) {
-                                    Slog.v(TAG,  "logContextCommitted() no datasets at " + j);
+                                    Slog.v(TAG, "logContextCommitted() no datasets at " + j);
                                 }
                             } else {
                                 for (int k = 0; k < datasets.size(); k++) {
@@ -3463,8 +3760,11 @@
                                     final String datasetId = dataset.getId();
                                     if (datasetId == null) {
                                         if (sVerbose) {
-                                            Slog.v(TAG, "logContextCommitted() skipping idless "
-                                                    + "dataset " + dataset);
+                                            Slog.v(
+                                                    TAG,
+                                                    "logContextCommitted() skipping idless "
+                                                            + "dataset "
+                                                            + dataset);
                                         }
                                     } else {
                                         final ArrayList<AutofillValue> values =
@@ -3473,9 +3773,13 @@
                                             final AutofillValue candidate = values.get(l);
                                             if (currentValue.equals(candidate)) {
                                                 if (sDebug) {
-                                                    Slog.d(TAG, "field " + viewState.id + " was "
-                                                            + "manually filled with value set by "
-                                                            + "dataset " + datasetId);
+                                                    Slog.d(
+                                                            TAG,
+                                                            "field "
+                                                                    + viewState.id
+                                                                    + " was manually filled with"
+                                                                    + " value set by dataset "
+                                                                    + datasetId);
                                                 }
                                                 if (manuallyFilledIds == null) {
                                                     manuallyFilledIds = new ArrayMap<>();
@@ -3524,10 +3828,20 @@
             }
         }
 
-        mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
-                changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
-                manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
-                mComponentName, mCompatMode, saveDialogNotShowReason);
+        mService.logContextCommittedLocked(
+                id,
+                mClientState,
+                mSelectedDatasetIds,
+                ignoredDatasets,
+                changedFieldIds,
+                changedDatasetIds,
+                manuallyFilledFieldIds,
+                manuallyFilledDatasetIds,
+                detectedFieldIds,
+                detectedFieldClassifications,
+                mComponentName,
+                mCompatMode,
+                saveDialogNotShowReason);
         mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
         mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
         mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason);
@@ -3537,7 +3851,8 @@
      * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for
      * {@code fieldId} based on its {@code currentValue} and {@code userData}.
      */
-    private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
+    private void logFieldClassificationScore(
+            @NonNull FieldClassificationStrategy fcStrategy,
             @NonNull FieldClassificationUserData userData,
             @NoSaveReason int saveDialogNotShowReason,
             @AutofillCommitReason int commitReason) {
@@ -3555,16 +3870,20 @@
         if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) {
             final int valuesLength = userValues == null ? -1 : userValues.length;
             final int idsLength = categoryIds == null ? -1 : categoryIds.length;
-            Slog.w(TAG, "setScores(): user data mismatch: values.length = "
-                    + valuesLength + ", ids.length = " + idsLength);
+            Slog.w(
+                    TAG,
+                    "setScores(): user data mismatch: values.length = "
+                            + valuesLength
+                            + ", ids.length = "
+                            + idsLength);
             return;
         }
 
         final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize();
 
         final ArrayList<AutofillId> detectedFieldIds = new ArrayList<>(maxFieldsSize);
-        final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>(
-                maxFieldsSize);
+        final ArrayList<FieldClassification> detectedFieldClassifications =
+                new ArrayList<>(maxFieldsSize);
 
         final Collection<ViewState> viewStates;
         synchronized (mLock) {
@@ -3583,33 +3902,49 @@
         }
 
         // Then use the results, asynchronously
-        final RemoteCallback callback = new RemoteCallback(
-                new LogFieldClassificationScoreOnResultListener(
-                        this,
-                        saveDialogNotShowReason,
-                        commitReason,
-                        viewsSize,
-                        autofillIds,
-                        userValues,
-                        categoryIds,
-                        detectedFieldIds,
-                        detectedFieldClassifications));
+        final RemoteCallback callback =
+                new RemoteCallback(
+                        new LogFieldClassificationScoreOnResultListener(
+                                this,
+                                saveDialogNotShowReason,
+                                commitReason,
+                                viewsSize,
+                                autofillIds,
+                                userValues,
+                                categoryIds,
+                                detectedFieldIds,
+                                detectedFieldClassifications));
 
-        fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
-                defaultAlgorithm, defaultArgs, algorithms, args);
+        fcStrategy.calculateScores(
+                callback,
+                currentValues,
+                userValues,
+                categoryIds,
+                defaultAlgorithm,
+                defaultArgs,
+                algorithms,
+                args);
     }
 
-    void handleLogFieldClassificationScore(@Nullable Bundle result, int saveDialogNotShowReason,
-            int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues,
-            String[] categoryIds, ArrayList<AutofillId> detectedFieldIds,
+    void handleLogFieldClassificationScore(
+            @Nullable Bundle result,
+            int saveDialogNotShowReason,
+            int commitReason,
+            int viewsSize,
+            AutofillId[] autofillIds,
+            String[] userValues,
+            String[] categoryIds,
+            ArrayList<AutofillId> detectedFieldIds,
             ArrayList<FieldClassification> detectedFieldClassifications) {
         if (result == null) {
             if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
             logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
             return;
         }
-        final Scores scores = result.getParcelable(EXTRA_SCORES,
-                android.service.autofill.AutofillFieldClassificationService.Scores.class);
+        final Scores scores =
+                result.getParcelable(
+                        EXTRA_SCORES,
+                        android.service.autofill.AutofillFieldClassificationService.Scores.class);
         if (scores == null) {
             Slog.w(TAG, "No field classification score on " + result);
             return;
@@ -3633,14 +3968,24 @@
                         final Float currentScore = scoresByField.get(categoryId);
                         if (currentScore != null && currentScore > score) {
                             if (sVerbose) {
-                                Slog.v(TAG, "skipping score " + score
-                                        + " because it's less than " + currentScore);
+                                Slog.v(
+                                        TAG,
+                                        "skipping score "
+                                                + score
+                                                + " because it's less than "
+                                                + currentScore);
                             }
                             continue;
                         }
                         if (sVerbose) {
-                            Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
-                                    + autofillId);
+                            Slog.v(
+                                    TAG,
+                                    "adding score "
+                                            + score
+                                            + " at index "
+                                            + j
+                                            + " and id "
+                                            + autofillId);
                         }
                         scoresByField.put(categoryId, score);
                     } else if (sVerbose) {
@@ -3666,13 +4011,16 @@
             wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
             return;
         }
-        logContextCommitted(detectedFieldIds, detectedFieldClassifications,
-                saveDialogNotShowReason, commitReason);
+        logContextCommitted(
+                detectedFieldIds,
+                detectedFieldClassifications,
+                saveDialogNotShowReason,
+                commitReason);
     }
 
     /**
-     * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN}
-     * when necessary.
+     * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} when
+     * necessary.
      *
      * <p>Note: It is necessary to call logContextCommitted() first before calling this method.
      */
@@ -3689,11 +4037,14 @@
     @NonNull
     public SaveResult showSaveLocked() {
         if (mDestroyed) {
-            Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
-                    + id + " destroyed");
+            Slog.w(
+                    TAG,
+                    "Call to Session#showSaveLocked() rejected - session: " + id + " destroyed");
             mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED);
             mSaveEventLogger.logAndEndEvent();
-            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+            return new SaveResult(
+                    /* logSaveShown= */ false,
+                    /* removeSession= */ false,
                     Event.NO_SAVE_UI_REASON_NONE);
         }
         mSessionState = STATE_FINISHED;
@@ -3705,12 +4056,16 @@
          */
         if (mSessionFlags.mScreenHasCredmanField) {
             if (sVerbose) {
-                Slog.v(TAG, "Call to Session#showSaveLocked() rejected - "
-                        + "there is credman field in screen");
+                Slog.v(
+                        TAG,
+                        "Call to Session#showSaveLocked() rejected - "
+                                + "there is credman field in screen");
             }
             mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD);
             mSaveEventLogger.logAndEndEvent();
-            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+            return new SaveResult(
+                    /* logSaveShown= */ false,
+                    /* removeSession= */ true,
                     Event.NO_SAVE_UI_REASON_NONE);
         }
 
@@ -3728,7 +4083,9 @@
             if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service");
             mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO);
             mSaveEventLogger.logAndEndEvent();
-            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+            return new SaveResult(
+                    /* logSaveShown= */ false,
+                    /* removeSession= */ true,
                     Event.NO_SAVE_UI_REASON_NO_SAVE_INFO);
         }
 
@@ -3737,7 +4094,9 @@
             if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save");
             mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG);
             mSaveEventLogger.logAndEndEvent();
-            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+            return new SaveResult(
+                    /* logSaveShown= */ false,
+                    /* removeSession= */ false,
                     Event.NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG);
         }
 
@@ -3774,12 +4133,13 @@
                     // Some apps clear the form before navigating to other activities.
                     // If current value is empty, consider fall back to last cached
                     // non-empty result first.
-                    final AutofillValue candidateSaveValue =
-                            viewState.getCandidateSaveValue();
+                    final AutofillValue candidateSaveValue = viewState.getCandidateSaveValue();
                     if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
                         if (sVerbose) {
-                            Slog.v(TAG, "current value is empty, using cached last non-empty "
-                                    + "value instead");
+                            Slog.v(
+                                    TAG,
+                                    "current value is empty, using cached last non-empty "
+                                            + "value instead");
                         }
                         value = candidateSaveValue;
                     } else {
@@ -3788,8 +4148,14 @@
                         final AutofillValue initialValue = getValueFromContextsLocked(id);
                         if (initialValue != null) {
                             if (sDebug) {
-                                Slog.d(TAG, "Value of required field " + id + " didn't change; "
-                                        + "using initial value (" + initialValue + ") instead");
+                                Slog.d(
+                                        TAG,
+                                        "Value of required field "
+                                                + id
+                                                + " didn't change; "
+                                                + "using initial value ("
+                                                + initialValue
+                                                + ") instead");
                             }
                             value = initialValue;
                         } else {
@@ -3821,8 +4187,13 @@
                         final AutofillValue initialValue = getValueFromContextsLocked(id);
                         if (initialValue != null && initialValue.equals(value)) {
                             if (sDebug) {
-                                Slog.d(TAG, "id " + id + " is part of dataset but initial value "
-                                        + "didn't change: " + value);
+                                Slog.d(
+                                        TAG,
+                                        "id "
+                                                + id
+                                                + " is part of dataset but initial value "
+                                                + "didn't change: "
+                                                + value);
                             }
                             changed = false;
                         } else {
@@ -3833,8 +4204,14 @@
                     }
                     if (changed) {
                         if (sDebug) {
-                            Slog.d(TAG, "found a change on required " + id + ": " + filledValue
-                                    + " => " + value);
+                            Slog.d(
+                                    TAG,
+                                    "found a change on required "
+                                            + id
+                                            + ": "
+                                            + filledValue
+                                            + " => "
+                                            + value);
                         }
                         atLeastOneChanged = true;
                     }
@@ -3844,8 +4221,12 @@
 
         final AutofillId[] optionalIds = saveInfo.getOptionalIds();
         if (sVerbose) {
-            Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: "
-                    + (optionalIds != null));
+            Slog.v(
+                    TAG,
+                    "allRequiredAreNotEmpty: "
+                            + allRequiredAreNotEmpty
+                            + " hasOptional: "
+                            + (optionalIds != null));
         }
         int saveDialogNotShowReason;
         if (!allRequiredAreNotEmpty) {
@@ -3859,7 +4240,7 @@
             // - if at least one required id changed but it was not part of a filled dataset, we
             //   need to check if an optional id is part of a filled datased (in which case we show
             //   Update instead of Save)
-            if (optionalIds!= null && (!atLeastOneChanged || !isUpdate)) {
+            if (optionalIds != null && (!atLeastOneChanged || !isUpdate)) {
                 // No change on required ids yet, look for changes on optional ids.
                 for (int i = 0; i < optionalIds.length; i++) {
                     final AutofillId id = optionalIds[i];
@@ -3879,8 +4260,10 @@
                                     viewState.getCandidateSaveValue();
                             if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
                                 if (sVerbose) {
-                                    Slog.v(TAG, "current value is empty, using cached last "
-                                            + "non-empty value instead");
+                                    Slog.v(
+                                            TAG,
+                                            "current value is empty, using cached last "
+                                                    + "non-empty value instead");
                                 }
                                 currentValue = candidateSaveValue;
                             }
@@ -3897,8 +4280,14 @@
                         final AutofillValue filledValue = viewState.getAutofilledValue();
                         if (value != null && !value.equals(filledValue)) {
                             if (sDebug) {
-                                Slog.d(TAG, "found a change on optional " + id + ": " + filledValue
-                                        + " => " + value);
+                                Slog.d(
+                                        TAG,
+                                        "found a change on optional "
+                                                + id
+                                                + ": "
+                                                + filledValue
+                                                + " => "
+                                                + value);
                             }
                             if (filledValue != null) {
                                 isUpdate = true;
@@ -3907,12 +4296,16 @@
                             }
                             atLeastOneChanged = true;
                         }
-                    } else  {
+                    } else {
                         // Update current values cache based on initial value
                         final AutofillValue initialValue = getValueFromContextsLocked(id);
                         if (sDebug) {
-                            Slog.d(TAG, "no current value for " + id + "; initial value is "
-                                    + initialValue);
+                            Slog.d(
+                                    TAG,
+                                    "no current value for "
+                                            + id
+                                            + "; initial value is "
+                                            + initialValue);
                         }
                         if (initialValue != null) {
                             currentValues.put(id, initialValue);
@@ -3935,17 +4328,18 @@
                     try {
                         isValid = validator.isValid(this);
                         if (sDebug) Slog.d(TAG, validator + " returned " + isValid);
-                        log.setType(isValid
-                                ? MetricsEvent.TYPE_SUCCESS
-                                : MetricsEvent.TYPE_DISMISS);
+                        log.setType(
+                                isValid ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_DISMISS);
                     } catch (Exception e) {
                         Slog.e(TAG, "Not showing save UI because validation failed:", e);
                         log.setType(MetricsEvent.TYPE_FAILURE);
                         mMetricsLogger.write(log);
                         mSaveEventLogger.maybeSetSaveUiNotShownReason(
-                            NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
+                                NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
                         mSaveEventLogger.logAndEndEvent();
-                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                        return new SaveResult(
+                                /* logSaveShown= */ false,
+                                /* removeSession= */ true,
                                 Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED);
                     }
 
@@ -3953,9 +4347,11 @@
                     if (!isValid) {
                         Slog.i(TAG, "not showing save UI because fields failed validation");
                         mSaveEventLogger.maybeSetSaveUiNotShownReason(
-                            NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
+                                NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
                         mSaveEventLogger.logAndEndEvent();
-                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                        return new SaveResult(
+                                /* logSaveShown= */ false,
+                                /* removeSession= */ true,
                                 Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED);
                     }
                 }
@@ -3964,15 +4360,23 @@
                 // content.
                 final List<Dataset> datasets = response.getDatasets();
                 if (datasets != null) {
-                    datasets_loop: for (int i = 0; i < datasets.size(); i++) {
+                    datasets_loop:
+                    for (int i = 0; i < datasets.size(); i++) {
                         final Dataset dataset = datasets.get(i);
                         final ArrayMap<AutofillId, AutofillValue> datasetValues =
                                 Helper.getFields(dataset);
                         if (sVerbose) {
-                            Slog.v(TAG, "Checking if saved fields match contents of dataset #" + i
-                                    + ": " + dataset + "; savableIds=" + savableIds);
+                            Slog.v(
+                                    TAG,
+                                    "Checking if saved fields match contents of dataset #"
+                                            + i
+                                            + ": "
+                                            + dataset
+                                            + "; savableIds="
+                                            + savableIds);
                         }
-                        savable_ids_loop: for (int j = 0; j < savableIds.size(); j++) {
+                        savable_ids_loop:
+                        for (int j = 0; j < savableIds.size(); j++) {
                             final AutofillId id = savableIds.valueAt(j);
                             final AutofillValue currentValue = currentValues.get(id);
                             if (currentValue == null) {
@@ -3984,20 +4388,33 @@
                             final AutofillValue datasetValue = datasetValues.get(id);
                             if (!currentValue.equals(datasetValue)) {
                                 if (sDebug) {
-                                    Slog.d(TAG, "found a dataset change on id " + id + ": from "
-                                            + datasetValue + " to " + currentValue);
+                                    Slog.d(
+                                            TAG,
+                                            "found a dataset change on id "
+                                                    + id
+                                                    + ": from "
+                                                    + datasetValue
+                                                    + " to "
+                                                    + currentValue);
                                 }
                                 continue datasets_loop;
                             }
                             if (sVerbose) Slog.v(TAG, "no dataset changes for id " + id);
                         }
                         if (sDebug) {
-                            Slog.d(TAG, "ignoring Save UI because all fields match contents of "
-                                    + "dataset #" + i + ": " + dataset);
+                            Slog.d(
+                                    TAG,
+                                    "ignoring Save UI because all fields match contents of "
+                                            + "dataset #"
+                                            + i
+                                            + ": "
+                                            + dataset);
                         }
                         mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_DATASET_MATCH);
                         mSaveEventLogger.logAndEndEvent();
-                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                        return new SaveResult(
+                                /* logSaveShown= */ false,
+                                /* removeSession= */ true,
                                 Event.NO_SAVE_UI_REASON_DATASET_MATCH);
                     }
                 }
@@ -4015,13 +4432,26 @@
                     wtf(null, "showSaveLocked(): no service label or icon");
                     mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
                     mSaveEventLogger.logAndEndEvent();
-                    return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+                    return new SaveResult(
+                            /* logSaveShown= */ false,
+                            /* removeSession= */ true,
                             Event.NO_SAVE_UI_REASON_NONE);
                 }
-                getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
-                        mService.getServicePackageName(), saveInfo, this,
-                        mComponentName, this, mContext,  mPendingSaveUi, isUpdate, mCompatMode,
-                        response.getShowSaveDialogIcon(), mSaveEventLogger);
+                getUiForShowing()
+                        .showSaveUi(
+                                serviceLabel,
+                                serviceIcon,
+                                mService.getServicePackageName(),
+                                saveInfo,
+                                this,
+                                mComponentName,
+                                this,
+                                mContext,
+                                mPendingSaveUi,
+                                isUpdate,
+                                mCompatMode,
+                                response.getShowSaveDialogIcon(),
+                                mSaveEventLogger);
                 if (client != null) {
                     try {
                         client.setSaveUiState(id, true);
@@ -4031,21 +4461,30 @@
                 }
                 mSessionFlags.mShowingSaveUi = true;
                 if (sDebug) {
-                    Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for "
-                            + id + "!");
+                    Slog.d(
+                            TAG,
+                            "Good news, everyone! All checks passed, show save UI for " + id + "!");
                 }
-                return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false,
+                return new SaveResult(
+                        /* logSaveShown= */ true,
+                        /* removeSession= */ false,
                         Event.NO_SAVE_UI_REASON_NONE);
             }
         }
         // Nothing changed...
         if (sDebug) {
-            Slog.d(TAG, "showSaveLocked(" + id +"): with no changes, comes no responsibilities."
-                    + "allRequiredAreNotNull=" + allRequiredAreNotEmpty
-                    + ", atLeastOneChanged=" + atLeastOneChanged);
+            Slog.d(
+                    TAG,
+                    "showSaveLocked("
+                            + id
+                            + "): with no changes, comes no responsibilities."
+                            + "allRequiredAreNotNull="
+                            + allRequiredAreNotEmpty
+                            + ", atLeastOneChanged="
+                            + atLeastOneChanged);
         }
-        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
-                saveDialogNotShowReason);
+        return new SaveResult(
+                /* logSaveShown= */ false, /* removeSession= */ true, saveDialogNotShowReason);
     }
 
     private void logSaveShown() {
@@ -4078,25 +4517,21 @@
         return sanitized;
     }
 
-    /**
-     * Returns whether the session is currently showing the save UI
-     */
+    /** Returns whether the session is currently showing the save UI */
     @GuardedBy("mLock")
     boolean isSaveUiShowingLocked() {
         return mSessionFlags.mShowingSaveUi;
     }
 
-    /**
-     * Gets the latest non-empty value for the given id in the autofill contexts.
-     */
+    /** Gets the latest non-empty value for the given id in the autofill contexts. */
     @GuardedBy("mLock")
     @Nullable
     private ViewNode getViewNodeFromContextsLocked(@NonNull AutofillId autofillId) {
         final int numContexts = mContexts.size();
         for (int i = numContexts - 1; i >= 0; i--) {
             final FillContext context = mContexts.get(i);
-            final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
-                    autofillId);
+            final ViewNode node =
+                    Helper.findViewNodeByAutofillId(context.getStructure(), autofillId);
             if (node != null) {
                 return node;
             }
@@ -4104,22 +4539,28 @@
         return null;
     }
 
-    /**
-     * Gets the latest non-empty value for the given id in the autofill contexts.
-     */
+    /** Gets the latest non-empty value for the given id in the autofill contexts. */
     @GuardedBy("mLock")
     @Nullable
     private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) {
         final int numContexts = mContexts.size();
         for (int i = numContexts - 1; i >= 0; i--) {
             final FillContext context = mContexts.get(i);
-            final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
-                    autofillId);
+            final ViewNode node =
+                    Helper.findViewNodeByAutofillId(context.getStructure(), autofillId);
             if (node != null) {
                 final AutofillValue value = node.getAutofillValue();
                 if (sDebug) {
-                    Slog.d(TAG, "getValueFromContexts(" + this.id + "/" + autofillId + ") at "
-                            + i + ": " + value);
+                    Slog.d(
+                            TAG,
+                            "getValueFromContexts("
+                                    + this.id
+                                    + "/"
+                                    + autofillId
+                                    + ") at "
+                                    + i
+                                    + ": "
+                                    + value);
                 }
                 if (value != null && !value.isEmpty()) {
                     return value;
@@ -4129,17 +4570,15 @@
         return null;
     }
 
-    /**
-     * Gets the latest autofill options for the given id in the autofill contexts.
-     */
+    /** Gets the latest autofill options for the given id in the autofill contexts. */
     @GuardedBy("mLock")
     @Nullable
     private CharSequence[] getAutofillOptionsFromContextsLocked(@NonNull AutofillId autofillId) {
         final int numContexts = mContexts.size();
         for (int i = numContexts - 1; i >= 0; i--) {
             final FillContext context = mContexts.get(i);
-            final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
-                    autofillId);
+            final ViewNode node =
+                    Helper.findViewNodeByAutofillId(context.getStructure(), autofillId);
             if (node != null && node.getAutofillOptions() != null) {
                 return node.getAutofillOptions();
             }
@@ -4160,7 +4599,7 @@
             final FillContext context = mContexts.get(contextNum);
 
             final ViewNode[] nodes =
-                context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
+                    context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
 
             if (sVerbose) Slog.v(TAG, "updateValuesForSaveLocked(): updating " + context);
 
@@ -4191,8 +4630,11 @@
                 if (sanitizedValue != null) {
                     node.updateAutofillValue(sanitizedValue);
                 } else if (sDebug) {
-                    Slog.d(TAG, "updateValuesForSaveLocked(): not updating field " + id
-                            + " because it failed sanitization");
+                    Slog.d(
+                            TAG,
+                            "updateValuesForSaveLocked(): not updating field "
+                                    + id
+                                    + " because it failed sanitization");
                 }
             }
 
@@ -4200,28 +4642,33 @@
             context.getStructure().sanitizeForParceling(false);
 
             if (sVerbose) {
-                Slog.v(TAG, "updateValuesForSaveLocked(): dumping structure of " + context
-                        + " before calling service.save()");
+                Slog.v(
+                        TAG,
+                        "updateValuesForSaveLocked(): dumping structure of "
+                                + context
+                                + " before calling service.save()");
                 context.getStructure().dump(false);
             }
         }
     }
 
-    /**
-     * Calls service when user requested save.
-     */
+    /** Calls service when user requested save. */
     @GuardedBy("mLock")
     void callSaveLocked() {
         if (mDestroyed) {
-            Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: "
-                    + id + " destroyed");
+            Slog.w(
+                    TAG,
+                    "Call to Session#callSaveLocked() rejected - session: " + id + " destroyed");
             mSaveEventLogger.maybeSetIsSaved(false);
             mSaveEventLogger.logAndEndEvent();
             return;
         }
         if (mRemoteFillService == null) {
-            wtf(null, "callSaveLocked() called without a remote service. "
-                    + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+            wtf(
+                    null,
+                    "callSaveLocked() called without a remote service. "
+                            + "mForAugmentedAutofillOnly: %s",
+                    mSessionFlags.mAugmentedAutofillOnly);
             mSaveEventLogger.maybeSetIsSaved(false);
             mSaveEventLogger.logAndEndEvent();
             return;
@@ -4241,7 +4688,7 @@
         // Remove pending fill requests as the session is finished.
         cancelCurrentRequestLocked();
 
-        final ArrayList<FillContext> contexts = mergePreviousSessionLocked( /* forSave= */ true);
+        final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ true);
 
         FieldClassificationResponse fieldClassificationResponse =
                 mClassificationState.mLastFieldClassificationResponse;
@@ -4251,8 +4698,9 @@
             if (mClientState == null) {
                 mClientState = new Bundle();
             }
-            mClientState.putParcelableArrayList(EXTRA_KEY_DETECTIONS, new ArrayList<>(
-                    fieldClassificationResponse.getClassifications()));
+            mClientState.putParcelableArrayList(
+                    EXTRA_KEY_DETECTIONS,
+                    new ArrayList<>(fieldClassificationResponse.getClassifications()));
         }
         final SaveRequest saveRequest =
                 new SaveRequest(contexts, mClientState, mSelectedDatasetIds);
@@ -4270,11 +4718,12 @@
      * from previous sessions that were asked by the service to be delayed (if any).
      *
      * <p>As a side-effect:
+     *
      * <ul>
-     *   <li>If the current {@link #mClientState} is {@code null}, sets it with the last non-
-     *   {@code null} client state from previous sessions.
+     *   <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- {@code
+     *       null} client state from previous sessions.
      *   <li>When {@code forSave} is {@code true}, calls {@link #updateValuesForSaveLocked()} in the
-     *   previous sessions.
+     *       previous sessions.
      * </ul>
      */
     @NonNull
@@ -4283,30 +4732,51 @@
         final ArrayList<FillContext> contexts;
         if (previousSessions != null) {
             if (sDebug) {
-                Slog.d(TAG, "mergeSessions(" + this.id + "): Merging the content of "
-                        + previousSessions.size() + " sessions for task " + taskId);
+                Slog.d(
+                        TAG,
+                        "mergeSessions("
+                                + this.id
+                                + "): Merging the content of "
+                                + previousSessions.size()
+                                + " sessions for task "
+                                + taskId);
             }
             contexts = new ArrayList<>();
             for (int i = 0; i < previousSessions.size(); i++) {
                 final Session previousSession = previousSessions.get(i);
                 final ArrayList<FillContext> previousContexts = previousSession.mContexts;
                 if (previousContexts == null) {
-                    Slog.w(TAG, "mergeSessions(" + this.id + "): Not merging null contexts from "
-                            + previousSession.id);
+                    Slog.w(
+                            TAG,
+                            "mergeSessions("
+                                    + this.id
+                                    + "): Not merging null contexts from "
+                                    + previousSession.id);
                     continue;
                 }
                 if (forSave) {
                     previousSession.updateValuesForSaveLocked();
                 }
                 if (sDebug) {
-                    Slog.d(TAG, "mergeSessions(" + this.id + "): adding " + previousContexts.size()
-                            + " context from previous session #" + previousSession.id);
+                    Slog.d(
+                            TAG,
+                            "mergeSessions("
+                                    + this.id
+                                    + "): adding "
+                                    + previousContexts.size()
+                                    + " context from previous session #"
+                                    + previousSession.id);
                 }
                 contexts.addAll(previousContexts);
                 if (mClientState == null && previousSession.mClientState != null) {
                     if (sDebug) {
-                        Slog.d(TAG, "mergeSessions(" + this.id + "): setting client state from "
-                                + "previous session" + previousSession.id);
+                        Slog.d(
+                                TAG,
+                                "mergeSessions("
+                                        + this.id
+                                        + "): setting client state from "
+                                        + "previous session"
+                                        + previousSession.id);
                     }
                     mClientState = previousSession.mClientState;
                 }
@@ -4351,8 +4821,12 @@
         // If it's not, then check if it should start a partition.
         if (shouldStartNewPartitionLocked(id, flags)) {
             if (sDebug) {
-                Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
-                        + viewState.getStateAsString());
+                Slog.d(
+                        TAG,
+                        "Starting partition or augmented request for view id "
+                                + id
+                                + ": "
+                                + viewState.getStateAsString());
             }
             // Fix to always let standard autofill start.
             // Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as
@@ -4363,8 +4837,12 @@
         }
 
         if (sVerbose) {
-            Slog.v(TAG, "Not starting new partition for view " + id + ": "
-                    + viewState.getStateAsString());
+            Slog.v(
+                    TAG,
+                    "Not starting new partition for view "
+                            + id
+                            + ": "
+                            + viewState.getStateAsString());
         }
         return Optional.empty();
     }
@@ -4373,17 +4851,17 @@
      * Determines if a new partition should be started for an id.
      *
      * @param id The id of the view that is entered
-     *
      * @return {@code true} if a new partition should be started
      */
     @GuardedBy("mLock")
     private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) {
         final ViewState currentView = mViewStates.get(id);
-        SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags)
-                ? mSecondaryResponses : mResponses;
+        SparseArray<FillResponse> responses =
+                shouldRequestSecondaryProvider(flags) ? mSecondaryResponses : mResponses;
         if (responses == null) {
-            return currentView != null && (currentView.getState()
-                    & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0;
+            return currentView != null
+                    && (currentView.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST)
+                            == 0;
         }
 
         if (mSessionFlags.mExpiredResponse) {
@@ -4395,8 +4873,14 @@
 
         final int numResponses = responses.size();
         if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
-            Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
-                    + " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
+            Slog.e(
+                    TAG,
+                    "Not starting a new partition on "
+                            + id
+                            + " because session "
+                            + this.id
+                            + " reached maximum of "
+                            + AutofillManagerService.getPartitionMaxCount());
             return false;
         }
 
@@ -4437,8 +4921,7 @@
     }
 
     boolean shouldRequestSecondaryProvider(int flags) {
-        if (!mService.isAutofillCredmanIntegrationEnabled()
-                || mSecondaryProviderHandler == null) {
+        if (!mService.isAutofillCredmanIntegrationEnabled() || mSecondaryProviderHandler == null) {
             return false;
         }
         if (mIsPrimaryCredential) {
@@ -4452,8 +4935,8 @@
     // 'Session.this.mLock', which is the same as mLock.
     @SuppressWarnings("GuardedBy")
     @GuardedBy("mLock")
-    void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action,
-            int flags) {
+    void updateLocked(
+            AutofillId id, Rect virtualBounds, AutofillValue value, int action, int flags) {
         if (mDestroyed) {
             Slog.w(TAG, "updateLocked(" + id + "):  rejected - session: destroyed");
             return;
@@ -4464,7 +4947,7 @@
                 Slog.d(TAG, "updateLocked(" + id + "): Set the response has expired.");
             }
             mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
-                        NOT_SHOWN_REASON_VIEW_CHANGED);
+                    NOT_SHOWN_REASON_VIEW_CHANGED);
             mPresentationStatsEventLogger.logAndEndEvent("ACTION_RESPONSE_EXPIRED");
             return;
         }
@@ -4474,23 +4957,35 @@
         if (sVerbose) {
             Slog.v(
                     TAG,
-                    "updateLocked(" + id + "): "
-                            + "id=" + this.id
-                            + ", action=" + actionAsString(action)
-                            + ", flags=" + flags
-                            + ", mCurrentViewId=" + mCurrentViewId
-                            + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse
-                            + ", viewState=" + viewState);
+                    "updateLocked("
+                            + id
+                            + "): "
+                            + "id="
+                            + this.id
+                            + ", action="
+                            + actionAsString(action)
+                            + ", flags="
+                            + flags
+                            + ", mCurrentViewId="
+                            + mCurrentViewId
+                            + ", mExpiredResponse="
+                            + mSessionFlags.mExpiredResponse
+                            + ", viewState="
+                            + viewState);
         }
 
         if (viewState == null) {
-            if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED
+            if (action == ACTION_START_SESSION
+                    || action == ACTION_VALUE_CHANGED
                     || action == ACTION_VIEW_ENTERED) {
                 if (sVerbose) Slog.v(TAG, "Creating viewState for " + id);
                 boolean isIgnored = isIgnoredLocked(id);
-                viewState = new ViewState(id, this,
-                        isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL,
-                        mIsPrimaryCredential);
+                viewState =
+                        new ViewState(
+                                id,
+                                this,
+                                isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL,
+                                mIsPrimaryCredential);
                 mViewStates.put(id, viewState);
 
                 // TODO(b/73648631): for optimization purposes, should also ignore if change is
@@ -4521,7 +5016,7 @@
             mSessionFlags.mScreenHasCredmanField = true;
         }
 
-        switch(action) {
+        switch (action) {
             case ACTION_START_SESSION:
                 // View is triggering autofill.
                 mCurrentViewId = viewState.id;
@@ -4545,14 +5040,14 @@
             case ACTION_VALUE_CHANGED:
                 if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
                     // Must cancel the session if the value of the URL bar changed
-                    final String currentUrl = mUrlBar == null ? null
-                            : mUrlBar.getText().toString().trim();
+                    final String currentUrl =
+                            mUrlBar == null ? null : mUrlBar.getText().toString().trim();
                     if (currentUrl == null) {
                         // Validation check - shouldn't happen.
                         wtf(null, "URL bar value changed, but current value is null");
                         return;
                     }
-                    if (value == null || ! value.isText()) {
+                    if (value == null || !value.isText()) {
                         // Validation check - shouldn't happen.
                         wtf(null, "URL bar value changed to null or non-text: %s", value);
                         return;
@@ -4567,8 +5062,10 @@
                         // are finished, as the URL bar changed callback is usually called before
                         // the virtual views become invisible.
                         if (sDebug) {
-                            Slog.d(TAG, "Ignoring change on URL because session will finish when "
-                                    + "views are gone");
+                            Slog.d(
+                                    TAG,
+                                    "Ignoring change on URL because session will finish when "
+                                            + "views are gone");
                         }
                         return;
                     }
@@ -4598,8 +5095,9 @@
                 // isSameViewEntered has some limitations, where it isn't considered same view when
                 // autofill suggestions pop up, user selects, and the focus lands back on the view.
                 // isSameViewAgain tries to overcome that situation.
-                final boolean isSameViewAgain = isSameViewEntered
-                        || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId);
+                final boolean isSameViewAgain =
+                        isSameViewEntered
+                                || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId);
                 if (mCurrentViewId != null) {
                     mPreviousNonNullEnteredViewId = mCurrentViewId;
                 }
@@ -4655,8 +5153,8 @@
                 // Trigger augmented autofill if applicable
                 if ((flags & FLAG_MANUAL_REQUEST) == 0) {
                     // Not a manual request
-                    if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(
-                            id)) {
+                    if (mAugmentedAutofillableIds != null
+                            && mAugmentedAutofillableIds.contains(id)) {
                         // Regular autofill handled the view and returned null response, but it
                         // triggered augmented autofill
                         if (!isSameViewEntered) {
@@ -4664,16 +5162,20 @@
                             triggerAugmentedAutofillLocked(flags);
                         } else {
                             if (sDebug) {
-                                Slog.d(TAG, "skip augmented autofill for same view: "
-                                        + "same view entered");
+                                Slog.d(
+                                        TAG,
+                                        "skip augmented autofill for same view: "
+                                                + "same view entered");
                             }
                         }
                         return;
                     } else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) {
                         // Regular autofill is disabled.
                         if (sDebug) {
-                            Slog.d(TAG, "skip augmented autofill for same view: "
-                                    + "standard autofill disabled.");
+                            Slog.d(
+                                    TAG,
+                                    "skip augmented autofill for same view: "
+                                            + "standard autofill disabled.");
                         }
                         return;
                     }
@@ -4717,8 +5219,8 @@
                     // on the IME side if it arrives before the input view is finished on the IME.
                     mInlineSessionController.resetInlineFillUiLocked();
 
-                    if ((viewState.getState() &
-                            ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) != 0) {
+                    if ((viewState.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST)
+                            != 0) {
                         // View was exited before Inline Request sent back, do not set it to
                         // null yet to let onHandleAssistData finish processing
                     } else {
@@ -4728,7 +5230,7 @@
                     // It's not necessary that there's no more presentation for this view. It could
                     // be that the user chose some suggestion, in which case, view exits.
                     mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
-                                NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+                            NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
                 }
                 break;
             default:
@@ -4737,8 +5239,8 @@
     }
 
     @GuardedBy("mLock")
-    private void logPresentationStatsOnViewEnteredLocked(FillResponse response,
-            boolean isCredmanRequested) {
+    private void logPresentationStatsOnViewEnteredLocked(
+            FillResponse response, boolean isCredmanRequested) {
         mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
         mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
                 mFieldClassificationIdSnapshot);
@@ -4754,16 +5256,13 @@
 
     @GuardedBy("mLock")
     private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) {
-        if ((viewState.getState()
-                & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
+        if ((viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
             viewState.resetState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL);
             cancelAugmentedAutofillLocked();
         }
     }
 
-    /**
-     * Checks whether a view should be ignored.
-     */
+    /** Checks whether a view should be ignored. */
     @GuardedBy("mLock")
     private boolean isIgnoredLocked(AutofillId id) {
         // Always check the latest response only
@@ -4782,22 +5281,30 @@
                 && getSaveInfoLocked() != null) {
             final int length = viewState.getCurrentValue().getTextValue().length();
             if (sDebug) {
-                Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
-                        + length + " chars long");
+                Slog.d(
+                        TAG,
+                        "updateLocked("
+                                + id
+                                + "): resetting value that was "
+                                + length
+                                + " chars long");
             }
-            final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
-                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
+            final LogMaker log =
+                    newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
+                            .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
             mMetricsLogger.write(log);
         }
     }
 
     @GuardedBy("mLock")
-    private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
-            ViewState viewState, int flags) {
+    private void updateViewStateAndUiOnValueChangedLocked(
+            AutofillId id, AutofillValue value, ViewState viewState, int flags) {
         // Cache the last non-empty value for save purpose. Some apps clear the form before
         // navigating to other activities.
-        if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty())
-                && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText()
+        if (mIgnoreViewStateResetToEmpty
+                && (value == null || value.isEmpty())
+                && viewState.getCurrentValue() != null
+                && viewState.getCurrentValue().isText()
                 && viewState.getCurrentValue().getTextValue() != null
                 && viewState.getCurrentValue().getTextValue().length() > 1) {
             if (sVerbose) {
@@ -4875,8 +5382,8 @@
      * indicate the IME attempting to probe the potentially sensitive content of inline suggestions.
      */
     @GuardedBy("mLock")
-    private void updateFilteringStateOnValueChangedLocked(@Nullable String newTextValue,
-            ViewState viewState) {
+    private void updateFilteringStateOnValueChangedLocked(
+            @Nullable String newTextValue, ViewState viewState) {
         if (newTextValue == null) {
             // Don't just return here, otherwise the IME can circumvent this logic using non-text
             // values.
@@ -4901,18 +5408,22 @@
     }
 
     @Override
-    public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
-            @Nullable AutofillValue value, int flags) {
+    public void onFillReady(
+            @NonNull FillResponse response,
+            @NonNull AutofillId filledId,
+            @Nullable AutofillValue value,
+            int flags) {
         synchronized (mLock) {
             mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
                     mFieldClassificationIdSnapshot);
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#onFillReady() rejected - session: " + id + " destroyed");
                 mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED);
                 mSaveEventLogger.logAndEndEvent();
                 mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
-                    NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY);
+                        NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY);
                 mPresentationStatsEventLogger.logAndEndEvent("on fill ready");
                 return;
             }
@@ -4954,7 +5465,6 @@
             } else {
                 setFillDialogDisabled();
             }
-
         }
 
         if (response.supportsInlineSuggestions()) {
@@ -4971,10 +5481,20 @@
             }
         }
 
-        getUiForShowing().showFillUi(filledId, response, filterText,
-                mService.getServicePackageName(), mComponentName,
-                serviceLabel, serviceIcon, this, mContext, id, mCompatMode,
-                mService.getMaster().getMaxInputLengthForAutofill());
+        getUiForShowing()
+                .showFillUi(
+                        filledId,
+                        response,
+                        filterText,
+                        mService.getServicePackageName(),
+                        mComponentName,
+                        serviceLabel,
+                        serviceIcon,
+                        this,
+                        mContext,
+                        id,
+                        mCompatMode,
+                        mService.getMaster().getMaxInputLengthForAutofill());
 
         synchronized (mLock) {
             if (mUiShownTime == 0) {
@@ -4983,21 +5503,26 @@
                 final long duration = mUiShownTime - mStartTime;
 
                 if (sDebug) {
-                    final StringBuilder msg = new StringBuilder("1st UI for ")
-                            .append(mActivityToken)
-                            .append(" shown in ");
+                    final StringBuilder msg =
+                            new StringBuilder("1st UI for ")
+                                    .append(mActivityToken)
+                                    .append(" shown in ");
                     TimeUtils.formatDuration(duration, msg);
                     Slog.d(TAG, msg.toString());
                 }
-                final StringBuilder historyLog = new StringBuilder("id=").append(id)
-                        .append(" app=").append(mActivityToken)
-                        .append(" svc=").append(mService.getServicePackageName())
-                        .append(" latency=");
+                final StringBuilder historyLog =
+                        new StringBuilder("id=")
+                                .append(id)
+                                .append(" app=")
+                                .append(mActivityToken)
+                                .append(" svc=")
+                                .append(mService.getServicePackageName())
+                                .append(" latency=");
                 TimeUtils.formatDuration(duration, historyLog);
                 mUiLatencyHistory.log(historyLog.toString());
 
-                addTaggedDataToRequestLogLocked(response.getRequestId(),
-                        MetricsEvent.FIELD_AUTOFILL_DURATION, duration);
+                addTaggedDataToRequestLogLocked(
+                        response.getRequestId(), MetricsEvent.FIELD_AUTOFILL_DURATION, duration);
             }
         }
     }
@@ -5052,8 +5577,8 @@
         }
     }
 
-    private boolean requestShowFillDialog(FillResponse response,
-            AutofillId filledId, String filterText, int flags) {
+    private boolean requestShowFillDialog(
+            FillResponse response, AutofillId filledId, String filterText, int flags) {
         if (!isFillDialogUiEnabled()) {
             // Unsupported fill dialog UI
             if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled");
@@ -5079,7 +5604,6 @@
                 if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
                 return false;
             }
-
         }
 
         Drawable serviceIcon = null;
@@ -5087,21 +5611,31 @@
             serviceIcon = getServiceIcon(response);
         }
 
-        getUiForShowing().showFillDialog(filledId, response, filterText,
-                mService.getServicePackageName(), mComponentName, serviceIcon, this,
-                id, mCompatMode, mPresentationStatsEventLogger, mLock);
+        getUiForShowing()
+                .showFillDialog(
+                        filledId,
+                        response,
+                        filterText,
+                        mService.getServicePackageName(),
+                        mComponentName,
+                        serviceIcon,
+                        this,
+                        id,
+                        mCompatMode,
+                        mPresentationStatsEventLogger,
+                        mLock);
         return true;
     }
 
     /**
-     * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able
-     * to be fetched, use the default provider icon instead
+     * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able to
+     * be fetched, use the default provider icon instead
      *
      * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise
      */
     @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
-                                   // actually the same object as mLock.
-                                   // TODO: Expose mService.mLock or redesign instead.
+    // actually the same object as mLock.
+    // TODO: Expose mService.mLock or redesign instead.
     @GuardedBy("mLock")
     private Drawable getServiceIcon(FillResponse response) {
         Drawable serviceIcon = null;
@@ -5130,14 +5664,14 @@
     }
 
     /**
-     * Get the custom label that was passed through FillResponse. If the custom label
-     * wasn't able to be fetched, use the default provider icon instead
+     * Get the custom label that was passed through FillResponse. If the custom label wasn't able to
+     * be fetched, use the default provider icon instead
      *
      * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise
      */
     @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
-                                   // actually the same object as mLock.
-                                   // TODO: Expose mService.mLock or redesign instead.
+    // actually the same object as mLock.
+    // TODO: Expose mService.mLock or redesign instead.
     @GuardedBy("mLock")
     private CharSequence getServiceLabel(FillResponse response) {
         CharSequence serviceLabel = null;
@@ -5167,11 +5701,9 @@
         return serviceLabel;
     }
 
-    /**
-     * Returns whether we made a request to show inline suggestions.
-     */
-    private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
-            @Nullable String filterText) {
+    /** Returns whether we made a request to show inline suggestions. */
+    private boolean requestShowInlineSuggestionsLocked(
+            @NonNull FillResponse response, @Nullable String filterText) {
         if (mCurrentViewId == null) {
             Log.w(TAG, "requestShowInlineSuggestionsLocked(): no view currently focused");
             return false;
@@ -5199,89 +5731,122 @@
         }
 
         final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
-                new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
-                        filterText, remoteRenderService, userId, id);
-        InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
-                new InlineFillUi.InlineSuggestionUiCallback() {
-                    @Override
-                    public void autofill(@NonNull Dataset dataset, int datasetIndex) {
-                        fill(response.getRequestId(), datasetIndex, dataset, UI_TYPE_INLINE);
-                    }
+                new InlineFillUi.InlineFillUiInfo(
+                        inlineSuggestionsRequest.get(),
+                        focusedId,
+                        filterText,
+                        remoteRenderService,
+                        userId,
+                        id);
+        InlineFillUi inlineFillUi =
+                InlineFillUi.forAutofill(
+                        inlineFillUiInfo,
+                        response,
+                        new InlineFillUi.InlineSuggestionUiCallback() {
+                            @Override
+                            public void autofill(@NonNull Dataset dataset, int datasetIndex) {
+                                fill(
+                                        response.getRequestId(),
+                                        datasetIndex,
+                                        dataset,
+                                        UI_TYPE_INLINE);
+                            }
 
-                    @Override
-                    public void authenticate(int requestId, int datasetIndex) {
-                        Session.this.authenticate(response.getRequestId(), datasetIndex,
-                                response.getAuthentication(), response.getClientState(),
-                                UI_TYPE_INLINE);
-                    }
+                            @Override
+                            public void authenticate(int requestId, int datasetIndex) {
+                                Session.this.authenticate(
+                                        response.getRequestId(),
+                                        datasetIndex,
+                                        response.getAuthentication(),
+                                        response.getClientState(),
+                                        UI_TYPE_INLINE);
+                            }
 
-                    @Override
-                    public void startIntentSender(@NonNull IntentSender intentSender) {
-                        Session.this.startIntentSender(intentSender, new Intent());
-                    }
+                            @Override
+                            public void startIntentSender(@NonNull IntentSender intentSender) {
+                                Session.this.startIntentSender(intentSender, new Intent());
+                            }
 
-                    @Override
-                    public void onError() {
-                        synchronized (mLock) {
-                            mInlineSessionController.setInlineFillUiLocked(
-                                    InlineFillUi.emptyUi(focusedId));
-                        }
-                    }
+                            @Override
+                            public void onError() {
+                                synchronized (mLock) {
+                                    mInlineSessionController.setInlineFillUiLocked(
+                                            InlineFillUi.emptyUi(focusedId));
+                                }
+                            }
 
-                    @Override
-                    public void onInflate() {
-                        Session.this.onShown(UI_TYPE_INLINE, 1);
-                    }
-                }, mService.getMaster().getMaxInputLengthForAutofill());
+                            @Override
+                            public void onInflate() {
+                                Session.this.onShown(UI_TYPE_INLINE, 1);
+                            }
+                        },
+                        mService.getMaster().getMaxInputLengthForAutofill());
         return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
     }
 
     private ResultReceiver constructCredentialManagerCallback(int requestId) {
-        final ResultReceiver resultReceiver = new ResultReceiver(mHandler) {
-            final AutofillId mAutofillId = mCurrentViewId;
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
-                    Slog.d(TAG, "onReceiveResult from Credential Manager "
-                            + "bottom sheet with mCurrentViewId: " + mAutofillId);
-                    GetCredentialResponse getCredentialResponse =
-                            resultData.getParcelable(
-                                    CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
-                                    GetCredentialResponse.class);
+        final ResultReceiver resultReceiver =
+                new ResultReceiver(mHandler) {
+                    final AutofillId mAutofillId = mCurrentViewId;
 
-                    if (Flags.autofillCredmanDevIntegration()) {
-                        sendCredentialManagerResponseToApp(getCredentialResponse,
-                                /*exception=*/ null, mAutofillId);
-                    } else {
-                        Dataset datasetFromCredential = getDatasetFromCredentialResponse(
-                                getCredentialResponse);
-                        if (datasetFromCredential != null) {
-                            autoFill(requestId, /*datasetIndex=*/-1,
-                                    datasetFromCredential, false,
-                                    UI_TYPE_CREDMAN_BOTTOM_SHEET);
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
+                            Slog.d(
+                                    TAG,
+                                    "onReceiveResult from Credential Manager "
+                                            + "bottom sheet with mCurrentViewId: "
+                                            + mAutofillId);
+                            GetCredentialResponse getCredentialResponse =
+                                    resultData.getParcelable(
+                                            CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+                                            GetCredentialResponse.class);
+
+                            if (Flags.autofillCredmanDevIntegration()) {
+                                sendCredentialManagerResponseToApp(
+                                        getCredentialResponse, /* exception= */ null, mAutofillId);
+                            } else {
+                                Dataset datasetFromCredential =
+                                        getDatasetFromCredentialResponse(getCredentialResponse);
+                                if (datasetFromCredential != null) {
+                                    autoFill(
+                                            requestId,
+                                            /* datasetIndex= */ -1,
+                                            datasetFromCredential,
+                                            false,
+                                            UI_TYPE_CREDMAN_BOTTOM_SHEET);
+                                }
+                            }
+                        } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
+                            String[] exception =
+                                    resultData.getStringArray(
+                                            CredentialProviderService
+                                                    .EXTRA_GET_CREDENTIAL_EXCEPTION);
+                            if (exception != null && exception.length >= 2) {
+                                String errType = exception[0];
+                                String errMsg = exception[1];
+                                Slog.w(
+                                        TAG,
+                                        "Credman bottom sheet from pinned "
+                                                + "entry failed with: + "
+                                                + errType
+                                                + " , "
+                                                + errMsg);
+                                sendCredentialManagerResponseToApp(
+                                        /* response= */ null,
+                                        new GetCredentialException(errType, errMsg),
+                                        mAutofillId);
+                            }
+                        } else {
+                            Slog.d(
+                                    TAG,
+                                    "Unknown resultCode from credential "
+                                            + "manager bottom sheet: "
+                                            + resultCode);
                         }
                     }
-                } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
-                    String[] exception =  resultData.getStringArray(
-                            CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
-                    if (exception != null && exception.length >= 2) {
-                        String errType = exception[0];
-                        String errMsg = exception[1];
-                        Slog.w(TAG, "Credman bottom sheet from pinned "
-                                + "entry failed with: + " + errType + " , "
-                                + errMsg);
-                        sendCredentialManagerResponseToApp(/*response=*/ null,
-                                new GetCredentialException(errType, errMsg),
-                                mAutofillId);
-                    }
-                } else {
-                    Slog.d(TAG, "Unknown resultCode from credential "
-                            + "manager bottom sheet: " + resultCode);
-                }
-            }
-        };
-        ResultReceiver ipcFriendlyResultReceiver =
-                toIpcFriendlyResultReceiver(resultReceiver);
+                };
+        ResultReceiver ipcFriendlyResultReceiver = toIpcFriendlyResultReceiver(resultReceiver);
 
         return ipcFriendlyResultReceiver;
     }
@@ -5309,8 +5874,8 @@
         }
     }
 
-    private void notifyUnavailableToClient(int sessionFinishedState,
-            @Nullable ArrayList<AutofillId> autofillableIds) {
+    private void notifyUnavailableToClient(
+            int sessionFinishedState, @Nullable ArrayList<AutofillId> autofillableIds) {
         synchronized (mLock) {
             if (mCurrentViewId == null) return;
             try {
@@ -5374,27 +5939,25 @@
                 if (saveInfo.getRequiredIds() != null) {
                     Collections.addAll(trackedViews, saveInfo.getRequiredIds());
                     mSaveEventLogger.maybeSetSaveUiShownReason(
-                        SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE);
+                            SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE);
                 }
 
                 if (saveInfo.getOptionalIds() != null) {
                     Collections.addAll(trackedViews, saveInfo.getOptionalIds());
                     mSaveEventLogger.maybeSetSaveUiShownReason(
-                        SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE);
+                            SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE);
                 }
             }
             if ((flags & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) {
-                mSaveEventLogger.maybeSetSaveUiShownReason(
-                    SAVE_UI_SHOWN_REASON_UNKNOWN);
+                mSaveEventLogger.maybeSetSaveUiShownReason(SAVE_UI_SHOWN_REASON_UNKNOWN);
                 mSaveEventLogger.maybeSetSaveUiNotShownReason(
-                    NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG);
+                        NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG);
                 saveOnFinish = false;
             }
 
         } else {
             flags = 0;
-            mSaveEventLogger.maybeSetSaveUiNotShownReason(
-                NO_SAVE_REASON_NO_SAVE_INFO);
+            mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO);
             saveTriggerId = null;
         }
 
@@ -5427,21 +5990,35 @@
 
         try {
             if (sVerbose) {
-                Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews
-                        + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId
-                        + " saveOnFinish:" + saveOnFinish + " flags: " + flags
-                        + " hasSaveInfo: " + (saveInfo != null));
+                Slog.v(
+                        TAG,
+                        "updateTrackedIdsLocked(): trackedViews: "
+                                + trackedViews
+                                + " fillableIds: "
+                                + fillableIds
+                                + " triggerId: "
+                                + saveTriggerId
+                                + " saveOnFinish:"
+                                + saveOnFinish
+                                + " flags: "
+                                + flags
+                                + " hasSaveInfo: "
+                                + (saveInfo != null));
             }
-            mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
-                    saveOnFinish, toArray(fillableIds), saveTriggerId, hasAuthentication);
+            mClient.setTrackedViews(
+                    id,
+                    toArray(trackedViews),
+                    mSaveOnAllViewsInvisible,
+                    saveOnFinish,
+                    toArray(fillableIds),
+                    saveTriggerId,
+                    hasAuthentication);
         } catch (RemoteException e) {
             Slog.w(TAG, "Cannot set tracked ids", e);
         }
     }
 
-    /**
-     * Sets the state of views that failed to autofill.
-     */
+    /** Sets the state of views that failed to autofill. */
     @GuardedBy("mLock")
     void setAutofillFailureLocked(@NonNull List<AutofillId> ids, boolean isRefill) {
         if (sVerbose && !ids.isEmpty()) {
@@ -5464,9 +6041,7 @@
         mPresentationStatsEventLogger.maybeSetViewFillFailureCounts(ids, isRefill);
     }
 
-    /**
-     * Sets the state of views that failed to autofill.
-     */
+    /** Sets the state of views that failed to autofill. */
     @GuardedBy("mLock")
     void setViewAutofilledLocked(@NonNull AutofillId id) {
         if (sVerbose) {
@@ -5478,17 +6053,14 @@
         mPresentationStatsEventLogger.maybeAddSuccessId(id);
     }
 
-    /**
-     * Sets the state of views that failed to autofill.
-     */
+    /** Sets the state of views that failed to autofill. */
     void setNotifyNotExpiringResponseDuringAuth() {
         synchronized (mLock) {
             mPresentationStatsEventLogger.maybeSetNotifyNotExpiringResponseDuringAuth();
         }
     }
-    /**
-     * Sets the state of views that failed to autofill.
-     */
+
+    /** Sets the state of views that failed to autofill. */
     void setLogViewEnteredIgnoredDuringAuth() {
         synchronized (mLock) {
             mPresentationStatsEventLogger.notifyViewEnteredIgnoredDuringAuthCount();
@@ -5496,10 +6068,15 @@
     }
 
     @GuardedBy("mLock")
-    private void replaceResponseLocked(@NonNull FillResponse oldResponse,
-            @NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
+    private void replaceResponseLocked(
+            @NonNull FillResponse oldResponse,
+            @NonNull FillResponse newResponse,
+            @Nullable Bundle newClientState) {
         // Disassociate view states with the old response
-        setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, /* clearResponse= */ true,
+        setViewStatesLocked(
+                oldResponse,
+                ViewState.STATE_INITIAL,
+                /* clearResponse= */ true,
                 /* isPrimary= */ true);
         // Move over the id
         newResponse.setRequestId(oldResponse.getRequestId());
@@ -5519,7 +6096,7 @@
         final ArrayList<AutofillId> autofillableIds;
         if (context != null) {
             final AssistStructure structure = context.getStructure();
-            autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true);
+            autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */ true);
         } else {
             Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId);
             autofillableIds = null;
@@ -5535,8 +6112,13 @@
         mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags);
         if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) {
             if (sVerbose) {
-                Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot "
-                        + "be augmented. AutofillableIds: " + autofillableIds);
+                Slog.v(
+                        TAG,
+                        "canceling session "
+                                + id
+                                + " when service returned null and it cannot "
+                                + "be augmented. AutofillableIds: "
+                                + autofillableIds);
             }
             // Nothing to be done, but need to notify client.
             notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds);
@@ -5544,15 +6126,25 @@
         } else {
             if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
                 if (sVerbose) {
-                    Slog.v(TAG, "keeping session " + id + " when service returned null and "
-                            + "augmented service is disabled for password fields. "
-                            + "AutofillableIds: " + autofillableIds);
+                    Slog.v(
+                            TAG,
+                            "keeping session "
+                                    + id
+                                    + " when service returned null and "
+                                    + "augmented service is disabled for password fields. "
+                                    + "AutofillableIds: "
+                                    + autofillableIds);
                 }
                 mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
             } else {
                 if (sVerbose) {
-                    Slog.v(TAG, "keeping session " + id + " when service returned null but "
-                            + "it can be augmented. AutofillableIds: " + autofillableIds);
+                    Slog.v(
+                            TAG,
+                            "keeping session "
+                                    + id
+                                    + " when service returned null but "
+                                    + "it can be augmented. AutofillableIds: "
+                                    + autofillableIds);
                 }
             }
             mAugmentedAutofillableIds = autofillableIds;
@@ -5567,8 +6159,8 @@
     /**
      * Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
      *
-     * <p> The request may not have been sent when this method returns as it may be waiting for
-     * the inline suggestion request asynchronously.
+     * <p>The request may not have been sent when this method returns as it may be waiting for the
+     * inline suggestion request asynchronously.
      *
      * @return callback to destroy the autofill UI, or {@code null} if not supported.
      */
@@ -5582,8 +6174,8 @@
         }
 
         // Check if Smart Suggestions is supported...
-        final @SmartSuggestionMode int supportedModes = mService
-                .getSupportedSmartSuggestionModesLocked();
+        final @SmartSuggestionMode int supportedModes =
+                mService.getSupportedSmartSuggestionModesLocked();
         if (supportedModes == 0) {
             if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no supported modes");
             return null;
@@ -5591,8 +6183,8 @@
 
         // ...then if the service is set for the user
 
-        final RemoteAugmentedAutofillService remoteService = mService
-                .getRemoteAugmentedAutofillServiceLocked();
+        final RemoteAugmentedAutofillService remoteService =
+                mService.getRemoteAugmentedAutofillServiceLocked();
         if (remoteService == null) {
             if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user");
             return null;
@@ -5612,25 +6204,37 @@
             return null;
         }
 
-        final boolean isAllowlisted = mService
-                .isWhitelistedForAugmentedAutofillLocked(mComponentName);
+        final boolean isAllowlisted =
+                mService.isWhitelistedForAugmentedAutofillLocked(mComponentName);
 
         if (!isAllowlisted) {
             if (sVerbose) {
-                Slog.v(TAG, "triggerAugmentedAutofillLocked(): "
-                        + ComponentName.flattenToShortString(mComponentName) + " not whitelisted ");
+                Slog.v(
+                        TAG,
+                        "triggerAugmentedAutofillLocked(): "
+                                + ComponentName.flattenToShortString(mComponentName)
+                                + " not whitelisted ");
             }
-            logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
-                    mCurrentViewId, isAllowlisted, /* isInline= */ null);
+            logAugmentedAutofillRequestLocked(
+                    mode,
+                    remoteService.getComponentName(),
+                    mCurrentViewId,
+                    isAllowlisted,
+                    /* isInline= */ null);
             return null;
         }
 
         if (sVerbose) {
-            Slog.v(TAG, "calling Augmented Autofill Service ("
-                    + ComponentName.flattenToShortString(remoteService.getComponentName())
-                    + ") on view " + mCurrentViewId + " using suggestion mode "
-                    + getSmartSuggestionModeToString(mode)
-                    + " when server returned null for session " + this.id);
+            Slog.v(
+                    TAG,
+                    "calling Augmented Autofill Service ("
+                            + ComponentName.flattenToShortString(remoteService.getComponentName())
+                            + ") on view "
+                            + mCurrentViewId
+                            + " using suggestion mode "
+                            + getSmartSuggestionModeToString(mode)
+                            + " when server returned null for session "
+                            + this.id);
         }
         // Log FillRequest for Augmented Autofill.
         mFillRequestEventLogger.startLogForNewRequest();
@@ -5648,8 +6252,10 @@
         if (mAugmentedRequestsLogs == null) {
             mAugmentedRequestsLogs = new ArrayList<>();
         }
-        final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_AUGMENTED_REQUEST,
-                remoteService.getComponentName().getPackageName());
+        final LogMaker log =
+                newLogMaker(
+                        MetricsEvent.AUTOFILL_AUGMENTED_REQUEST,
+                        remoteService.getComponentName().getPackageName());
         mAugmentedRequestsLogs.add(log);
 
         final AutofillId focusedId = mCurrentViewId;
@@ -5715,8 +6321,7 @@
             }
             synchronized (session.mLock) {
                 session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
-                        mFocusedId, /*requestConsumer=*/ mRequestAugmentedAutofill,
-                        result);
+                        mFocusedId, /* requestConsumer= */ mRequestAugmentedAutofill, result);
             }
         }
     }
@@ -5741,19 +6346,17 @@
             mIsAllowlisted = isAllowlisted;
             mMode = mode;
             mCurrentValue = currentValue;
-
         }
+
         @Override
         public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) {
             Session session = mSessionWeakRef.get();
 
-            if (logIfSessionNull(
-                    session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) {
+            if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) {
                 return;
             }
             session.onAugmentedAutofillInlineSuggestionAccept(
                     inlineSuggestionsRequest, mFocusedId, mIsAllowlisted, mMode, mCurrentValue);
-
         }
     }
 
@@ -5770,8 +6373,7 @@
         public Boolean apply(InlineFillUi inlineFillUi) {
             Session session = mSessionWeakRef.get();
 
-            if (logIfSessionNull(
-                    session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) {
+            if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) {
                 return false;
             }
 
@@ -5801,8 +6403,9 @@
     }
 
     /**
-     * If the session is null or has been destroyed, log the error msg, and return true.
-     * This is a helper function intended to be called when de-referencing from a weak reference.
+     * If the session is null or has been destroyed, log the error msg, and return true. This is a
+     * helper function intended to be called when de-referencing from a weak reference.
+     *
      * @param session
      * @param logPrefix
      * @return true if the session is null, false otherwise.
@@ -5830,15 +6433,25 @@
         synchronized (mLock) {
             final RemoteAugmentedAutofillService remoteService =
                     mService.getRemoteAugmentedAutofillServiceLocked();
-            logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
-                    focussedId, isAllowlisted, inlineSuggestionsRequest != null);
-            remoteService.onRequestAutofillLocked(id, mClient,
-                    taskId, mComponentName, mActivityToken,
-                    AutofillId.withoutSession(focussedId), currentValue,
+            logAugmentedAutofillRequestLocked(
+                    mode,
+                    remoteService.getComponentName(),
+                    focussedId,
+                    isAllowlisted,
+                    inlineSuggestionsRequest != null);
+            remoteService.onRequestAutofillLocked(
+                    id,
+                    mClient,
+                    taskId,
+                    mComponentName,
+                    mActivityToken,
+                    AutofillId.withoutSession(focussedId),
+                    currentValue,
                     inlineSuggestionsRequest,
                     new AugmentedAutofillInlineSuggestionsResponseCallback(this),
                     new AugmentedAutofillErrorCallback(this),
-                    mService.getRemoteInlineSuggestionRenderServiceLocked(), userId);
+                    mService.getRemoteInlineSuggestionRenderServiceLocked(),
+                    userId);
         }
     }
 
@@ -5847,15 +6460,14 @@
             cancelAugmentedAutofillLocked();
 
             // Also cancel augmented in IME
-            mInlineSessionController.setInlineFillUiLocked(
-                    InlineFillUi.emptyUi(mCurrentViewId));
+            mInlineSessionController.setInlineFillUiLocked(InlineFillUi.emptyUi(mCurrentViewId));
         }
     }
 
     @GuardedBy("mLock")
     private void cancelAugmentedAutofillLocked() {
-        final RemoteAugmentedAutofillService remoteService = mService
-                .getRemoteAugmentedAutofillServiceLocked();
+        final RemoteAugmentedAutofillService remoteService =
+                mService.getRemoteAugmentedAutofillServiceLocked();
         if (remoteService == null) {
             Slog.w(TAG, "cancelAugmentedAutofillLocked(): no service for user");
             return;
@@ -5865,8 +6477,8 @@
     }
 
     @GuardedBy("mLock")
-    private void processResponseLocked(@NonNull FillResponse newResponse,
-            @Nullable Bundle newClientState, int flags) {
+    private void processResponseLocked(
+            @NonNull FillResponse newResponse, @Nullable Bundle newClientState, int flags) {
         // Make sure we are hiding the UI which will be shown
         // only if handling the current response requires it.
         mUi.hideAll(this);
@@ -5878,9 +6490,18 @@
 
         final int requestId = newResponse.getRequestId();
         if (sVerbose) {
-            Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId
-                    + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse
-                    + ",newClientState=" + newClientState);
+            Slog.v(
+                    TAG,
+                    "processResponseLocked(): mCurrentViewId="
+                            + mCurrentViewId
+                            + ",flags="
+                            + flags
+                            + ", reqId="
+                            + requestId
+                            + ", resp="
+                            + newResponse
+                            + ",newClientState="
+                            + newClientState);
         }
 
         if (mResponses == null) {
@@ -5892,8 +6513,9 @@
         mResponses.put(requestId, newResponse);
         mClientState = newClientState != null ? newClientState : newResponse.getClientState();
 
-        boolean webviewRequestedCredman = newClientState != null && newClientState.getBoolean(
-                WEBVIEW_REQUESTED_CREDENTIAL_KEY, false);
+        boolean webviewRequestedCredman =
+                newClientState != null
+                        && newClientState.getBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, false);
         List<Dataset> datasetList = newResponse.getDatasets();
 
         mPresentationStatsEventLogger.maybeSetWebviewRequestedCredential(webviewRequestedCredman);
@@ -5901,7 +6523,10 @@
         mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
         mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);
 
-        setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false,
+        setViewStatesLocked(
+                newResponse,
+                ViewState.STATE_FILLABLE,
+                /* clearResponse= */ false,
                 /* isPrimary= */ true);
         updateFillDialogTriggerIdsLocked();
         updateTrackedIdsLocked();
@@ -5914,12 +6539,10 @@
         currentView.maybeCallOnFillReady(flags);
     }
 
-    /**
-     * Sets the state of all views in the given response.
-     */
+    /** Sets the state of all views in the given response. */
     @GuardedBy("mLock")
-    private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse,
-                                     boolean isPrimary) {
+    private void setViewStatesLocked(
+            FillResponse response, int state, boolean clearResponse, boolean isPrimary) {
         final List<Dataset> datasets = response.getDatasets();
         if (datasets != null && !datasets.isEmpty()) {
             for (int i = 0; i < datasets.size(); i++) {
@@ -5964,12 +6587,14 @@
         }
     }
 
-    /**
-     * Sets the state and response of all views in the given dataset.
-     */
+    /** Sets the state and response of all views in the given dataset. */
     @GuardedBy("mLock")
-    private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset,
-            int state, boolean clearResponse, boolean isPrimary) {
+    private void setViewStatesLocked(
+            @Nullable FillResponse response,
+            @NonNull Dataset dataset,
+            int state,
+            boolean clearResponse,
+            boolean isPrimary) {
         final ArrayList<AutofillId> ids = dataset.getFieldIds();
         final ArrayList<AutofillValue> values = dataset.getFieldValues();
         for (int j = 0; j < ids.size(); j++) {
@@ -5989,10 +6614,10 @@
     }
 
     @GuardedBy("mLock")
-    private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state,
-            @Nullable AutofillValue value) {
+    private ViewState createOrUpdateViewStateLocked(
+            @NonNull AutofillId id, int state, @Nullable AutofillValue value) {
         ViewState viewState = mViewStates.get(id);
-        if (viewState != null)  {
+        if (viewState != null) {
             viewState.setState(state);
         } else {
             viewState = new ViewState(id, this, state, mIsPrimaryCredential);
@@ -6008,22 +6633,27 @@
         return viewState;
     }
 
-    void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent,
-            int uiType) {
+    void autoFill(
+            int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, int uiType) {
         if (sDebug) {
-            Slog.d(TAG, "autoFill(): requestId=" + requestId  + "; datasetIdx=" + datasetIndex
-                    + "; dataset=" + dataset);
+            Slog.d(
+                    TAG,
+                    "autoFill(): requestId="
+                            + requestId
+                            + "; datasetIdx="
+                            + datasetIndex
+                            + "; dataset="
+                            + dataset);
         }
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#autoFill() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(TAG, "Call to Session#autoFill() rejected - session: " + id + " destroyed");
                 return;
             }
             // Selected dataset id is logged regardless of authentication result.
             mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex);
             mPresentationStatsEventLogger.maybeSetSelectedDatasetPickReason(
-                dataset.getEligibleReason());
+                    dataset.getEligibleReason());
             // Autofill it directly...
             if (dataset.getAuthentication() == null) {
                 if (generateEvent) {
@@ -6039,10 +6669,14 @@
             // ...or handle authentication.
             mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType);
             mPresentationStatsEventLogger.maybeSetAuthenticationType(
-                AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
+                    AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
             // does not matter the value of isPrimary because null response won't be overridden.
-            setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH,
-                    /* clearResponse= */ false, /* isPrimary= */ true);
+            setViewStatesLocked(
+                    null,
+                    dataset,
+                    ViewState.STATE_WAITING_DATASET_AUTH,
+                    /* clearResponse= */ false,
+                    /* isPrimary= */ true);
             final Intent fillInIntent;
             if (dataset.getCredentialFillInIntent() != null && Flags.autofillCredmanIntegration()) {
                 Slog.d(TAG, "Setting credential fill intent");
@@ -6055,11 +6689,13 @@
                 forceRemoveFromServiceLocked();
                 return;
             }
-            final int authenticationId = AutofillManager.makeAuthenticationId(requestId,
-                    datasetIndex);
-            startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent,
-                    /* authenticateInline= */false);
-
+            final int authenticationId =
+                    AutofillManager.makeAuthenticationId(requestId, datasetIndex);
+            startAuthentication(
+                    authenticationId,
+                    dataset.getAuthentication(),
+                    fillInIntent,
+                    /* authenticateInline= */ false);
         }
     }
 
@@ -6072,13 +6708,17 @@
         final FillContext context = getFillContextByRequestIdLocked(requestId);
 
         if (context == null) {
-            wtf(null, "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s",
-                    requestId, mContexts);
+            wtf(
+                    null,
+                    "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s",
+                    requestId,
+                    mContexts);
             return null;
         }
         if (mLastInlineSuggestionsRequest != null
                 && mLastInlineSuggestionsRequest.first == requestId) {
-            fillInIntent.putExtra(AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST,
+            fillInIntent.putExtra(
+                    AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST,
                     mLastInlineSuggestionsRequest.second);
         }
         fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
@@ -6121,12 +6761,15 @@
         }
     }
 
-    private void startAuthentication(int authenticationId, IntentSender intent,
-            Intent fillInIntent, boolean authenticateInline) {
+    private void startAuthentication(
+            int authenticationId,
+            IntentSender intent,
+            Intent fillInIntent,
+            boolean authenticateInline) {
         try {
             synchronized (mLock) {
-                mClient.authenticate(id, authenticationId, intent, fillInIntent,
-                        authenticateInline);
+                mClient.authenticate(
+                        id, authenticationId, intent, fillInIntent, authenticateInline);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Error launching auth intent", e);
@@ -6139,22 +6782,18 @@
      * @hide
      */
     static final class SaveResult {
-        /**
-         * Whether to record the save dialog has been shown.
-         */
+        /** Whether to record the save dialog has been shown. */
         private boolean mLogSaveShown;
 
-        /**
-         * Whether to remove the session.
-         */
+        /** Whether to remove the session. */
         private boolean mRemoveSession;
 
-        /**
-         * The reason why a save dialog was not shown.
-         */
+        /** The reason why a save dialog was not shown. */
         @NoSaveReason private int mSaveDialogNotShowReason;
 
-        SaveResult(boolean logSaveShown, boolean removeSession,
+        SaveResult(
+                boolean logSaveShown,
+                boolean removeSession,
                 @NoSaveReason int saveDialogNotShowReason) {
             mLogSaveShown = logSaveShown;
             mRemoveSession = removeSession;
@@ -6218,15 +6857,19 @@
 
         @Override
         public String toString() {
-            return "SaveResult: [logSaveShown=" + mLogSaveShown
-                    + ", removeSession=" + mRemoveSession
-                    + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]";
+            return "SaveResult: [logSaveShown="
+                    + mLogSaveShown
+                    + ", removeSession="
+                    + mRemoveSession
+                    + ", saveDialogNotShowReason="
+                    + mSaveDialogNotShowReason
+                    + "]";
         }
     }
 
     /**
-     * Class maintaining the state of the requests to
-     * {@link android.service.assist.classification.FieldClassificationService}.
+     * Class maintaining the state of the requests to {@link
+     * android.service.assist.classification.FieldClassificationService}.
      */
     private static final class ClassificationState {
 
@@ -6234,18 +6877,16 @@
          * Initial state indicating that the request for classification hasn't been triggered yet.
          */
         private static final int STATE_INITIAL = 1;
-        /**
-         * Assist request has been triggered, but awaiting response.
-         */
+
+        /** Assist request has been triggered, but awaiting response. */
         private static final int STATE_PENDING_ASSIST_REQUEST = 2;
-        /**
-         * Classification request has been triggered, but awaiting response.
-         */
+
+        /** Classification request has been triggered, but awaiting response. */
         private static final int STATE_PENDING_REQUEST = 3;
-        /**
-         * Classification response has been received.
-         */
+
+        /** Classification response has been received. */
         private static final int STATE_RESPONSE = 4;
+
         /**
          * Classification state has been invalidated, and the last response may no longer be valid.
          * This could occur due to various reasons like views changing their layouts, becoming
@@ -6254,15 +6895,17 @@
          */
         private static final int STATE_INVALIDATED = 5;
 
-        @IntDef(prefix = { "STATE_" }, value = {
-                STATE_INITIAL,
-                STATE_PENDING_ASSIST_REQUEST,
-                STATE_PENDING_REQUEST,
-                STATE_RESPONSE,
-                STATE_INVALIDATED
-        })
+        @IntDef(
+                prefix = {"STATE_"},
+                value = {
+                    STATE_INITIAL,
+                    STATE_PENDING_ASSIST_REQUEST,
+                    STATE_PENDING_REQUEST,
+                    STATE_RESPONSE,
+                    STATE_INVALIDATED
+                })
         @Retention(RetentionPolicy.SOURCE)
-        @interface ClassificationRequestState{}
+        @interface ClassificationRequestState {}
 
         @GuardedBy("mLock")
         private @ClassificationRequestState int mState = STATE_INITIAL;
@@ -6284,8 +6927,8 @@
 
         /**
          * Typically, there would be a 1:1 mapping. However, in certain cases, we may have a hint
-         * being applicable to many types. An example of this being new/change password forms,
-         * where you need to confirm the passward twice.
+         * being applicable to many types. An example of this being new/change password forms, where
+         * you need to confirm the passward twice.
          */
         @GuardedBy("mLock")
         private ArrayMap<String, Set<AutofillId>> mHintsToAutofillIdMap;
@@ -6317,8 +6960,9 @@
 
         /**
          * Process the response received.
+         *
          * @return true if the response was processed, false otherwise. If there wasn't any
-         * response, yet this function was called, it would return false.
+         *     response, yet this function was called, it would return false.
          */
         @GuardedBy("mLock")
         private boolean processResponse() {
@@ -6358,7 +7002,9 @@
         }
 
         @GuardedBy("mLock")
-        private static void processDetections(Set<String> detections, AutofillId id,
+        private static void processDetections(
+                Set<String> detections,
+                AutofillId id,
                 ArrayMap<String, Set<AutofillId>> currentMap) {
             for (String detection : detections) {
                 Set<AutofillId> autofillIds;
@@ -6416,37 +7062,67 @@
         @Override
         public String toString() {
             return "ClassificationState: ["
-                    + "state=" + stateToString()
-                    + ", mPendingFieldClassificationRequest=" + mPendingFieldClassificationRequest
-                    + ", mLastFieldClassificationResponse=" + mLastFieldClassificationResponse
-                    + ", mClassificationHintsMap=" + mClassificationHintsMap
-                    + ", mClassificationGroupHintsMap=" + mClassificationGroupHintsMap
-                    + ", mHintsToAutofillIdMap=" + mHintsToAutofillIdMap
-                    + ", mGroupHintsToAutofillIdMap=" + mGroupHintsToAutofillIdMap
+                    + "state="
+                    + stateToString()
+                    + ", mPendingFieldClassificationRequest="
+                    + mPendingFieldClassificationRequest
+                    + ", mLastFieldClassificationResponse="
+                    + mLastFieldClassificationResponse
+                    + ", mClassificationHintsMap="
+                    + mClassificationHintsMap
+                    + ", mClassificationGroupHintsMap="
+                    + mClassificationGroupHintsMap
+                    + ", mHintsToAutofillIdMap="
+                    + mHintsToAutofillIdMap
+                    + ", mGroupHintsToAutofillIdMap="
+                    + mGroupHintsToAutofillIdMap
                     + "]";
         }
-
     }
 
     @Override
     public String toString() {
-        return "Session: [id=" + id + ", component=" + mComponentName
-                + ", state=" + sessionStateAsString(mSessionState) + "]";
+        return "Session: [id="
+                + id
+                + ", component="
+                + mComponentName
+                + ", state="
+                + sessionStateAsString(mSessionState)
+                + "]";
     }
 
     @GuardedBy("mLock")
     void dumpLocked(String prefix, PrintWriter pw) {
         final String prefix2 = prefix + "  ";
-        pw.print(prefix); pw.print("id: "); pw.println(id);
-        pw.print(prefix); pw.print("uid: "); pw.println(uid);
-        pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
-        pw.print(prefix); pw.print("flags: "); pw.println(mFlags);
-        pw.print(prefix); pw.print("displayId: "); pw.println(mContext.getDisplayId());
-        pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState));
-        pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName);
-        pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
-        pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime);
-        pw.print(prefix); pw.print("Time to show UI: ");
+        pw.print(prefix);
+        pw.print("id: ");
+        pw.println(id);
+        pw.print(prefix);
+        pw.print("uid: ");
+        pw.println(uid);
+        pw.print(prefix);
+        pw.print("taskId: ");
+        pw.println(taskId);
+        pw.print(prefix);
+        pw.print("flags: ");
+        pw.println(mFlags);
+        pw.print(prefix);
+        pw.print("displayId: ");
+        pw.println(mContext.getDisplayId());
+        pw.print(prefix);
+        pw.print("state: ");
+        pw.println(sessionStateAsString(mSessionState));
+        pw.print(prefix);
+        pw.print("mComponentName: ");
+        pw.println(mComponentName);
+        pw.print(prefix);
+        pw.print("mActivityToken: ");
+        pw.println(mActivityToken);
+        pw.print(prefix);
+        pw.print("mStartTime: ");
+        pw.println(mStartTime);
+        pw.print(prefix);
+        pw.print("Time to show UI: ");
         if (mUiShownTime == 0) {
             pw.println("N/A");
         } else {
@@ -6454,41 +7130,67 @@
             pw.println();
         }
         final int requestLogsSizes = mRequestLogs.size();
-        pw.print(prefix); pw.print("mSessionLogs: "); pw.println(requestLogsSizes);
+        pw.print(prefix);
+        pw.print("mSessionLogs: ");
+        pw.println(requestLogsSizes);
         for (int i = 0; i < requestLogsSizes; i++) {
             final int requestId = mRequestLogs.keyAt(i);
             final LogMaker log = mRequestLogs.valueAt(i);
-            pw.print(prefix2); pw.print('#'); pw.print(i); pw.print(": req=");
-            pw.print(requestId); pw.print(", log=" ); dumpRequestLog(pw, log); pw.println();
+            pw.print(prefix2);
+            pw.print('#');
+            pw.print(i);
+            pw.print(": req=");
+            pw.print(requestId);
+            pw.print(", log=");
+            dumpRequestLog(pw, log);
+            pw.println();
         }
-        pw.print(prefix); pw.print("mResponses: ");
+        pw.print(prefix);
+        pw.print("mResponses: ");
         if (mResponses == null) {
             pw.println("null");
         } else {
             pw.println(mResponses.size());
             for (int i = 0; i < mResponses.size(); i++) {
-                pw.print(prefix2); pw.print('#'); pw.print(i);
-                pw.print(' '); pw.println(mResponses.valueAt(i));
+                pw.print(prefix2);
+                pw.print('#');
+                pw.print(i);
+                pw.print(' ');
+                pw.println(mResponses.valueAt(i));
             }
         }
-        pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
-        pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
-        pw.print(prefix); pw.print("mShowingSaveUi: "); pw.println(mSessionFlags.mShowingSaveUi);
-        pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi);
+        pw.print(prefix);
+        pw.print("mCurrentViewId: ");
+        pw.println(mCurrentViewId);
+        pw.print(prefix);
+        pw.print("mDestroyed: ");
+        pw.println(mDestroyed);
+        pw.print(prefix);
+        pw.print("mShowingSaveUi: ");
+        pw.println(mSessionFlags.mShowingSaveUi);
+        pw.print(prefix);
+        pw.print("mPendingSaveUi: ");
+        pw.println(mPendingSaveUi);
         final int numberViews = mViewStates.size();
-        pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
+        pw.print(prefix);
+        pw.print("mViewStates size: ");
+        pw.println(mViewStates.size());
         for (int i = 0; i < numberViews; i++) {
-            pw.print(prefix); pw.print("ViewState at #"); pw.println(i);
+            pw.print(prefix);
+            pw.print("ViewState at #");
+            pw.println(i);
             mViewStates.valueAt(i).dump(prefix2, pw);
         }
 
-        pw.print(prefix); pw.print("mContexts: " );
+        pw.print(prefix);
+        pw.print("mContexts: ");
         if (mContexts != null) {
             int numContexts = mContexts.size();
             for (int i = 0; i < numContexts; i++) {
                 FillContext context = mContexts.get(i);
 
-                pw.print(prefix2); pw.print(context);
+                pw.print(prefix2);
+                pw.print(context);
                 if (sVerbose) {
                     pw.println("AssistStructure dumped at logcat)");
 
@@ -6500,43 +7202,62 @@
             pw.println("null");
         }
 
-        pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
+        pw.print(prefix);
+        pw.print("mHasCallback: ");
+        pw.println(mHasCallback);
         if (mClientState != null) {
-            pw.print(prefix); pw.print("mClientState: "); pw.print(mClientState.getSize()); pw
-                .println(" bytes");
+            pw.print(prefix);
+            pw.print("mClientState: ");
+            pw.print(mClientState.getSize());
+            pw.println(" bytes");
         }
-        pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
-        pw.print(prefix); pw.print("mUrlBar: ");
+        pw.print(prefix);
+        pw.print("mCompatMode: ");
+        pw.println(mCompatMode);
+        pw.print(prefix);
+        pw.print("mUrlBar: ");
         if (mUrlBar == null) {
             pw.println("N/A");
         } else {
-            pw.print("id="); pw.print(mUrlBar.getAutofillId());
-            pw.print(" domain="); pw.print(mUrlBar.getWebDomain());
-            pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText());
+            pw.print("id=");
+            pw.print(mUrlBar.getAutofillId());
+            pw.print(" domain=");
+            pw.print(mUrlBar.getWebDomain());
+            pw.print(" text=");
+            Helper.printlnRedactedText(pw, mUrlBar.getText());
         }
-        pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
-                mSaveOnAllViewsInvisible);
-        pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
+        pw.print(prefix);
+        pw.print("mSaveOnAllViewsInvisible: ");
+        pw.println(mSaveOnAllViewsInvisible);
+        pw.print(prefix);
+        pw.print("mSelectedDatasetIds: ");
+        pw.println(mSelectedDatasetIds);
         if (mSessionFlags.mAugmentedAutofillOnly) {
-            pw.print(prefix); pw.println("For Augmented Autofill Only");
+            pw.print(prefix);
+            pw.println("For Augmented Autofill Only");
         }
         if (mSessionFlags.mFillDialogDisabled) {
-            pw.print(prefix); pw.println("Fill Dialog disabled");
+            pw.print(prefix);
+            pw.println("Fill Dialog disabled");
         }
         if (mLastFillDialogTriggerIds != null) {
-            pw.print(prefix); pw.println("Last Fill Dialog trigger ids: ");
+            pw.print(prefix);
+            pw.println("Last Fill Dialog trigger ids: ");
             pw.println(mSelectedDatasetIds);
         }
         if (mAugmentedAutofillDestroyer != null) {
-            pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
+            pw.print(prefix);
+            pw.println("has mAugmentedAutofillDestroyer");
         }
         if (mAugmentedRequestsLogs != null) {
-            pw.print(prefix); pw.print("number augmented requests: ");
+            pw.print(prefix);
+            pw.print("number augmented requests: ");
             pw.println(mAugmentedRequestsLogs.size());
         }
 
         if (mAugmentedAutofillableIds != null) {
-            pw.print(prefix); pw.print("mAugmentedAutofillableIds: ");
+            pw.print(prefix);
+            pw.print("mAugmentedAutofillableIds: ");
             pw.println(mAugmentedAutofillableIds);
         }
         if (mRemoteFillService != null) {
@@ -6545,21 +7266,32 @@
     }
 
     private static void dumpRequestLog(@NonNull PrintWriter pw, @NonNull LogMaker log) {
-        pw.print("CAT="); pw.print(log.getCategory());
+        pw.print("CAT=");
+        pw.print(log.getCategory());
         pw.print(", TYPE=");
         final int type = log.getType();
         switch (type) {
-            case MetricsEvent.TYPE_SUCCESS: pw.print("SUCCESS"); break;
-            case MetricsEvent.TYPE_FAILURE: pw.print("FAILURE"); break;
-            case MetricsEvent.TYPE_CLOSE: pw.print("CLOSE"); break;
-            default: pw.print("UNSUPPORTED");
+            case MetricsEvent.TYPE_SUCCESS:
+                pw.print("SUCCESS");
+                break;
+            case MetricsEvent.TYPE_FAILURE:
+                pw.print("FAILURE");
+                break;
+            case MetricsEvent.TYPE_CLOSE:
+                pw.print("CLOSE");
+                break;
+            default:
+                pw.print("UNSUPPORTED");
         }
-        pw.print('('); pw.print(type); pw.print(')');
-        pw.print(", PKG="); pw.print(log.getPackageName());
-        pw.print(", SERVICE="); pw.print(log
-                .getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE));
-        pw.print(", ORDINAL="); pw.print(log
-                .getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL));
+        pw.print('(');
+        pw.print(type);
+        pw.print(')');
+        pw.print(", PKG=");
+        pw.print(log.getPackageName());
+        pw.print(", SERVICE=");
+        pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE));
+        pw.print(", ORDINAL=");
+        pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL));
         dumpNumericValue(pw, log, "FLAGS", MetricsEvent.FIELD_AUTOFILL_FLAGS);
         dumpNumericValue(pw, log, "NUM_DATASETS", MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS);
         dumpNumericValue(pw, log, "UI_LATENCY", MetricsEvent.FIELD_AUTOFILL_DURATION);
@@ -6569,64 +7301,86 @@
             pw.print(", AUTH_STATUS=");
             switch (authStatus) {
                 case MetricsEvent.AUTOFILL_AUTHENTICATED:
-                    pw.print("AUTHENTICATED"); break;
+                    pw.print("AUTHENTICATED");
+                    break;
                 case MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED:
-                    pw.print("DATASET_AUTHENTICATED"); break;
+                    pw.print("DATASET_AUTHENTICATED");
+                    break;
                 case MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION:
-                    pw.print("INVALID_AUTHENTICATION"); break;
+                    pw.print("INVALID_AUTHENTICATION");
+                    break;
                 case MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION:
-                    pw.print("INVALID_DATASET_AUTHENTICATION"); break;
-                default: pw.print("UNSUPPORTED");
+                    pw.print("INVALID_DATASET_AUTHENTICATION");
+                    break;
+                default:
+                    pw.print("UNSUPPORTED");
             }
-            pw.print('('); pw.print(authStatus); pw.print(')');
+            pw.print('(');
+            pw.print(authStatus);
+            pw.print(')');
         }
-        dumpNumericValue(pw, log, "FC_IDS",
-                MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS);
-        dumpNumericValue(pw, log, "COMPAT_MODE",
-                MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE);
+        dumpNumericValue(
+                pw, log, "FC_IDS", MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS);
+        dumpNumericValue(pw, log, "COMPAT_MODE", MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE);
     }
 
-    private static void dumpNumericValue(@NonNull PrintWriter pw, @NonNull LogMaker log,
-            @NonNull String field, int tag) {
+    private static void dumpNumericValue(
+            @NonNull PrintWriter pw, @NonNull LogMaker log, @NonNull String field, int tag) {
         final int value = getNumericValue(log, tag);
         if (value != 0) {
-            pw.print(", "); pw.print(field); pw.print('='); pw.print(value);
+            pw.print(", ");
+            pw.print(field);
+            pw.print('=');
+            pw.print(value);
         }
     }
 
-    void sendCredentialManagerResponseToApp(@Nullable GetCredentialResponse response,
-            @Nullable GetCredentialException exception, @NonNull AutofillId viewId) {
+    void sendCredentialManagerResponseToApp(
+            @Nullable GetCredentialResponse response,
+            @Nullable GetCredentialException exception,
+            @NonNull AutofillId viewId) {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#sendCredentialManagerResponseToApp() rejected "
-                        + "- session: " + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#sendCredentialManagerResponseToApp() rejected "
+                                + "- session: "
+                                + id
+                                + " destroyed");
                 return;
             }
             try {
                 final ViewState viewState = mViewStates.get(viewId);
                 if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
-                        && viewState != null && viewState.id.getSessionId() != id) {
+                        && viewState != null
+                        && viewState.id.getSessionId() != id) {
                     if (sVerbose) {
-                        Slog.v(TAG, "Skipping sending credential response to view: "
-                                + viewId + " as it isn't part of the current session: " + id);
+                        Slog.v(
+                                TAG,
+                                "Skipping sending credential response to view: "
+                                        + viewId
+                                        + " as it isn't part of the current session: "
+                                        + id);
                     }
                 }
                 if (exception != null) {
                     if (viewId.isVirtualInt()) {
-                        sendResponseToViewNode(viewId, /*response=*/ null, exception);
+                        sendResponseToViewNode(viewId, /* response= */ null, exception);
                     } else {
-                        mClient.onGetCredentialException(id, viewId, exception.getType(),
-                                exception.getMessage());
+                        mClient.onGetCredentialException(
+                                id, viewId, exception.getType(), exception.getMessage());
                     }
                 } else if (response != null) {
                     if (viewId.isVirtualInt()) {
-                        sendResponseToViewNode(viewId, response, /*exception=*/ null);
+                        sendResponseToViewNode(viewId, response, /* exception= */ null);
                     } else {
                         mClient.onGetCredentialResponse(id, viewId, response);
                     }
                 } else {
-                    Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response"
-                            + "and exception");
+                    Slog.w(
+                            TAG,
+                            "sendCredentialManagerResponseToApp called with null response"
+                                    + "and exception");
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Error sending credential response to activity: " + e);
@@ -6635,23 +7389,20 @@
     }
 
     @GuardedBy("mLock")
-    private void sendResponseToViewNode(AutofillId viewId, GetCredentialResponse response,
-            GetCredentialException exception) {
+    private void sendResponseToViewNode(
+            AutofillId viewId, GetCredentialResponse response, GetCredentialException exception) {
         ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
         if (viewNode != null && viewNode.getPendingCredentialCallback() != null) {
             Bundle resultData = new Bundle();
             if (response != null) {
                 resultData.putParcelable(
-                        CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
-                        response);
-                viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR,
-                        resultData);
+                        CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response);
+                viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, resultData);
             } else if (exception != null) {
                 resultData.putStringArray(
                         CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
                         new String[] {exception.getType(), exception.getMessage()});
-                viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR,
-                        resultData);
+                viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, resultData);
             }
         } else {
             Slog.w(TAG, "View node not found after GetCredentialResponse");
@@ -6661,8 +7412,9 @@
     void autoFillApp(Dataset dataset) {
         synchronized (mLock) {
             if (mDestroyed) {
-                Slog.w(TAG, "Call to Session#autoFillApp() rejected - session: "
-                        + id + " destroyed");
+                Slog.w(
+                        TAG,
+                        "Call to Session#autoFillApp() rejected - session: " + id + " destroyed");
                 return;
             }
             try {
@@ -6671,8 +7423,11 @@
                 final List<AutofillId> ids = new ArrayList<>(entryCount);
                 final List<AutofillValue> values = new ArrayList<>(entryCount);
                 boolean waitingDatasetAuth = false;
-                boolean hideHighlight = (entryCount == 1
-                        && dataset.getFieldIds().get(0).equals(mCurrentViewId));
+                boolean hideHighlight =
+                        highlightAutofillSingleField()
+                                ? false
+                                : (entryCount == 1
+                                        && dataset.getFieldIds().get(0).equals(mCurrentViewId));
                 // Count how many views are filtered because they are not in current session
                 int numOfViewsFiltered = 0;
                 for (int i = 0; i < entryCount; i++) {
@@ -6682,10 +7437,15 @@
                     final AutofillId viewId = dataset.getFieldIds().get(i);
                     final ViewState viewState = mViewStates.get(viewId);
                     if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
-                            && viewState != null && viewState.id.getSessionId() != id) {
+                            && viewState != null
+                            && viewState.id.getSessionId() != id) {
                         if (sVerbose) {
-                            Slog.v(TAG, "Skipping filling view: " +
-                                    viewId + " as it isn't part of the current session: " + id);
+                            Slog.v(
+                                    TAG,
+                                    "Skipping filling view: "
+                                            + viewId
+                                            + " as it isn't part of the current session: "
+                                            + id);
                         }
                         numOfViewsFiltered += 1;
                         continue;
@@ -6721,8 +7481,12 @@
                     }
                     // does not matter the value of isPrimary because null response won't be
                     // overridden.
-                    setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED,
-                            /* clearResponse= */ false, /* isPrimary= */ true);
+                    setViewStatesLocked(
+                            null,
+                            dataset,
+                            ViewState.STATE_AUTOFILLED,
+                            /* clearResponse= */ false,
+                            /* isPrimary= */ true);
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Error autofilling activity: " + e);
@@ -6750,7 +7514,7 @@
         mSessionCommittedEventLogger.maybeSetCommitReason(val);
         mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
         mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
-            SystemClock.elapsedRealtime() - mStartTime);
+                SystemClock.elapsedRealtime() - mStartTime);
         mFillRequestEventLogger.logAndEndEvent();
         mFillResponseEventLogger.logAndEndEvent();
         mPresentationStatsEventLogger.logAndEndEvent("log all events");
@@ -6808,8 +7572,8 @@
             }
         }
 
-        final int totalAugmentedRequests = mAugmentedRequestsLogs == null ? 0
-                : mAugmentedRequestsLogs.size();
+        final int totalAugmentedRequests =
+                mAugmentedRequestsLogs == null ? 0 : mAugmentedRequestsLogs.size();
         if (totalAugmentedRequests > 0) {
             if (sVerbose) {
                 Slog.v(TAG, "destroyLocked(): logging " + totalRequests + " augmented requests");
@@ -6820,11 +7584,12 @@
             }
         }
 
-        final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED)
-                .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests);
+        final LogMaker log =
+                newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED)
+                        .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests);
         if (totalAugmentedRequests > 0) {
-            log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS,
-                    totalAugmentedRequests);
+            log.addTaggedData(
+                    MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, totalAugmentedRequests);
         }
         if (mSessionFlags.mAugmentedAutofillOnly) {
             log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1);
@@ -6846,8 +7611,12 @@
     @GuardedBy("mLock")
     void forceRemoveFromServiceIfForAugmentedOnlyLocked() {
         if (sVerbose) {
-            Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): "
-                    + mSessionFlags.mAugmentedAutofillOnly);
+            Slog.v(
+                    TAG,
+                    "forceRemoveFromServiceIfForAugmentedOnlyLocked("
+                            + this.id
+                            + "): "
+                            + mSessionFlags.mAugmentedAutofillOnly);
         }
         if (!mSessionFlags.mAugmentedAutofillOnly) return;
 
@@ -6880,9 +7649,7 @@
         }
     }
 
-    /**
-     * Thread-safe version of {@link #removeFromServiceLocked()}.
-     */
+    /** Thread-safe version of {@link #removeFromServiceLocked()}. */
     private void removeFromService() {
         synchronized (mLock) {
             removeFromServiceLocked();
@@ -6897,8 +7664,11 @@
     void removeFromServiceLocked() {
         if (sVerbose) Slog.v(TAG, "removeFromServiceLocked(" + this.id + "): " + mPendingSaveUi);
         if (mDestroyed) {
-            Slog.w(TAG, "Call to Session#removeFromServiceLocked() rejected - session: "
-                    + id + " destroyed");
+            Slog.w(
+                    TAG,
+                    "Call to Session#removeFromServiceLocked() rejected - session: "
+                            + id
+                            + " destroyed");
             return;
         }
         if (isSaveUiPendingLocked()) {
@@ -6922,18 +7692,16 @@
     }
 
     /**
-     * Checks whether this session is hiding the Save UI to handle a custom description link for
-     * a specific {@code token} created by
-     * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
+     * Checks whether this session is hiding the Save UI to handle a custom description link for a
+     * specific {@code token} created by {@link PendingUi#PendingUi(IBinder, int,
+     * IAutoFillManagerClient)}.
      */
     @GuardedBy("mLock")
     boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) {
         return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken());
     }
 
-    /**
-     * Checks whether this session is hiding the Save UI to handle a custom description link.
-     */
+    /** Checks whether this session is hiding the Save UI to handle a custom description link. */
     @GuardedBy("mLock")
     private boolean isSaveUiPendingLocked() {
         return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
@@ -6942,8 +7710,8 @@
     // Return latest response index in mResponses SparseArray.
     @GuardedBy("mLock")
     private int getLastResponseIndexLocked() {
-        if (mResponses == null  || mResponses.size() == 0) {
-          return -1;
+        if (mResponses == null || mResponses.size() == 0) {
+            return -1;
         }
         List<Integer> requestIdList = new ArrayList<>();
         final int responseCount = mResponses.size();
@@ -6967,15 +7735,16 @@
 
     @GuardedBy("mLock")
     private void logAuthenticationStatusLocked(int requestId, int status) {
-        addTaggedDataToRequestLogLocked(requestId,
-                MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status);
+        addTaggedDataToRequestLogLocked(
+                requestId, MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status);
     }
 
     @GuardedBy("mLock")
     private void addTaggedDataToRequestLogLocked(int requestId, int tag, @Nullable Object value) {
         final LogMaker requestLog = mRequestLogs.get(requestId);
         if (requestLog == null) {
-            Slog.w(TAG,
+            Slog.w(
+                    TAG,
                     "addTaggedDataToRequestLogLocked(tag=" + tag + "): no log for id " + requestId);
             return;
         }
@@ -6983,20 +7752,33 @@
     }
 
     @GuardedBy("mLock")
-    private void logAugmentedAutofillRequestLocked(int mode,
-            ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted,
+    private void logAugmentedAutofillRequestLocked(
+            int mode,
+            ComponentName augmentedRemoteServiceName,
+            AutofillId focusedId,
+            boolean isWhitelisted,
             Boolean isInline) {
         final String historyItem =
-                "aug:id=" + id + " u=" + uid + " m=" + mode
-                        + " a=" + ComponentName.flattenToShortString(mComponentName)
-                        + " f=" + focusedId
-                        + " s=" + augmentedRemoteServiceName
-                        + " w=" + isWhitelisted
-                        + " i=" + isInline;
+                "aug:id="
+                        + id
+                        + " u="
+                        + uid
+                        + " m="
+                        + mode
+                        + " a="
+                        + ComponentName.flattenToShortString(mComponentName)
+                        + " f="
+                        + focusedId
+                        + " s="
+                        + augmentedRemoteServiceName
+                        + " w="
+                        + isWhitelisted
+                        + " i="
+                        + isInline;
         mService.getMaster().logRequestLocked(historyItem);
     }
 
-    private void wtf(@Nullable Exception e, String fmt, Object...args) {
+    private void wtf(@Nullable Exception e, String fmt, Object... args) {
         final String message = String.format(fmt, args);
         synchronized (mLock) {
             mWtfHistory.log(message);
@@ -7051,13 +7833,9 @@
         mClassificationState.updateResponseReceived(response);
     }
 
-    public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {
+    public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {}
 
-    }
-
-    public void onClassificationRequestTimeout(int requestId) {
-
-    }
+    public void onClassificationRequestTimeout(int requestId) {}
 
     @Override
     public void onServiceDied(@NonNull RemoteFieldClassificationService service) {
@@ -7070,12 +7848,12 @@
 
     @Override
     public void logFieldClassificationEvent(
-            long startTime, FieldClassificationResponse response,
+            long startTime,
+            FieldClassificationResponse response,
             @FieldClassificationEventLogger.FieldClassificationStatus int status) {
         final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
         logger.startNewLogForRequest();
-        logger.maybeSetLatencyMillis(
-                SystemClock.elapsedRealtime() - startTime);
+        logger.maybeSetLatencyMillis(SystemClock.elapsedRealtime() - startTime);
         logger.maybeSetAppPackageUid(uid);
         logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1);
         logger.maybeSetRequestId(sIdCounterForPcc.get());
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 42f69e9..95281c8 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -676,7 +676,7 @@
 
             final MacAddress macAddressObj = MacAddress.fromString(macAddress);
             mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddressObj,
-                    null, null, null, false, null, null);
+                    null, null, null, false, null, null, null);
         }
 
         private void checkCanCallNotificationApi(String callingPackage, int userId) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 4fc9d55..2804945 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -106,8 +106,8 @@
                     boolean selfManaged = getNextBooleanArg();
                     final MacAddress macAddress = MacAddress.fromString(address);
                     mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress,
-                            deviceProfile, deviceProfile, /* associatedDevice */ null, selfManaged,
-                            /* callback */ null, /* resultReceiver */ null);
+                            deviceProfile, deviceProfile, /* associatedDevice */ null, false,
+                            /* callback */ null, /* resultReceiver */ null, /* deviceIcon */ null);
                 }
                 break;
 
diff --git a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index 0c54720..77b1780 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -17,10 +17,12 @@
 package com.android.server.companion.association;
 
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -36,6 +38,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.companion.AssociationInfo;
+import android.graphics.drawable.Icon;
 import android.net.MacAddress;
 import android.os.Environment;
 import android.util.AtomicFile;
@@ -51,6 +54,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -172,6 +176,7 @@
     private static final String XML_ATTR_TIME_APPROVED = "time_approved";
     private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
     private static final String XML_ATTR_SYSTEM_DATA_SYNC_FLAGS = "system_data_sync_flags";
+    private static final String XML_ATTR_DEVICE_ICON = "device_icon";
 
     private static final String LEGACY_XML_ATTR_DEVICE = "device";
 
@@ -393,7 +398,7 @@
         return new AssociationInfo(associationId, userId, appPackage, tag,
                 MacAddress.fromString(deviceAddress), null, profile, null,
                 /* managedByCompanionApp */ false, notify, /* revoked */ false, /* pending */ false,
-                timeApproved, Long.MAX_VALUE, /* systemDataSyncFlags */ 0);
+                timeApproved, Long.MAX_VALUE, /* systemDataSyncFlags */ 0, null);
     }
 
     private static Associations readAssociationsV1(@NonNull TypedXmlPullParser parser,
@@ -444,10 +449,12 @@
                 parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE);
         final int systemDataSyncFlags = readIntAttribute(parser,
                 XML_ATTR_SYSTEM_DATA_SYNC_FLAGS, 0);
+        final Icon deviceIcon = byteArrayToIcon(
+                readByteArrayAttribute(parser, XML_ATTR_DEVICE_ICON));
 
         return new AssociationInfo(associationId, userId, appPackage, tag, macAddress, displayName,
                 profile, null, selfManaged, notify, revoked, pending, timeApproved,
-                lastTimeConnected, systemDataSyncFlags);
+                lastTimeConnected, systemDataSyncFlags, deviceIcon);
     }
 
     private static void writeAssociations(@NonNull XmlSerializer parent,
@@ -480,6 +487,8 @@
         writeLongAttribute(
                 serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs());
         writeIntAttribute(serializer, XML_ATTR_SYSTEM_DATA_SYNC_FLAGS, a.getSystemDataSyncFlags());
+        writeByteArrayAttribute(
+                serializer, XML_ATTR_DEVICE_ICON, iconToByteArray(a.getDeviceIcon()));
 
         serializer.endTag(null, XML_TAG_ASSOCIATION);
     }
@@ -494,4 +503,24 @@
     private static @Nullable MacAddress stringToMacAddress(@Nullable String address) {
         return address != null ? MacAddress.fromString(address) : null;
     }
+
+    private static byte[] iconToByteArray(Icon deviceIcon)
+            throws IOException {
+        if (deviceIcon == null) {
+            return null;
+        }
+
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        deviceIcon.writeToStream(byteStream);
+        return byteStream.toByteArray();
+    }
+
+    private static Icon byteArrayToIcon(byte[] bytes) throws IOException {
+        if (bytes == null) {
+            return null;
+        }
+
+        ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
+        return Icon.createFromStream(byteStream);
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index d56f17b..aebd11a 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -48,6 +48,7 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManagerInternal;
+import android.graphics.drawable.Icon;
 import android.net.MacAddress;
 import android.os.Binder;
 import android.os.Bundle;
@@ -281,7 +282,7 @@
             createAssociation(userId, packageName, macAddress, request.getDisplayName(),
                     request.getDeviceProfile(), request.getAssociatedDevice(),
                     request.isSelfManaged(),
-                    callback, resultReceiver);
+                    callback, resultReceiver, request.getDeviceIcon());
         });
     }
 
@@ -292,15 +293,15 @@
             @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
             @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
             boolean selfManaged, @Nullable IAssociationRequestCallback callback,
-            @Nullable ResultReceiver resultReceiver) {
+            @Nullable ResultReceiver resultReceiver, @Nullable Icon deviceIcon) {
         final int id = mAssociationStore.getNextId();
         final long timestamp = System.currentTimeMillis();
 
         final AssociationInfo association = new AssociationInfo(id, userId, packageName,
                 /* tag */ null, macAddress, displayName, deviceProfile, associatedDevice,
                 selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false,
-                /* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0);
-
+                /* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0,
+                deviceIcon);
         // Add role holder for association (if specified) and add new association to store.
         maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver);
     }
diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
index 6f0baef..150e8da 100644
--- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
@@ -16,7 +16,7 @@
 
 package com.android.server.companion.association;
 
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
 
 import static com.android.internal.util.CollectionUtils.any;
@@ -107,7 +107,7 @@
                     it -> deviceProfile.equals(it.getDeviceProfile()) && id != it.getId());
 
         final int packageProcessImportance = getPackageProcessImportance(userId, packageName);
-        if (packageProcessImportance <= IMPORTANCE_VISIBLE && deviceProfile != null
+        if (packageProcessImportance <= IMPORTANCE_FOREGROUND && deviceProfile != null
                 && !isRoleInUseByOtherAssociations) {
             // Need to remove the app from the list of role holders, but the process is visible
             // to the user at the moment, so we'll need to do it later.
@@ -238,12 +238,16 @@
      */
     private class OnPackageVisibilityChangeListener implements
             ActivityManager.OnUidImportanceListener {
-
+        // This method is called when the importance of a uid (app) changes.
+        // We only care about changes where the app is moving to the background.
+        // (e.g., the app currently is not at the top of the screen that the user
+        // is interacting with.)
         @Override
         public void onUidImportance(int uid, int importance) {
-            if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
-                // The lower the importance value the more "important" the process is.
-                // We are only interested when the process ceases to be visible.
+            // Higher importance values indicate the app is less important.
+            // We are only interested when the process importance level
+            // is greater than IMPORTANCE_FOREGROUND.
+            if (importance <= IMPORTANCE_FOREGROUND) {
                 return;
             }
 
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 91ba9b3..74908a4 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -39,7 +39,9 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 
@@ -59,9 +61,12 @@
     @GuardedBy("mTransportsListeners")
     private final RemoteCallbackList<IOnTransportsChangedListener> mTransportsListeners =
             new RemoteCallbackList<>();
+
     /** Message type -> IOnMessageReceivedListener */
+    @GuardedBy("mMessageListeners")
     @NonNull
-    private final SparseArray<IOnMessageReceivedListener> mMessageListeners = new SparseArray<>();
+    private final SparseArray<Set<IOnMessageReceivedListener>> mMessageListeners =
+            new SparseArray<>();
 
     public CompanionTransportManager(Context context, AssociationStore associationStore) {
         mContext = context;
@@ -72,7 +77,12 @@
      * Add a listener to receive callbacks when a message is received for the message type
      */
     public void addListener(int message, @NonNull IOnMessageReceivedListener listener) {
-        mMessageListeners.put(message, listener);
+        synchronized (mMessageListeners) {
+            if (!mMessageListeners.contains(message)) {
+                mMessageListeners.put(message, new HashSet<IOnMessageReceivedListener>());
+            }
+            mMessageListeners.get(message).add(listener);
+        }
         synchronized (mTransports) {
             for (int i = 0; i < mTransports.size(); i++) {
                 mTransports.valueAt(i).addListener(message, listener);
@@ -113,7 +123,12 @@
      * Remove the listener to stop receiving calbacks when a message is received for the given type
      */
     public void removeListener(int messageType, IOnMessageReceivedListener listener) {
-        mMessageListeners.remove(messageType);
+        synchronized (mMessageListeners) {
+            if (!mMessageListeners.contains(messageType)) {
+                return;
+            }
+            mMessageListeners.get(messageType).remove(listener);
+        }
     }
 
     /**
@@ -315,8 +330,12 @@
     }
 
     private void addMessageListenersToTransport(Transport transport) {
-        for (int i = 0; i < mMessageListeners.size(); i++) {
-            transport.addListener(mMessageListeners.keyAt(i), mMessageListeners.valueAt(i));
+        synchronized (mMessageListeners) {
+            for (int i = 0; i < mMessageListeners.size(); i++) {
+                for (IOnMessageReceivedListener listener : mMessageListeners.valueAt(i)) {
+                    transport.addListener(mMessageListeners.keyAt(i), listener);
+                }
+            }
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java
index 8a5774e..986bd6c 100644
--- a/services/companion/java/com/android/server/companion/transport/Transport.java
+++ b/services/companion/java/com/android/server/companion/transport/Transport.java
@@ -40,8 +40,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -71,7 +71,8 @@
      * the future to allow multiple listeners to receive callbacks for the same message type, the
      * value of the map can be a list.
      */
-    private final Map<Integer, IOnMessageReceivedListener> mListeners;
+    @GuardedBy("mListeners")
+    private final SparseArray<Set<IOnMessageReceivedListener>> mListeners = new SparseArray<>();
 
     private OnTransportClosedListener mOnTransportClosed;
 
@@ -98,7 +99,6 @@
         mRemoteIn = new ParcelFileDescriptor.AutoCloseInputStream(fd);
         mRemoteOut = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
         mContext = context;
-        mListeners = new HashMap<>();
     }
 
     /**
@@ -107,7 +107,12 @@
      * @param listener Execute when a message with the type is received
      */
     public void addListener(int message, IOnMessageReceivedListener listener) {
-        mListeners.put(message, listener);
+        synchronized (mListeners) {
+            if (!mListeners.contains(message)) {
+                mListeners.put(message, new HashSet<IOnMessageReceivedListener>());
+            }
+            mListeners.get(message).add(listener);
+        }
     }
 
     public int getAssociationId() {
@@ -281,12 +286,19 @@
     }
 
     private void callback(int message, byte[] data) {
-        if (mListeners.containsKey(message)) {
+        Set<IOnMessageReceivedListener> listenersToCall;
+        synchronized (mListeners) {
+            if (!mListeners.contains(message)) {
+                return;
+            }
+            listenersToCall = mListeners.get(message);
+        }
+        Slog.d(TAG, "Message 0x" + Integer.toHexString(message)
+                + " is received from associationId " + mAssociationId
+                + ", sending data length " + data.length + " to the listener(s).");
+        for (IOnMessageReceivedListener listener: listenersToCall) {
             try {
-                mListeners.get(message).onMessageReceived(getAssociationId(), data);
-                Slog.d(TAG, "Message 0x" + Integer.toHexString(message)
-                        + " is received from associationId " + mAssociationId
-                        + ", sending data length " + data.length + " to the listener.");
+                listener.onMessageReceived(getAssociationId(), data);
             } catch (RemoteException ignored) {
             }
         }
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index c927cd0..f37e0c9 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -86,7 +86,7 @@
             @NonNull AssociationRequest request, int packageUid) {
         enforcePermissionForRequestingProfile(context, request.getDeviceProfile(), packageUid);
 
-        if (request.isSelfManaged()) {
+        if (request.isSelfManaged() || request.getDeviceIcon() != null) {
             enforcePermissionForRequestingSelfManaged(context, packageUid);
         }
     }
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 3bcca1c..281a2ce 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -16,10 +16,13 @@
 
 package com.android.server.companion.virtual;
 
+import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
+import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED;
 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY;
 import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
 import static android.companion.virtual.VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED;
+import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
 import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
@@ -30,7 +33,6 @@
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
 import static android.companion.virtualdevice.flags.Flags.virtualCameraServiceDiscovery;
 
-import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -95,7 +97,6 @@
 import android.os.IBinder;
 import android.os.LocaleList;
 import android.os.Looper;
-import android.os.PermissionEnforcer;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -402,7 +403,6 @@
             VirtualDeviceParams params,
             DisplayManagerGlobal displayManager,
             VirtualCameraController virtualCameraController) {
-        super(PermissionEnforcer.fromContext(context));
         mVirtualDeviceLog = virtualDeviceLog;
         mOwnerPackageName = attributionSource.getPackageName();
         mAttributionSource = attributionSource;
@@ -425,6 +425,27 @@
         mDisplayManager = displayManager;
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mPowerManager = context.getSystemService(PowerManager.class);
+
+        if (mDevicePolicies.get(POLICY_TYPE_CLIPBOARD, DEVICE_POLICY_DEFAULT)
+                != DEVICE_POLICY_DEFAULT) {
+            if (mContext.checkCallingOrSelfPermission(ADD_TRUSTED_DISPLAY)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+                        + "set a custom clipboard policy.");
+            }
+        }
+
+        int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
+        if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
+            if (mContext.checkCallingOrSelfPermission(ADD_ALWAYS_UNLOCKED_DISPLAY)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires ADD_ALWAYS_UNLOCKED_DISPLAY permission to "
+                        + "create an always unlocked virtual device.");
+            }
+            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
+        }
+        mBaseVirtualDisplayFlags = flags;
+
         if (inputController == null) {
             mInputController = new InputController(
                     context.getMainThreadHandler(),
@@ -467,12 +488,6 @@
                             : mParams.getAllowedActivities();
         }
 
-        int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
-        if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
-            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
-        }
-        mBaseVirtualDisplayFlags = flags;
-
         if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) {
             final String imeId = mParams.getInputMethodComponent().flattenToShortString();
             Slog.d(TAG, "Setting custom input method " + imeId + " as default for virtual device "
@@ -547,10 +562,8 @@
      * object is created before the returned VirtualDeviceInternal one.
      */
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void setListeners(@NonNull IVirtualDeviceActivityListener activityListener,
             @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) {
-        super.setListeners_enforcePermission();
         mActivityListener = Objects.requireNonNull(activityListener);
         mSoundEffectListener = Objects.requireNonNull(soundEffectListener);
     }
@@ -597,9 +610,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void goToSleep() {
-        super.goToSleep_enforcePermission();
+        checkCallerIsDeviceOwner();
         synchronized (mVirtualDeviceLock) {
             mRequestedToBeAwake = false;
         }
@@ -612,9 +624,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void wakeUp() {
-        super.wakeUp_enforcePermission();
+        checkCallerIsDeviceOwner();
         synchronized (mVirtualDeviceLock) {
             mRequestedToBeAwake = true;
             if (mLockdownActive) {
@@ -632,16 +643,12 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void launchPendingIntent(int displayId, PendingIntent pendingIntent,
             ResultReceiver resultReceiver) {
-        super.launchPendingIntent_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(pendingIntent);
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplays.contains(displayId)) {
-                throw new SecurityException("Display ID " + displayId
-                        + " not found for this virtual device");
-            }
+            checkDisplayOwnedByVirtualDeviceLocked(displayId);
         }
         if (pendingIntent.isActivity()) {
             try {
@@ -673,9 +680,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void addActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
-        super.addActivityPolicyExemption_enforcePermission();
+        checkCallerIsDeviceOwner();
         final int displayId = exemption.getDisplayId();
         if (exemption.getComponentName() == null || displayId != Display.INVALID_DISPLAY) {
             if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
@@ -711,9 +717,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void removeActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
-        super.removeActivityPolicyExemption_enforcePermission();
+        checkCallerIsDeviceOwner();
         final int displayId = exemption.getDisplayId();
         if (exemption.getComponentName() == null || displayId != Display.INVALID_DISPLAY) {
             if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
@@ -764,9 +769,7 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void close() {
-        super.close_enforcePermission();
         // Remove about-to-be-closed virtual device from the service before butchering it.
         if (!mService.removeVirtualDevice(mDeviceId)) {
             // Device is already closed.
@@ -841,11 +844,10 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void onAudioSessionStarting(int displayId,
             @NonNull IAudioRoutingCallback routingCallback,
             @Nullable IAudioConfigChangedCallback configChangedCallback) {
-        super.onAudioSessionStarting_enforcePermission();
+        checkCallerIsDeviceOwner();
         synchronized (mVirtualDeviceLock) {
             checkDisplayOwnedByVirtualDeviceLocked(displayId);
             if (mVirtualAudioController == null) {
@@ -859,9 +861,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void onAudioSessionEnded() {
-        super.onAudioSessionEnded_enforcePermission();
+        checkCallerIsDeviceOwner();
         synchronized (mVirtualDeviceLock) {
             if (mVirtualAudioController != null) {
                 mVirtualAudioController.stopListening();
@@ -871,10 +872,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void setDevicePolicy(@VirtualDeviceParams.DynamicPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
-        super.setDevicePolicy_enforcePermission();
+        checkCallerIsDeviceOwner();
         if (!Flags.dynamicPolicy()) {
             return;
         }
@@ -884,8 +884,12 @@
                 synchronized (mVirtualDeviceLock) {
                     mDevicePolicies.put(policyType, devicePolicy);
                     for (int i = 0; i < mVirtualDisplays.size(); i++) {
-                        mVirtualDisplays.valueAt(i).getWindowPolicyController()
-                                .setShowInHostDeviceRecents(devicePolicy == DEVICE_POLICY_DEFAULT);
+                        VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+                        if (wrapper.isTrusted()) {
+                            wrapper.getWindowPolicyController()
+                                    .setShowInHostDeviceRecents(
+                                            devicePolicy == DEVICE_POLICY_DEFAULT);
+                        }
                     }
                 }
                 break;
@@ -905,7 +909,20 @@
                 break;
             case POLICY_TYPE_CLIPBOARD:
                 if (Flags.crossDeviceClipboard()) {
+                    if (devicePolicy == DEVICE_POLICY_CUSTOM
+                            && mContext.checkCallingOrSelfPermission(ADD_TRUSTED_DISPLAY)
+                            != PackageManager.PERMISSION_GRANTED) {
+                        throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+                                + "set a custom clipboard policy.");
+                    }
                     synchronized (mVirtualDeviceLock) {
+                        for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                            VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+                            if (!wrapper.isTrusted() && !wrapper.isMirror()) {
+                                throw new SecurityException("All displays must be trusted for "
+                                        + "devices with custom clipboard policy.");
+                            }
+                        }
                         mDevicePolicies.put(policyType, devicePolicy);
                     }
                 }
@@ -924,11 +941,10 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void setDevicePolicyForDisplay(int displayId,
             @VirtualDeviceParams.DynamicDisplayPolicyType int policyType,
             @VirtualDeviceParams.DevicePolicy int devicePolicy) {
-        super.setDevicePolicyForDisplay_enforcePermission();
+        checkCallerIsDeviceOwner();
         if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
             return;
         }
@@ -936,8 +952,11 @@
             checkDisplayOwnedByVirtualDeviceLocked(displayId);
             switch (policyType) {
                 case POLICY_TYPE_RECENTS:
-                    mVirtualDisplays.get(displayId).getWindowPolicyController()
-                            .setShowInHostDeviceRecents(devicePolicy == DEVICE_POLICY_DEFAULT);
+                    VirtualDisplayWrapper wrapper = mVirtualDisplays.get(displayId);
+                    if (wrapper.isTrusted()) {
+                        wrapper.getWindowPolicyController()
+                                .setShowInHostDeviceRecents(devicePolicy == DEVICE_POLICY_DEFAULT);
+                    }
                     break;
                 case POLICY_TYPE_ACTIVITY:
                     mVirtualDisplays.get(displayId).getWindowPolicyController()
@@ -951,9 +970,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualDpad(VirtualDpadConfig config, @NonNull IBinder deviceToken) {
-        super.createVirtualDpad_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(config);
         checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
         final long ident = Binder.clearCallingIdentity();
@@ -969,9 +987,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualKeyboard(VirtualKeyboardConfig config, @NonNull IBinder deviceToken) {
-        super.createVirtualKeyboard_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(config);
         checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
         final long ident = Binder.clearCallingIdentity();
@@ -991,9 +1008,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualMouse(VirtualMouseConfig config, @NonNull IBinder deviceToken) {
-        super.createVirtualMouse_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(config);
         checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
         final long ident = Binder.clearCallingIdentity();
@@ -1008,10 +1024,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualTouchscreen(VirtualTouchscreenConfig config,
             @NonNull IBinder deviceToken) {
-        super.createVirtualTouchscreen_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(config);
         checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
         final long ident = Binder.clearCallingIdentity();
@@ -1027,10 +1042,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualNavigationTouchpad(VirtualNavigationTouchpadConfig config,
             @NonNull IBinder deviceToken) {
-        super.createVirtualNavigationTouchpad_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(config);
         checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
         final long ident = Binder.clearCallingIdentity();
@@ -1048,10 +1062,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualStylus(@NonNull VirtualStylusConfig config,
             @NonNull IBinder deviceToken) {
-        super.createVirtualStylus_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(config);
         Objects.requireNonNull(deviceToken);
         checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
@@ -1068,10 +1081,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void createVirtualRotaryEncoder(@NonNull VirtualRotaryEncoderConfig config,
             @NonNull IBinder deviceToken) {
-        super.createVirtualRotaryEncoder_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(config);
         Objects.requireNonNull(deviceToken);
         checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
@@ -1088,9 +1100,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void unregisterInputDevice(IBinder token) {
-        super.unregisterInputDevice_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             mInputController.unregisterInputDevice(token);
@@ -1100,9 +1111,7 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public int getInputDeviceId(IBinder token) {
-        super.getInputDeviceId_enforcePermission();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.getInputDeviceId(token);
@@ -1113,9 +1122,8 @@
 
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendDpadKeyEvent(IBinder token, VirtualKeyEvent event) {
-        super.sendDpadKeyEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.sendDpadKeyEvent(token, event);
@@ -1125,9 +1133,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendKeyEvent(IBinder token, VirtualKeyEvent event) {
-        super.sendKeyEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.sendKeyEvent(token, event);
@@ -1137,9 +1144,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendButtonEvent(IBinder token, VirtualMouseButtonEvent event) {
-        super.sendButtonEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.sendButtonEvent(token, event);
@@ -1149,9 +1155,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendTouchEvent(IBinder token, VirtualTouchEvent event) {
-        super.sendTouchEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.sendTouchEvent(token, event);
@@ -1161,9 +1166,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendRelativeEvent(IBinder token, VirtualMouseRelativeEvent event) {
-        super.sendRelativeEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.sendRelativeEvent(token, event);
@@ -1173,9 +1177,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendScrollEvent(IBinder token, VirtualMouseScrollEvent event) {
-        super.sendScrollEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.sendScrollEvent(token, event);
@@ -1185,9 +1188,7 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public PointF getCursorPosition(IBinder token) {
-        super.getCursorPosition_enforcePermission();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.getCursorPosition(token);
@@ -1197,10 +1198,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendStylusMotionEvent(@NonNull IBinder token,
             @NonNull VirtualStylusMotionEvent event) {
-        super.sendStylusMotionEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(token);
         Objects.requireNonNull(event);
         final long ident = Binder.clearCallingIdentity();
@@ -1212,10 +1212,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendStylusButtonEvent(@NonNull IBinder token,
             @NonNull VirtualStylusButtonEvent event) {
-        super.sendStylusButtonEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(token);
         Objects.requireNonNull(event);
         final long ident = Binder.clearCallingIdentity();
@@ -1227,10 +1226,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendRotaryEncoderScrollEvent(@NonNull IBinder token,
             @NonNull VirtualRotaryEncoderScrollEvent event) {
-        super.sendRotaryEncoderScrollEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mInputController.sendRotaryEncoderScrollEvent(token, event);
@@ -1240,17 +1238,19 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void setShowPointerIcon(boolean showPointerIcon) {
-        super.setShowPointerIcon_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mVirtualDeviceLock) {
                 mDefaultShowPointerIcon = showPointerIcon;
-            }
-            final int[] displayIds = getDisplayIds();
-            for (int i = 0; i < displayIds.length; ++i) {
-                mInputController.setShowPointerIcon(showPointerIcon, displayIds[i]);
+                for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                    VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
+                    if (wrapper.isTrusted() || wrapper.isMirror()) {
+                        mInputController.setShowPointerIcon(
+                                mDefaultShowPointerIcon, mVirtualDisplays.keyAt(i));
+                    }
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -1258,14 +1258,10 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) {
-        super.setDisplayImePolicy_enforcePermission();
+        checkCallerIsDeviceOwner();
         synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplays.contains(displayId)) {
-                throw new SecurityException("Display ID " + displayId
-                        + " not found for this virtual device");
-            }
+            checkDisplayOwnedByVirtualDeviceLocked(displayId);
         }
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -1276,10 +1272,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @Nullable
     public List<VirtualSensor> getVirtualSensorList() {
-        super.getVirtualSensorList_enforcePermission();
+        checkCallerIsDeviceOwner();
         return mSensorController.getSensorList();
     }
 
@@ -1289,9 +1284,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) {
-        super.sendSensorEvent_enforcePermission();
+        checkCallerIsDeviceOwner();
         final long ident = Binder.clearCallingIdentity();
         try {
             return mSensorController.sendSensorEvent(token, event);
@@ -1301,10 +1295,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
             IntentFilter filter) {
-        super.registerIntentInterceptor_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(intentInterceptor);
         Objects.requireNonNull(filter);
         synchronized (mVirtualDeviceLock) {
@@ -1313,10 +1306,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void unregisterIntentInterceptor(
             @NonNull IVirtualDeviceIntentInterceptor intentInterceptor) {
-        super.unregisterIntentInterceptor_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(intentInterceptor);
         synchronized (mVirtualDeviceLock) {
             mIntentInterceptors.remove(intentInterceptor.asBinder());
@@ -1324,10 +1316,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void registerVirtualCamera(@NonNull VirtualCameraConfig cameraConfig)
             throws RemoteException {
-        super.registerVirtualCamera_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(cameraConfig);
         if (mVirtualCameraController == null) {
             throw new UnsupportedOperationException("Virtual camera controller is not available");
@@ -1336,10 +1327,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public void unregisterVirtualCamera(@NonNull VirtualCameraConfig cameraConfig)
             throws RemoteException {
-        super.unregisterVirtualCamera_enforcePermission();
+        checkCallerIsDeviceOwner();
         Objects.requireNonNull(cameraConfig);
         if (mVirtualCameraController == null) {
             throw new UnsupportedOperationException("Virtual camera controller is not available");
@@ -1348,10 +1338,8 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public String getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig)
             throws RemoteException {
-        super.getVirtualCameraId_enforcePermission();
         Objects.requireNonNull(cameraConfig);
         if (mVirtualCameraController == null) {
             throw new UnsupportedOperationException("Virtual camera controller is not available");
@@ -1474,10 +1462,9 @@
     }
 
     @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     public int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
             @NonNull IVirtualDisplayCallback callback) {
-        super.createVirtualDisplay_enforcePermission();
+        checkCallerIsDeviceOwner();
         GenericWindowPolicyController gwpc;
         synchronized (mVirtualDeviceLock) {
             gwpc = createWindowPolicyControllerLocked(virtualDisplayConfig.getDisplayCategories());
@@ -1491,6 +1478,12 @@
         boolean isTrustedDisplay =
                 (mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED)
                         == Display.FLAG_TRUSTED;
+        if (!isTrustedDisplay) {
+            if (getDevicePolicy(POLICY_TYPE_CLIPBOARD) != DEVICE_POLICY_DEFAULT) {
+                throw new SecurityException("All displays must be trusted for devices with custom"
+                        + "clipboard policy.");
+            }
+        }
 
         boolean showPointer;
         synchronized (mVirtualDeviceLock) {
@@ -1500,7 +1493,8 @@
                         "Virtual device already has a virtual display with ID " + displayId);
             }
 
-            PowerManager.WakeLock wakeLock = createAndAcquireWakeLockForDisplay(displayId);
+            PowerManager.WakeLock wakeLock =
+                    isTrustedDisplay ? createAndAcquireWakeLockForDisplay(displayId) : null;
             mVirtualDisplays.put(displayId, new VirtualDisplayWrapper(callback, gwpc, wakeLock,
                     isTrustedDisplay, isMirrorDisplay));
             showPointer = mDefaultShowPointerIcon;
@@ -1508,14 +1502,15 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            mInputController.setShowPointerIcon(showPointer, displayId);
             mInputController.setMousePointerAccelerationEnabled(false, displayId);
             mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
                     displayId);
-            // WM throws a SecurityException if the display is untrusted.
             if (isTrustedDisplay) {
+                mInputController.setShowPointerIcon(showPointer, displayId);
                 mInputController.setDisplayImePolicy(displayId,
                         WindowManager.DISPLAY_IME_POLICY_LOCAL);
+            } else {
+                gwpc.setShowInHostDeviceRecents(true);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -1616,6 +1611,11 @@
                     != PackageManager.PERMISSION_GRANTED) {
             synchronized (mVirtualDeviceLock) {
                 checkDisplayOwnedByVirtualDeviceLocked(displayId);
+                VirtualDisplayWrapper wrapper = mVirtualDisplays.get(displayId);
+                if (!wrapper.isTrusted() && !wrapper.isMirror()) {
+                    throw new SecurityException(
+                            "Cannot create input device associated with an untrusted display");
+                }
             }
         }
     }
@@ -1629,6 +1629,13 @@
         }
     }
 
+    private void checkCallerIsDeviceOwner() {
+        if (Binder.getCallingUid() != mOwnerUid) {
+            throw new SecurityException(
+                "Caller is not the owner of this virtual device");
+        }
+    }
+
     void goToSleepInternal(@PowerManager.GoToSleepReason int reason) {
         final long now = SystemClock.uptimeMillis();
         synchronized (mVirtualDeviceLock) {
@@ -1665,7 +1672,7 @@
      * @param virtualDisplayWrapper - VirtualDisplayWrapper to release resources for.
      */
     private void releaseOwnedVirtualDisplayResources(VirtualDisplayWrapper virtualDisplayWrapper) {
-        virtualDisplayWrapper.getWakeLock().release();
+        virtualDisplayWrapper.releaseWakeLock();
         virtualDisplayWrapper.getWindowPolicyController().unregisterRunningAppsChangedListener(
                 this);
     }
@@ -1833,10 +1840,10 @@
 
         VirtualDisplayWrapper(@NonNull IVirtualDisplayCallback token,
                 @NonNull GenericWindowPolicyController windowPolicyController,
-                @NonNull PowerManager.WakeLock wakeLock, boolean isTrusted, boolean isMirror) {
+                @Nullable PowerManager.WakeLock wakeLock, boolean isTrusted, boolean isMirror) {
             mToken = Objects.requireNonNull(token);
             mWindowPolicyController = Objects.requireNonNull(windowPolicyController);
-            mWakeLock = Objects.requireNonNull(wakeLock);
+            mWakeLock = wakeLock;
             mIsTrusted = isTrusted;
             mIsMirror = isMirror;
         }
@@ -1845,8 +1852,10 @@
             return mWindowPolicyController;
         }
 
-        PowerManager.WakeLock getWakeLock() {
-            return mWakeLock;
+        void releaseWakeLock() {
+            if (mWakeLock != null && mWakeLock.isHeld()) {
+                mWakeLock.release();
+            }
         }
 
         boolean isTrusted() {
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 41b6a85..f87e3c3 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -21,6 +21,7 @@
 
 import static com.android.server.wm.ActivityInterceptorCallback.VIRTUAL_DEVICE_SERVICE_ORDERED_ID;
 
+import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -122,7 +123,6 @@
     private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener =
             new CompanionDeviceManager.OnAssociationsChangedListener() {
                 @Override
-                @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
                 public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) {
                     syncVirtualDevicesToCdmAssociations(associations);
                 }
@@ -339,7 +339,6 @@
         return true;
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) {
         Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
         synchronized (mVirtualDeviceManagerLock) {
@@ -382,7 +381,6 @@
         cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
     }
 
-    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     void onCdmAssociationsChanged(List<AssociationInfo> associations) {
         ArrayMap<String, AssociationInfo> vdmAssociations = new ArrayMap<>();
         for (int i = 0; i < associations.size(); ++i) {
@@ -452,7 +450,7 @@
                     }
                 };
 
-        @android.annotation.EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+        @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @Override // Binder call
         public IVirtualDevice createVirtualDevice(
                 IBinder token,
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index 57c643b..a7aab49 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -160,11 +160,13 @@
             HardwareBuffer snapshotBuffer = null;
             int colorSpaceId = 0;
 
+            TaskSnapshot snapshot = null;
             // Skip taking TaskSnapshot when bitmap is provided.
             if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
                 // Can block, so call before acquiring the lock.
-                TaskSnapshot snapshot =
-                        mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false);
+                snapshot = mActivityTaskManagerInternal.getTaskSnapshotBlocking(
+                        taskId, false /* isLowResolution */,
+                        TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
                 if (snapshot != null) {
                     snapshotBuffer = snapshot.getHardwareBuffer();
                     ColorSpace colorSpace = snapshot.getColorSpace();
@@ -185,6 +187,9 @@
                     }
                 }
             }
+            if (snapshot != null) {
+                snapshot.removeReference(TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 05d07ae..485bf31 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1523,7 +1523,7 @@
                 @Override
                 public void onApexStaged(ApexStagedEvent event) throws RemoteException {
                     Slog.d(TAG, "A new APEX has been staged for update. There are currently "
-                            + event.stagedApexModuleNames.length + " APEX(s) staged for update. "
+                            + event.stagedApexInfos.length + " APEX(s) staged for update. "
                             + "Scheduling measurement...");
                     UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                             BinaryTransparencyService.this);
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index d80e40c..6a1e319 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -46,6 +46,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.build.UnboundedSdkLevel;
 import com.android.server.pm.permission.PermissionAllowlist;
@@ -212,6 +213,30 @@
         }
     }
 
+    /**
+     * Utility class for testing interaction with compile-time defined system features.
+     * @hide
+    */
+    @VisibleForTesting
+    public static class Injector {
+        /** Whether a system feature is defined as enabled and available at compile-time. */
+        public boolean isReadOnlySystemEnabledFeature(String featureName, int version) {
+            return Boolean.TRUE.equals(RoSystemFeatures.maybeHasFeature(featureName, version));
+        }
+
+        /** Whether a system feature is defined as disabled and unavailable at compile-time. */
+        public boolean isReadOnlySystemDisabledFeature(String featureName, int version) {
+            return Boolean.FALSE.equals(RoSystemFeatures.maybeHasFeature(featureName, version));
+        }
+
+        /** The full set of system features defined as compile-time enabled and available. */
+        public ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+            return RoSystemFeatures.getReadOnlySystemEnabledFeatures();
+        }
+    }
+
+    private final Injector mInjector;
+
     // These are the built-in shared libraries that were read from the
     // system configuration files. Keys are the library names; values are
     // the individual entries that contain information such as filename
@@ -220,7 +245,7 @@
 
     // These are the features this devices supports that were read from the
     // system configuration files.
-    final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
+    final ArrayMap<String, FeatureInfo> mAvailableFeatures;
 
     // These are the features which this device doesn't support; the OEM
     // partition uses these to opt-out of features from the system image.
@@ -602,12 +627,26 @@
     public ArrayMap<String, Integer> getOemDefinedUids() {
         return mOemDefinedUids;
     }
+
     /**
      * Only use for testing. Do NOT use in production code.
      * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
      */
     @VisibleForTesting
     public SystemConfig(boolean readPermissions) {
+        this(readPermissions, new Injector());
+    }
+
+    /**
+     * Only use for testing. Do NOT use in production code.
+     * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
+     * @param injector Additional dependency injection for testing.
+     */
+    @VisibleForTesting
+    public SystemConfig(boolean readPermissions, Injector injector) {
+        mInjector = injector;
+        mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures();
+
         if (readPermissions) {
             Slog.w(TAG, "Constructing a test SystemConfig");
             readAllPermissions();
@@ -617,6 +656,9 @@
     }
 
     SystemConfig() {
+        mInjector = new Injector();
+        mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures();
+
         TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
         log.traceBegin("readAllPermissions");
         try {
@@ -727,10 +769,8 @@
             return;
         }
         // Read configuration of features, libs and priv-app permissions from apex module.
-        int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
-        if (android.permission.flags.Flags.apexSignaturePermissionAllowlistEnabled()) {
-            apexPermissionFlag |= ALLOW_SIGNATURE_PERMISSIONS;
-        }
+        int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
+                | ALLOW_SIGNATURE_PERMISSIONS;
         // TODO: Use a solid way to filter apex module folders?
         for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) {
             if (f.isFile() || f.getPath().contains("@")) {
@@ -1777,6 +1817,10 @@
     }
 
     private void addFeature(String name, int version) {
+        if (mInjector.isReadOnlySystemDisabledFeature(name, version)) {
+            Slog.w(TAG, "Skipping feature addition for compile-time disabled feature: " + name);
+            return;
+        }
         FeatureInfo fi = mAvailableFeatures.get(name);
         if (fi == null) {
             fi = new FeatureInfo();
@@ -1789,6 +1833,10 @@
     }
 
     private void removeFeature(String name) {
+        if (mInjector.isReadOnlySystemEnabledFeature(name, /*version=*/0)) {
+            Slog.w(TAG, "Skipping feature removal for compile-time enabled feature: " + name);
+            return;
+        }
         if (mAvailableFeatures.remove(name) != null) {
             Slog.d(TAG, "Removed unavailable feature " + name);
         }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7442277..39ac515 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -422,9 +422,9 @@
     private int[] mSimultaneousCellularCallingSubIds = {};
 
     private int[] mECBMReason;
-    private boolean[] mECBMStarted;
+    private long[] mECBMDuration;
     private int[] mSCBMReason;
-    private boolean[] mSCBMStarted;
+    private long[] mSCBMDuration;
 
     private boolean[] mCarrierRoamingNtnMode = null;
     private boolean[] mCarrierRoamingNtnEligible = null;
@@ -724,9 +724,9 @@
             mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
             mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones);
             mECBMReason = copyOf(mECBMReason, mNumPhones);
-            mECBMStarted = copyOf(mECBMStarted, mNumPhones);
+            mECBMDuration = copyOf(mECBMDuration, mNumPhones);
             mSCBMReason = copyOf(mSCBMReason, mNumPhones);
-            mSCBMStarted = copyOf(mSCBMStarted, mNumPhones);
+            mSCBMDuration = copyOf(mSCBMDuration, mNumPhones);
             mCarrierRoamingNtnMode = copyOf(mCarrierRoamingNtnMode, mNumPhones);
             mCarrierRoamingNtnEligible = copyOf(mCarrierRoamingNtnEligible, mNumPhones);
             // ds -> ss switch.
@@ -784,9 +784,9 @@
                 mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
                 mCarrierServiceStates.add(i, new Pair<>(null, Process.INVALID_UID));
                 mECBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
-                mECBMStarted[i] = false;
+                mECBMDuration[i] = 0;
                 mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
-                mSCBMStarted[i] = false;
+                mSCBMDuration[i] = 0;
                 mCarrierRoamingNtnMode[i] = false;
                 mCarrierRoamingNtnEligible[i] = false;
             }
@@ -859,9 +859,9 @@
         mCarrierPrivilegeStates = new ArrayList<>();
         mCarrierServiceStates = new ArrayList<>();
         mECBMReason = new int[numPhones];
-        mECBMStarted = new boolean[numPhones];
+        mECBMDuration = new long[numPhones];
         mSCBMReason = new int[numPhones];
-        mSCBMStarted = new boolean[numPhones];
+        mSCBMDuration = new long[numPhones];
         mCarrierRoamingNtnMode = new boolean[numPhones];
         mCarrierRoamingNtnEligible = new boolean[numPhones];
 
@@ -904,9 +904,9 @@
             mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
             mCarrierServiceStates.add(i, new Pair<>(null, Process.INVALID_UID));
             mECBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
-            mECBMStarted[i] = false;
+            mECBMDuration[i] = 0;
             mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN;
-            mSCBMStarted[i] = false;
+            mSCBMDuration[i] = 0;
             mCarrierRoamingNtnMode[i] = false;
             mCarrierRoamingNtnEligible[i] = false;
         }
@@ -1493,24 +1493,24 @@
                 }
                 if (events.contains(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) {
                     try {
-                        boolean ecbmStarted = mECBMStarted[r.phoneId];
-                        if (ecbmStarted) {
-                            r.callback.onCallBackModeStarted(
-                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL);
-                        } else {
-                            r.callback.onCallBackModeStopped(
+                        if (mECBMDuration[r.phoneId] != 0) {
+                            r.callback.onCallbackModeStarted(
                                     TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL,
-                                    mECBMReason[r.phoneId]);
+                                    mECBMDuration[r.phoneId], r.subId);
+                        } else {
+                            r.callback.onCallbackModeStopped(
+                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL,
+                                    mECBMReason[r.phoneId], r.subId);
                         }
 
-                        boolean scbmStarted = mSCBMStarted[r.phoneId];
-                        if (scbmStarted) {
-                            r.callback.onCallBackModeStarted(
-                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS);
-                        } else {
-                            r.callback.onCallBackModeStopped(
+                        if (mSCBMReason[r.phoneId] != 0) {
+                            r.callback.onCallbackModeStarted(
                                     TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS,
-                                    mSCBMReason[r.phoneId]);
+                                    mSCBMDuration[r.phoneId], r.subId);
+                        } else {
+                            r.callback.onCallbackModeStopped(
+                                    TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS,
+                                    mSCBMReason[r.phoneId], r.subId);
                         }
                     } catch (RemoteException ex) {
                         remove(r.binder);
@@ -3457,10 +3457,9 @@
     }
 
     @Override
-    public void notifyCallbackModeStarted(int phoneId, int subId, int type) {
-        if (!checkNotifyPermission("notifyCallbackModeStarted()")) {
-            return;
-        }
+    public void notifyCallbackModeStarted(int phoneId, int subId, int type, long durationMillis) {
+        if (!checkNotifyPermission("notifyCallbackModeStarted()")) return;
+
         if (VDBG) {
             log("notifyCallbackModeStarted: phoneId=" + phoneId + ", subId=" + subId
                     + ", type=" + type);
@@ -3468,9 +3467,9 @@
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) {
-                    mECBMStarted[phoneId] = true;
+                    mECBMDuration[phoneId] = durationMillis;
                 } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) {
-                    mSCBMStarted[phoneId] = true;
+                    mSCBMDuration[phoneId] = durationMillis;
                 }
             }
             for (Record r : mRecords) {
@@ -3478,7 +3477,39 @@
                 if (r.matchTelephonyCallbackEvent(
                         TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) {
                     try {
-                        r.callback.onCallBackModeStarted(type);
+                        r.callback.onCallbackModeStarted(type, durationMillis, subId);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+        }
+        handleRemoveListLocked();
+    }
+
+    @Override
+    public void notifyCallbackModeRestarted(int phoneId, int subId, int type,
+            long durationMillis) {
+        if (!checkNotifyPermission("notifyCallbackModeRestarted()")) return;
+
+        if (VDBG) {
+            log("notifyCallbackModeRestarted: phoneId=" + phoneId + ", subId=" + subId
+                    + ", type=" + type);
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) {
+                    mECBMDuration[phoneId] = durationMillis;
+                } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) {
+                    mSCBMDuration[phoneId] = durationMillis;
+                }
+            }
+            for (Record r : mRecords) {
+                // Send to all listeners regardless of subscription
+                if (r.matchTelephonyCallbackEvent(
+                        TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) {
+                    try {
+                        r.callback.onCallbackModeRestarted(type, durationMillis, subId);
                     } catch (RemoteException ex) {
                         mRemoveList.add(r.binder);
                     }
@@ -3490,9 +3521,8 @@
 
     @Override
     public void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason) {
-        if (!checkNotifyPermission("notifyCallbackModeStopped()")) {
-            return;
-        }
+        if (!checkNotifyPermission("notifyCallbackModeStopped()")) return;
+
         if (VDBG) {
             log("notifyCallbackModeStopped: phoneId=" + phoneId + ", subId=" + subId
                     + ", type=" + type + ", reason=" + reason);
@@ -3500,11 +3530,11 @@
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) {
-                    mECBMStarted[phoneId] = false;
                     mECBMReason[phoneId] = reason;
+                    mECBMDuration[phoneId] = 0;
                 } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) {
-                    mSCBMStarted[phoneId] = false;
                     mSCBMReason[phoneId] = reason;
+                    mSCBMDuration[phoneId] = 0;
                 }
             }
             for (Record r : mRecords) {
@@ -3512,7 +3542,7 @@
                 if (r.matchTelephonyCallbackEvent(
                         TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) {
                     try {
-                        r.callback.onCallBackModeStopped(type, reason);
+                        r.callback.onCallbackModeStopped(type, reason, subId);
                     } catch (RemoteException ex) {
                         mRemoveList.add(r.binder);
                     }
@@ -3662,9 +3692,9 @@
                 pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
                 pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
                 pw.println("mECBMReason=" + mECBMReason[i]);
-                pw.println("mECBMStarted=" + mECBMStarted[i]);
+                pw.println("mECBMDuration=" + mECBMDuration[i]);
                 pw.println("mSCBMReason=" + mSCBMReason[i]);
-                pw.println("mSCBMStarted=" + mSCBMStarted[i]);
+                pw.println("mSCBMDuration=" + mSCBMDuration[i]);
                 pw.println("mCarrierRoamingNtnMode=" + mCarrierRoamingNtnMode[i]);
                 pw.println("mCarrierRoamingNtnEligible=" + mCarrierRoamingNtnEligible[i]);
 
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index f8857d3..d13dd2f 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -443,6 +443,11 @@
         mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression;
     }
 
+    @VisibleForTesting
+    void setCurrentUser(int currentUserId) {
+        mCurrentUser = currentUserId;
+    }
+
     @Override
     public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
         mCurrentUser = to.getUserIdentifier();
@@ -864,9 +869,9 @@
                     throw new IllegalArgumentException("Unknown mode: " + mode);
             }
 
-            final int user = UserHandle.getCallingUserId();
-            enforceCurrentUserIfVisibleBackgroundEnabled(user);
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
 
+            final int user = UserHandle.getCallingUserId();
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -929,7 +934,7 @@
                 @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
             setAttentionModeThemeOverlay_enforcePermission();
 
-            enforceCurrentUserIfVisibleBackgroundEnabled(UserHandle.getCallingUserId());
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
 
             synchronized (mLock) {
                 if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) {
@@ -1020,16 +1025,16 @@
                 return false;
             }
             final int user = Binder.getCallingUserHandle().getIdentifier();
-            enforceCurrentUserIfVisibleBackgroundEnabled(user);
-
             if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS)
                     != PackageManager.PERMISSION_GRANTED) {
                 Slog.e(TAG, "Target user is not current user,"
                         + " INTERACT_ACROSS_USERS permission is required");
                 return false;
-
             }
+
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
             // Store the last requested bedtime night mode state so that we don't need to notify
             // anyone if the user decides to switch to the night mode to bedtime.
             if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
@@ -1078,9 +1083,10 @@
                 Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission");
                 return;
             }
-            final int user = UserHandle.getCallingUserId();
-            enforceCurrentUserIfVisibleBackgroundEnabled(user);
 
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
+            final int user = UserHandle.getCallingUserId();
             final long ident = Binder.clearCallingIdentity();
             try {
                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1108,9 +1114,10 @@
                 Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission");
                 return;
             }
-            final int user = UserHandle.getCallingUserId();
-            enforceCurrentUserIfVisibleBackgroundEnabled(user);
 
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
+            final int user = UserHandle.getCallingUserId();
             final long ident = Binder.clearCallingIdentity();
             try {
                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1131,7 +1138,7 @@
             assertLegit(callingPackage);
             assertSingleProjectionType(projectionType);
             enforceProjectionTypePermissions(projectionType);
-            enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
 
             synchronized (mLock) {
                 if (mProjectionHolders == null) {
@@ -1177,7 +1184,7 @@
             assertLegit(callingPackage);
             assertSingleProjectionType(projectionType);
             enforceProjectionTypePermissions(projectionType);
-            enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
 
             return releaseProjectionUnchecked(projectionType, callingPackage);
         }
@@ -1219,7 +1226,7 @@
                 return;
             }
 
-            enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+            enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
 
             synchronized (mLock) {
                 if (mProjectionListeners == null) {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 12e8c57..947f6b7 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -48,7 +48,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
-import android.net.vcn.Flags;
 import android.net.vcn.IVcnManagementService;
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -447,22 +446,16 @@
         }
 
         final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+        final UserManager userManager = mContext.getSystemService(UserManager.class);
 
-        if (Flags.enforceMainUser()) {
-            final UserManager userManager = mContext.getSystemService(UserManager.class);
-
-            Binder.withCleanCallingIdentity(
-                    () -> {
-                        if (!Objects.equals(userManager.getMainUser(), userHandle)) {
-                            throw new SecurityException(
-                                    "VcnManagementService can only be used by callers running as"
-                                            + " the main user");
-                        }
-                    });
-        } else if (!userHandle.isSystem()) {
-            throw new SecurityException(
-                    "VcnManagementService can only be used by callers running as the primary user");
-        }
+        Binder.withCleanCallingIdentity(
+                () -> {
+                    if (!Objects.equals(userManager.getMainUser(), userHandle)) {
+                        throw new SecurityException(
+                                "VcnManagementService can only be used by callers running as"
+                                        + " the main user");
+                    }
+                });
     }
 
     private void enforceCallingUserAndCarrierPrivilege(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f9197e3c..3f540ad 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1189,8 +1189,8 @@
             if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
         }
         final boolean wasStartRequested = r.startRequested;
-        r.lastActivity = SystemClock.uptimeMillis();
-        r.startRequested = true;
+        mAm.mProcessStateController.setServiceLastActivityTime(r, SystemClock.uptimeMillis());
+        mAm.mProcessStateController.setStartRequested(r, true);
         r.delayedStop = false;
         r.fgRequired = fgRequired;
         r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
@@ -1623,7 +1623,7 @@
             FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
                     serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
             mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
-            service.startRequested = false;
+            mAm.mProcessStateController.setStartRequested(service, false);
             if (service.tracker != null) {
                 synchronized (mAm.mProcessStats.mLock) {
                     service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -1812,7 +1812,7 @@
             FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
                     serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
             mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
-            r.startRequested = false;
+            mAm.mProcessStateController.setStartRequested(r, false);
             if (r.tracker != null) {
                 synchronized (mAm.mProcessStats.mLock) {
                     r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -2618,7 +2618,7 @@
                     }
                     notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
                     r.foregroundNoti = notification;
-                    r.foregroundServiceType = foregroundServiceType;
+                    mAm.mProcessStateController.setForegroundServiceType(r, foregroundServiceType);
                     if (!r.isForeground) {
                         final ServiceMap smap = getServiceMapLocked(r.userId);
                         if (smap != null) {
@@ -2643,7 +2643,7 @@
                             }
                             active.mNumActive++;
                         }
-                        r.isForeground = true;
+                        mAm.mProcessStateController.setIsForegroundService(r, true);
 
                         // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
                         // be deferred, make a copy of mAllowStartForeground and
@@ -2772,7 +2772,7 @@
                     }
                 }
 
-                r.isForeground = false;
+                mAm.mProcessStateController.setIsForegroundService(r, false);
                 r.mFgsExitTime = SystemClock.uptimeMillis();
                 synchronized (mAm.mProcessStats.mLock) {
                     final ServiceState stracker = r.getTracker();
@@ -3565,7 +3565,7 @@
     private void maybeUpdateShortFgsTrackingLocked(ServiceRecord sr,
             boolean extendTimeout) {
         if (!sr.isShortFgs()) {
-            sr.clearShortFgsInfo(); // Just in case we have it.
+            mAm.mProcessStateController.clearShortFgsInfo(sr); // Just in case we have it.
             unscheduleShortFgsTimeoutLocked(sr);
             return;
         }
@@ -3581,7 +3581,7 @@
                 }
             }
             traceInstant("short FGS start/extend: ", sr);
-            sr.setShortFgsInfo(SystemClock.uptimeMillis());
+            mAm.mProcessStateController.setShortFgsInfo(sr, SystemClock.uptimeMillis());
 
             // We'll restart the timeout.
             unscheduleShortFgsTimeoutLocked(sr);
@@ -3605,7 +3605,7 @@
      * Stop the timeout for a ServiceRecord, if it's of a short-FGS.
      */
     private void maybeStopShortFgsTimeoutLocked(ServiceRecord sr) {
-        sr.clearShortFgsInfo(); // Always clear, just in case.
+        mAm.mProcessStateController.clearShortFgsInfo(sr); // Always clear, just in case.
         if (!sr.isShortFgs()) {
             return;
         }
@@ -3993,7 +3993,7 @@
     private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
         maybeStopShortFgsTimeoutLocked(service);
         final ProcessServiceRecord psr = service.app.mServices;
-        psr.stopService(service);
+        mAm.mProcessStateController.stopService(psr, service);
         psr.updateBoundClientUids();
         if (service.allowlistManager) {
             updateAllowlistManagerLocked(psr);
@@ -4047,7 +4047,7 @@
             }
         }
         if (anyClientActivities != psr.hasClientActivities()) {
-            psr.setHasClientActivities(anyClientActivities);
+            mAm.mProcessStateController.setHasClientActivities(psr, anyClientActivities);
             if (updateLru) {
                 mAm.updateLruProcessLocked(psr.mApp, anyClientActivities, null);
             }
@@ -4216,7 +4216,8 @@
             }
 
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
-                s.lastActivity = SystemClock.uptimeMillis();
+                mAm.mProcessStateController.setServiceLastActivityTime(s,
+                        SystemClock.uptimeMillis());
                 if (!s.hasAutoCreateConnections()) {
                     // This is the first binding, let the tracker know.
                     synchronized (mAm.mProcessStats.mLock) {
@@ -4253,12 +4254,12 @@
             if (activity != null) {
                 activity.addConnection(c);
             }
-            clientPsr.addConnection(c);
+            mAm.mProcessStateController.addConnection(clientPsr, c);
             c.startAssociationIfNeeded();
             // Don't set hasAboveClient if binding to self to prevent modifyRawOomAdj() from
             // dropping the process' adjustment level.
             if (b.client != s.app && c.hasFlag(Context.BIND_ABOVE_CLIENT)) {
-                clientPsr.setHasAboveClient(true);
+                mAm.mProcessStateController.setHasAboveClient(clientPsr, true);
             }
             if (c.hasFlag(BIND_ALLOW_WHITELIST_MANAGEMENT)) {
                 s.allowlistManager = true;
@@ -4274,7 +4275,8 @@
             if (s.app != null && s.app.mState != null
                     && s.app.mState.getCurProcState() <= PROCESS_STATE_TOP
                     && c.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)) {
-                s.lastTopAlmostPerceptibleBindRequestUptimeMs = SystemClock.uptimeMillis();
+                mAm.mProcessStateController.setLastTopAlmostPerceptibleBindRequest(s,
+                        SystemClock.uptimeMillis());
             }
 
             if (s.app != null) {
@@ -4312,7 +4314,8 @@
 
             boolean needOomAdj = false;
             if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
-                s.lastActivity = SystemClock.uptimeMillis();
+                mAm.mProcessStateController.setServiceLastActivityTime(s,
+                        SystemClock.uptimeMillis());
                 needOomAdj = (serviceBindingOomAdjPolicy
                         & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) == 0;
                 if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
@@ -4328,7 +4331,7 @@
             if (s.app != null) {
                 ProcessServiceRecord servicePsr = s.app.mServices;
                 if (c.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
-                    servicePsr.setTreatLikeActivity(true);
+                    mAm.mProcessStateController.setTreatLikeActivity(servicePsr, true);
                 }
                 if (s.allowlistManager) {
                     servicePsr.mAllowlistManager = true;
@@ -4575,7 +4578,9 @@
                     }
                     // This could have made the service less important.
                     if (r.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
-                        psr.setTreatLikeActivity(true);
+                        // TODO(b/367545398): the following line is a bug. A service unbind
+                        //  should potentially lower a process's importance, not elevate it.
+                        mAm.mProcessStateController.setTreatLikeActivity(psr, true);
                         mAm.updateLruProcessLocked(app, true, null);
                     }
                     // If the bindee is more important than the binder, we may skip the OomAdjuster.
@@ -5165,8 +5170,9 @@
             }
             if (r.app != null) {
                 psr = r.app.mServices;
-                psr.startExecutingService(r);
-                psr.setExecServicesFg(psr.shouldExecServicesFg() || fg);
+                mAm.mProcessStateController.startExecutingService(psr, r);
+                mAm.mProcessStateController.setExecServicesFg(psr,
+                        psr.shouldExecServicesFg() || fg);
                 if (timeoutNeeded && psr.numberOfExecutingServices() == 1) {
                     if (!shouldSkipTimeout) {
                         scheduleServiceTimeoutLocked(r.app);
@@ -5178,7 +5184,7 @@
         } else if (r.app != null && fg) {
             psr = r.app.mServices;
             if (!psr.shouldExecServicesFg()) {
-                psr.setExecServicesFg(true);
+                mAm.mProcessStateController.setExecServicesFg(psr, true);
                 if (timeoutNeeded) {
                     if (!shouldSkipTimeout) {
                         scheduleServiceTimeoutLocked(r.app);
@@ -6023,11 +6029,13 @@
             Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                     + ", ProcessRecord.uid = " + app.uid);
         r.setProcess(app, thread, pid, uidRecord);
-        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
+        final long now = SystemClock.uptimeMillis();
+        r.restartTime = now;
+        mAm.mProcessStateController.setServiceLastActivityTime(r, now);
         final boolean skipOomAdj = (serviceBindingOomAdjPolicy
                 & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) != 0;
         final ProcessServiceRecord psr = app.mServices;
-        final boolean newService = psr.startService(r);
+        final boolean newService = mAm.mProcessStateController.startService(psr, r);
         bumpServiceExecutingLocked(r, execInFg, "create",
                 OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */,
                 skipOomAdj /* skipTimeoutIfPossible */);
@@ -6086,7 +6094,7 @@
 
                 // Cleanup.
                 if (newService) {
-                    psr.stopService(r);
+                    mAm.mProcessStateController.stopService(psr, r);
                     r.setProcess(null, null, 0, null);
                 }
 
@@ -6431,7 +6439,7 @@
             mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
         }
 
-        r.isForeground = false;
+        mAm.mProcessStateController.setIsForegroundService(r, false);
         r.mFgsNotificationWasDeferred = false;
         dropFgsNotificationStateLocked(r);
         r.foregroundId = 0;
@@ -6582,9 +6590,9 @@
         }
         if (b.client != skipApp) {
             final ProcessServiceRecord psr = b.client.mServices;
-            psr.removeConnection(c);
+            mAm.mProcessStateController.removeConnection(psr, c);
             if (c.hasFlag(Context.BIND_ABOVE_CLIENT)) {
-                psr.updateHasAboveClientLocked();
+                mAm.mProcessStateController.updateHasAboveClientLocked(psr);
             }
             // If this connection requested allowlist management, see if we should
             // now clear that state.
@@ -6600,7 +6608,7 @@
             }
             // And for almost perceptible exceptions.
             if (c.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)) {
-                psr.updateHasTopStartedAlmostPerceptibleServices();
+                mAm.mProcessStateController.updateHasTopStartedAlmostPerceptibleServices(psr);
             }
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app.mServices, c, true);
@@ -6799,8 +6807,8 @@
                 final ProcessServiceRecord psr = r.app.mServices;
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                         "Nesting at 0 of " + r.shortInstanceName);
-                psr.setExecServicesFg(false);
-                psr.stopExecutingService(r);
+                mAm.mProcessStateController.setExecServicesFg(psr, false);
+                mAm.mProcessStateController.stopExecutingService(psr, r);
                 if (psr.numberOfExecutingServices() == 0) {
                     if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
                             "No more executingServices of " + r.shortInstanceName);
@@ -6809,7 +6817,7 @@
                     // Need to re-evaluate whether the app still needs to be in the foreground.
                     for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
                         if (psr.getExecutingServiceAt(i).executeFg) {
-                            psr.setExecServicesFg(true);
+                            mAm.mProcessStateController.setExecServicesFg(psr, true);
                             break;
                         }
                     }
@@ -6822,9 +6830,9 @@
                 }
                 if (oomAdjReason != OOM_ADJ_REASON_NONE) {
                     if (enqueueOomAdj) {
-                        mAm.enqueueOomAdjTargetLocked(r.app);
+                        mAm.mProcessStateController.enqueueUpdateTarget(r.app);
                     } else {
-                        mAm.updateOomAdjLocked(r.app, oomAdjReason);
+                        mAm.mProcessStateController.runUpdate(r.app, oomAdjReason);
                     }
                 } else {
                     // Skip oom adj if it wasn't bumped during the bumpServiceExecutingLocked()
@@ -7209,8 +7217,7 @@
             removeConnectionLocked(r, app, null, true);
         }
         updateServiceConnectionActivitiesLocked(psr);
-        psr.removeAllConnections();
-        psr.removeAllSdkSandboxConnections();
+        mAm.mProcessStateController.removeAllConnections(psr);
 
         psr.mAllowlistManager = false;
 
@@ -7220,7 +7227,7 @@
             mAm.mBatteryStatsService.noteServiceStopLaunch(sr.appInfo.uid, sr.name.getPackageName(),
                     sr.name.getClassName());
             if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
-                sr.app.mServices.stopService(sr);
+                mAm.mProcessStateController.stopService(sr.app.mServices, sr);
                 sr.app.mServices.updateBoundClientUids();
             }
             sr.setProcess(null, null, 0, null);
@@ -7290,7 +7297,7 @@
             // Unless the process is persistent, this process record is going away,
             // so make sure the service is cleaned out of it.
             if (!app.isPersistent()) {
-                psr.stopService(sr);
+                mAm.mProcessStateController.stopService(psr, sr);
                 psr.updateBoundClientUids();
             }
 
@@ -7331,7 +7338,7 @@
                     // Update to stopped state because the explicit start is gone. The service is
                     // scheduled to restart for other reason (e.g. connections) so we don't bring
                     // down it.
-                    sr.startRequested = false;
+                    mAm.mProcessStateController.setStartRequested(sr, false);
                     if (sr.tracker != null) {
                         synchronized (mAm.mProcessStats.mLock) {
                             sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -7345,7 +7352,7 @@
         mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_STOP_SERVICE);
 
         if (!allowRestart) {
-            psr.stopAllServices();
+            mAm.mProcessStateController.stopAllServices(psr);
             psr.clearBoundClientUids();
 
             // Make sure there are no more restarting services for this process.
@@ -7387,7 +7394,7 @@
             }
         }
 
-        psr.stopAllExecutingServices();
+        mAm.mProcessStateController.stopAllExecutingServices(psr);
         psr.noteScheduleServiceTimeoutPending(false);
     }
 
@@ -9213,14 +9220,14 @@
                 new ForegroundServiceDelegation(options, connection);
         r.mFgsDelegation = delegation;
         mFgsDelegations.put(delegation, r);
-        r.isForeground = true;
+        mAm.mProcessStateController.setIsForegroundService(r, true);
         r.mFgsEnterTime = SystemClock.uptimeMillis();
-        r.foregroundServiceType = options.mForegroundServiceTypes;
+        mAm.mProcessStateController.setForegroundServiceType(r, options.mForegroundServiceTypes);
         r.updateOomAdjSeq();
         setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
                 BackgroundStartPrivileges.NONE,  false /* isBindService */);
         final ProcessServiceRecord psr = callerApp.mServices;
-        final boolean newService = psr.startService(r);
+        final boolean newService = mAm.mProcessStateController.startService(psr, r);
         // updateOomAdj.
         updateServiceForegroundLocked(psr, /* oomAdj= */ true);
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index f5a297b..65a2c18 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -246,7 +246,7 @@
 
     static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000;
 
-    private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false;
+    private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = Flags.oomadjusterCachedAppTiers();
     private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000;
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 74437cd..217ef20 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -134,7 +134,6 @@
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
 
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED;
 import static com.android.sdksandbox.flags.Flags.sdkSandboxInstrumentationInfo;
 import static com.android.server.am.ActiveServices.FGS_SAW_RESTRICTIONS;
@@ -415,7 +414,6 @@
 import com.android.internal.os.Zygote;
 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.internal.policy.AttributeCache;
-import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
@@ -441,6 +439,7 @@
 import com.android.server.appop.AppOpsService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
+import com.android.server.crashrecovery.CrashRecoveryAdaptor;
 import com.android.server.crashrecovery.CrashRecoveryHelper;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.firewall.IntentFirewall;
@@ -626,6 +625,8 @@
             DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSZ");
 
     OomAdjuster mOomAdjuster;
+    @GuardedBy("this")
+    ProcessStateController mProcessStateController;
 
     static final String EXTRA_TITLE = "android.intent.extra.TITLE";
     static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
@@ -1958,7 +1959,7 @@
                         new HostingRecord(HostingRecord.HOSTING_TYPE_SYSTEM));
                 app.setPersistent(true);
                 app.setPid(MY_PID);
-                app.mState.setMaxAdj(ProcessList.SYSTEM_ADJ);
+                mProcessStateController.setMaxAdj(app, ProcessList.SYSTEM_ADJ);
                 app.makeActive(new ApplicationThreadDeferred(mSystemThread.getApplicationThread()),
                         mProcessStats);
                 app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_SYSTEM);
@@ -2208,7 +2209,7 @@
                 mService.mBroadcastController.startBroadcastObservers();
             } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 if (!refactorCrashrecovery()) {
-                    mService.mPackageWatchdog.onPackagesReady();
+                    CrashRecoveryAdaptor.packageWatchdogOnPackagesReady(mService.mPackageWatchdog);
                 } else {
                     mService.mCrashRecoveryHelper.registerConnectivityModuleHealthListener();
                 }
@@ -2394,9 +2395,11 @@
         mProcessList.init(this, activeUids, mPlatformCompat);
         mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null);
         mPhantomProcessList = new PhantomProcessList(this);
-        mOomAdjuster = mConstants.ENABLE_NEW_OOMADJ
-                ? new OomAdjusterModernImpl(this, mProcessList, activeUids, handlerThread)
-                : new OomAdjuster(this, mProcessList, activeUids, handlerThread);
+        mProcessStateController = new ProcessStateController.Builder(this, mProcessList, activeUids)
+                .setHandlerThread(handlerThread)
+                .useModernOomAdjuster(mConstants.ENABLE_NEW_OOMADJ)
+                .build();
+        mOomAdjuster = mProcessStateController.getOomAdjuster();
 
         mIntentFirewall = injector.getIntentFirewall();
         mProcessStats = new ProcessStatsService(this, mContext.getCacheDir());
@@ -2459,9 +2462,10 @@
         mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(),
                 new LowMemDetector(this));
         mPhantomProcessList = new PhantomProcessList(this);
-        mOomAdjuster = mConstants.ENABLE_NEW_OOMADJ
-                ? new OomAdjusterModernImpl(this, mProcessList, activeUids)
-                : new OomAdjuster(this, mProcessList, activeUids);
+        mProcessStateController = new ProcessStateController.Builder(this, mProcessList, activeUids)
+                .useModernOomAdjuster(mConstants.ENABLE_NEW_OOMADJ)
+                .build();
+        mOomAdjuster = mProcessStateController.getOomAdjuster();
 
         mBroadcastQueue = mInjector.getBroadcastQueue(this);
         mBroadcastController = new BroadcastController(mContext, this, mBroadcastQueue);
@@ -2753,8 +2757,11 @@
         if (isolated) {
             if (mIsolatedAppBindArgs == null) {
                 mIsolatedAppBindArgs = new ArrayMap<>(1);
+                // See b/79378449 about the following exemption.
                 addServiceToMap(mIsolatedAppBindArgs, "package");
-                addServiceToMap(mIsolatedAppBindArgs, "permissionmgr");
+                if (!android.server.Flags.removeJavaServiceManagerCache()) {
+                    addServiceToMap(mIsolatedAppBindArgs, "permissionmgr");
+                }
             }
             return mIsolatedAppBindArgs;
         }
@@ -2765,27 +2772,33 @@
             // Add common services.
             // IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
             // Enable the check in ApplicationThread.bindApplication() to make sure.
+            if (!android.server.Flags.removeJavaServiceManagerCache()) {
+                addServiceToMap(mAppBindArgs, "permissionmgr");
+                addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.CONNECTIVITY_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.ACCESSIBILITY_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
+                addServiceToMap(mAppBindArgs, "graphicsstats");
+                addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
+                addServiceToMap(mAppBindArgs, "content");
+                addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
+                addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
+                addServiceToMap(mAppBindArgs, "mount");
+                addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
+            }
+            // See b/79378449
+            // Getting the window service and package service binder from servicemanager
+            // is blocked for Apps. However they are necessary for apps.
+            // TODO: remove exception
             addServiceToMap(mAppBindArgs, "package");
-            addServiceToMap(mAppBindArgs, "permissionmgr");
             addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.CONNECTIVITY_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.ACCESSIBILITY_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
-            addServiceToMap(mAppBindArgs, "graphicsstats");
-            addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
-            addServiceToMap(mAppBindArgs, "content");
-            addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
-            addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
-            addServiceToMap(mAppBindArgs, "mount");
-            addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
         }
         return mAppBindArgs;
     }
@@ -4574,7 +4587,7 @@
         EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
 
         synchronized (mProcLock) {
-            mOomAdjuster.setAttachingProcessStatesLSP(app);
+            mProcessStateController.setAttachingProcessStatesLSP(app);
             clearProcessForegroundLocked(app);
             app.setDebugging(false);
             app.setKilledByAm(false);
@@ -4648,8 +4661,6 @@
                 notifyPackageUse(instr.mClass.getPackageName(),
                                  PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
             }
-            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
-                    processName, app.getWindowProcessController().getConfiguration());
             ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
 
             ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr);
@@ -4770,7 +4781,7 @@
                 app.makeActive(new ApplicationThreadDeferred(thread), mProcessStats);
                 checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
             }
-            app.setPendingFinishAttach(true);
+            mProcessStateController.setPendingFinishAttach(app, true);
 
             updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
@@ -4854,7 +4865,7 @@
 
         synchronized (this) {
             // Mark the finish attach application phase as completed
-            app.setPendingFinishAttach(false);
+            mProcessStateController.setPendingFinishAttach(app, false);
 
             final boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
             final String processName = app.processName;
@@ -5009,7 +5020,7 @@
         // If another follow up update is needed, it will be scheduled by OomAdjuster.
         mHandler.removeMessages(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG);
         synchronized (this) {
-            mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+            mProcessStateController.runFollowUpUpdate();
         }
     }
 
@@ -5536,7 +5547,6 @@
     public int sendIntentSender(IApplicationThread caller, IIntentSender target,
             IBinder allowlistToken, int code, Intent intent, String resolvedType,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-        addCreatorToken(intent);
         if (target instanceof PendingIntentRecord) {
             final PendingIntentRecord originalRecord = (PendingIntentRecord) target;
 
@@ -5578,19 +5588,23 @@
                 intent = new Intent(Intent.ACTION_MAIN);
             }
             try {
+                final int callingUid = Binder.getCallingUid();
+                final String packageName;
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    packageName = AppGlobals.getPackageManager().getNameForUid(callingUid);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+
                 if (allowlistToken != null) {
-                    final int callingUid = Binder.getCallingUid();
-                    final String packageName;
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        packageName = AppGlobals.getPackageManager().getNameForUid(callingUid);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
                     Slog.wtf(TAG, "Send a non-null allowlistToken to a non-PI target."
                             + " Calling package: " + packageName + "; intent: " + intent
                             + "; options: " + options);
                 }
+
+                addCreatorToken(intent, packageName);
+
                 target.send(code, intent, resolvedType, null, null,
                         requiredPermission, options);
             } catch (RemoteException e) {
@@ -5804,10 +5818,10 @@
                 if (pr == null) {
                     return;
                 }
-                pr.mState.setForcingToImportant(null);
+                mProcessStateController.setForcingToImportant(pr, null);
                 clearProcessForegroundLocked(pr);
             }
-            updateOomAdjLocked(pr, OOM_ADJ_REASON_UI_VISIBILITY);
+            mProcessStateController.runUpdate(pr, OOM_ADJ_REASON_UI_VISIBILITY);
         }
     }
 
@@ -5830,7 +5844,7 @@
                     oldToken.token.unlinkToDeath(oldToken, 0);
                     mImportantProcesses.remove(pid);
                     if (pr != null) {
-                        pr.mState.setForcingToImportant(null);
+                        mProcessStateController.setForcingToImportant(pr, null);
                     }
                     changed = true;
                 }
@@ -5844,7 +5858,7 @@
                     try {
                         token.linkToDeath(newToken, 0);
                         mImportantProcesses.put(pid, newToken);
-                        pr.mState.setForcingToImportant(newToken);
+                        mProcessStateController.setForcingToImportant(pr, newToken);
                         changed = true;
                     } catch (RemoteException e) {
                         // If the process died while doing this, we will later
@@ -5854,7 +5868,7 @@
             }
 
             if (changed) {
-                updateOomAdjLocked(pr, OOM_ADJ_REASON_UI_VISIBILITY);
+                mProcessStateController.runUpdate(pr, OOM_ADJ_REASON_UI_VISIBILITY);
             }
         }
     }
@@ -7274,7 +7288,7 @@
 
         if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
             app.setPersistent(true);
-            app.mState.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
+            mProcessStateController.setMaxAdj(app, ProcessList.PERSISTENT_PROC_ADJ);
         }
         if (app.getThread() == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
@@ -7377,7 +7391,7 @@
                 mServices.updateScreenStateLocked(isAwake);
                 reportCurWakefulnessUsageEvent();
                 mActivityTaskManager.onScreenAwakeChanged(isAwake);
-                mOomAdjuster.onWakefulnessChanged(wakefulness);
+                mProcessStateController.setWakefulness(wakefulness);
 
                 updateOomAdjLocked(OOM_ADJ_REASON_UI_VISIBILITY);
             }
@@ -8346,16 +8360,10 @@
                         Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
                         return;
                     }
-                    if (pr.mState.hasTopUi() != hasTopUi) {
-                        if (DEBUG_OOM_ADJ) {
-                            Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
-                        }
-                        pr.mState.setHasTopUi(hasTopUi);
-                        changed = true;
-                    }
+                    changed = mProcessStateController.setHasTopUi(pr, hasTopUi);
                 }
                 if (changed) {
-                    updateOomAdjLocked(pr, OOM_ADJ_REASON_UI_VISIBILITY);
+                    mProcessStateController.runUpdate(pr, OOM_ADJ_REASON_UI_VISIBILITY);
                 }
             }
         } finally {
@@ -12371,7 +12379,7 @@
                         continue;
                     }
                     endTime = SystemClock.currentThreadTimeMillis();
-                    hasSwapPss = mi.hasSwappedOutPss;
+                    hasSwapPss = hasSwapPss || mi.hasSwappedOutPss;
                     memtrackGraphics = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS);
                     memtrackGl = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GL);
                 } else {
@@ -12825,6 +12833,8 @@
             final long lostRAM = memInfo.getTotalSizeKb()
                     - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+                    // NR_SHMEM is subtracted twice (getCachedSizeKb() and getKernelUsedSizeKb())
+                    + memInfo.getShmemSizeKb()
                     - kernelUsed - memInfo.getZramTotalSizeKb();
             if (!opts.isCompact) {
                 pw.print(" Used RAM: "); pw.print(stringifyKBSize(ss[INDEX_TOTAL_PSS] - cachedPss
@@ -13047,7 +13057,7 @@
                     continue;
                 }
                 endTime = SystemClock.currentThreadTimeMillis();
-                hasSwapPss = mi.hasSwappedOutPss;
+                hasSwapPss = hasSwapPss || mi.hasSwappedOutPss;
             } else {
                 reportType = ProcessStats.ADD_PSS_EXTERNAL;
                 startTime = SystemClock.currentThreadTimeMillis();
@@ -13338,6 +13348,8 @@
             long lostRAM = memInfo.getTotalSizeKb()
                     - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+                    // NR_SHMEM is subtracted twice (getCachedSizeKb() and getKernelUsedSizeKb())
+                    + memInfo.getShmemSizeKb()
                     - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
             proto.write(MemInfoDumpProto.USED_PSS_KB, ss[INDEX_TOTAL_PSS] - cachedPss);
             proto.write(MemInfoDumpProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
@@ -13624,7 +13636,7 @@
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
-        addCreatorToken(service);
+        addCreatorToken(service, callingPackage);
         if (service != null) {
             // Refuse possible leaked file descriptors
             if (service.hasFileDescriptors()) {
@@ -13886,7 +13898,7 @@
 
         validateServiceInstanceName(instanceName);
 
-        addCreatorToken(service);
+        addCreatorToken(service, callingPackage);
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                 final ComponentName cn = service.getComponent();
@@ -14084,10 +14096,14 @@
                 proc.setInFullBackup(true);
             }
             r.app = proc;
+            // TODO(b/369300367): This code suggests there could be a previous backup being
+            //  replaced here, but an OomAdjsuter update is not triggered on the previous app
+            //  (whose state will change from being removed from mBackupTargets).
             final BackupRecord backupTarget = mBackupTargets.get(targetUserId);
             oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1;
             newBackupUid = proc.isInFullBackup() ? r.appInfo.uid : -1;
             mBackupTargets.put(targetUserId, r);
+            mProcessStateController.setBackupTarget(proc, targetUserId);
 
             proc.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_BACKUP);
 
@@ -14141,6 +14157,7 @@
                 }
                 mBackupTargets.removeAt(indexOfKey);
             }
+            mProcessStateController.stopBackupTarget(userId);
         }
 
         JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
@@ -14219,6 +14236,8 @@
 
                 // Not backing this app up any more; reset its OOM adjustment
                 final ProcessRecord proc = backupTarget.app;
+                // TODO(b/369300367): Triggering the update before the state is actually set
+                //  seems wrong.
                 updateOomAdjLocked(proc, OOM_ADJ_REASON_BACKUP);
                 proc.setInFullBackup(false);
                 proc.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_BACKUP);
@@ -14237,6 +14256,7 @@
                 }
             } finally {
                 mBackupTargets.delete(userId);
+                mProcessStateController.stopBackupTarget(userId);
             }
         }
 
@@ -15309,7 +15329,8 @@
                             proc.info.packageName, proc.info.uid, proc.getPid(), isForeground);
                 }
             }
-            psr.setHasForegroundServices(isForeground, fgServiceTypes, hasTypeNoneFgs);
+            mProcessStateController.setHasForegroundServices(psr, isForeground, fgServiceTypes,
+                    hasTypeNoneFgs);
             ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
                     proc.info.uid);
             if (isForeground) {
@@ -15340,7 +15361,7 @@
                     ProcessChangeItem.CHANGE_FOREGROUND_SERVICES, fgServiceTypes);
         }
         if (oomAdj) {
-            updateOomAdjLocked(proc, OOM_ADJ_REASON_UI_VISIBILITY);
+            mProcessStateController.runUpdate(proc, OOM_ADJ_REASON_UI_VISIBILITY);
         }
     }
 
@@ -15390,7 +15411,7 @@
      */
     @GuardedBy("this")
     void enqueueOomAdjTargetLocked(ProcessRecord app) {
-        mOomAdjuster.enqueueOomAdjTargetLocked(app);
+        mProcessStateController.enqueueUpdateTarget(app);
     }
 
     /**
@@ -15398,7 +15419,7 @@
      */
     @GuardedBy("this")
     void removeOomAdjTargetLocked(ProcessRecord app, boolean procDied) {
-        mOomAdjuster.removeOomAdjTargetLocked(app, procDied);
+        mProcessStateController.removeUpdateTarget(app, procDied);
     }
 
     /**
@@ -15407,7 +15428,7 @@
      */
     @GuardedBy("this")
     void updateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
-        mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason);
+        mProcessStateController.runPendingUpdate(oomAdjReason);
     }
 
     static final class ProcStatsRunnable implements Runnable {
@@ -15426,7 +15447,7 @@
 
     @GuardedBy("this")
     final void updateOomAdjLocked(@OomAdjReason int oomAdjReason) {
-        mOomAdjuster.updateOomAdjLocked(oomAdjReason);
+        mProcessStateController.runFullUpdate(oomAdjReason);
     }
 
     /**
@@ -15438,7 +15459,7 @@
      */
     @GuardedBy("this")
     final boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {
-        return mOomAdjuster.updateOomAdjLocked(app, oomAdjReason);
+        return mProcessStateController.runUpdate(app, oomAdjReason);
     }
 
     @Override
@@ -15703,7 +15724,7 @@
 
     @GuardedBy({"this", "mProcLock"})
     final void setUidTempAllowlistStateLSP(int uid, boolean onAllowlist) {
-        mOomAdjuster.setUidTempAllowlistStateLSP(uid, onAllowlist);
+        mProcessStateController.setUidTempAllowlistStateLSP(uid, onAllowlist);
     }
 
     private void trimApplications(boolean forceFullOomAdj, @OomAdjReason int oomAdjReason) {
@@ -16753,12 +16774,9 @@
                         return;
                     }
                 }
-                if (pr.mState.hasOverlayUi() == hasOverlayUi) {
-                    return;
+                if (mProcessStateController.setHasOverlayUi(pr, hasOverlayUi)) {
+                    mProcessStateController.runUpdate(pr, OOM_ADJ_REASON_UI_VISIBILITY);
                 }
-                pr.mState.setHasOverlayUi(hasOverlayUi);
-                //Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
-                updateOomAdjLocked(pr, OOM_ADJ_REASON_UI_VISIBILITY);
             }
         }
 
@@ -17164,7 +17182,7 @@
                 Slog.v(TAG_SERVICE,
                         "startServiceInPackage: " + service + " type=" + resolvedType);
             }
-            addCreatorToken(service);
+            addCreatorToken(service, callingPackage);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res;
             try {
@@ -17992,8 +18010,8 @@
         }
 
         @Override
-        public void addCreatorToken(Intent intent) {
-            ActivityManagerService.this.addCreatorToken(intent);
+        public void addCreatorToken(Intent intent, String creatorPackage) {
+            ActivityManagerService.this.addCreatorToken(intent, creatorPackage);
         }
     }
 
@@ -19150,9 +19168,9 @@
         private final Key mKeyFields;
         private final WeakReference<IntentCreatorToken> mRef;
 
-        public IntentCreatorToken(int creatorUid, Intent intent) {
+        public IntentCreatorToken(int creatorUid, String creatorPackage, Intent intent) {
             super();
-            this.mKeyFields = new Key(creatorUid, intent);
+            this.mKeyFields = new Key(creatorUid, creatorPackage, intent);
             mRef = new WeakReference<>(this);
         }
 
@@ -19160,7 +19178,10 @@
             return mKeyFields.mCreatorUid;
         }
 
-        /** {@hide} */
+        public String getCreatorPackage() {
+            return mKeyFields.mCreatorPackage;
+        }
+
         public static boolean isValid(@NonNull Intent intent) {
             IBinder binder = intent.getCreatorToken();
             IntentCreatorToken token = null;
@@ -19168,7 +19189,8 @@
                 token = (IntentCreatorToken) binder;
             }
             return token != null && token.mKeyFields.equals(
-                    new Key(token.mKeyFields.mCreatorUid, intent));
+                    new Key(token.mKeyFields.mCreatorUid, token.mKeyFields.mCreatorPackage,
+                            intent));
         }
 
         @Override
@@ -19192,8 +19214,9 @@
         }
 
         private static class Key {
-            private Key(int creatorUid, Intent intent) {
+            private Key(int creatorUid, String creatorPackage, Intent intent) {
                 this.mCreatorUid = creatorUid;
+                this.mCreatorPackage = creatorPackage;
                 this.mAction = intent.getAction();
                 this.mData = intent.getData();
                 this.mType = intent.getType();
@@ -19210,6 +19233,7 @@
             }
 
             private final int mCreatorUid;
+            private final String mCreatorPackage;
             private final String mAction;
             private final Uri mData;
             private final String mType;
@@ -19223,17 +19247,20 @@
                 if (this == o) return true;
                 if (o == null || getClass() != o.getClass()) return false;
                 Key key = (Key) o;
-                return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags && Objects.equals(
-                        mAction, key.mAction) && Objects.equals(mData, key.mData)
-                        && Objects.equals(mType, key.mType) && Objects.equals(mPackage,
-                        key.mPackage) && Objects.equals(mComponent, key.mComponent)
+                return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags
+                        && Objects.equals(mCreatorPackage, key.mCreatorPackage)
+                        && Objects.equals(mAction, key.mAction)
+                        && Objects.equals(mData, key.mData)
+                        && Objects.equals(mType, key.mType)
+                        && Objects.equals(mPackage, key.mPackage)
+                        && Objects.equals(mComponent, key.mComponent)
                         && Objects.equals(mClipDataUris, key.mClipDataUris);
             }
 
             @Override
             public int hashCode() {
-                return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent,
-                        mFlags, mClipDataUris);
+                return Objects.hash(mCreatorUid, mCreatorPackage, mAction, mData, mType, mPackage,
+                        mComponent, mFlags, mClipDataUris);
             }
         }
     }
@@ -19244,7 +19271,7 @@
      * @param intent The given intent
      * @hide
      */
-    public void addCreatorToken(@Nullable Intent intent) {
+    public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
         if (!preventIntentRedirect()) return;
 
         if (intent == null || intent.getExtraIntentKeys() == null) return;
@@ -19257,7 +19284,7 @@
                     continue;
                 }
                 Slog.wtf(TAG, "A creator token is added to an intent.");
-                IBinder creatorToken = createIntentCreatorToken(extraIntent);
+                IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
                 if (creatorToken != null) {
                     extraIntent.setCreatorToken(creatorToken);
                 }
@@ -19270,15 +19297,15 @@
         }
     }
 
-    private IBinder createIntentCreatorToken(Intent intent) {
+    private IBinder createIntentCreatorToken(Intent intent, String creatorPackage) {
         if (IntentCreatorToken.isValid(intent)) return null;
         int creatorUid = getCallingUid();
-        IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, intent);
+        IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent);
         IntentCreatorToken token;
         synchronized (sIntentCreatorTokenCache) {
             WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key);
             if (ref == null || ref.get() == null) {
-                token = new IntentCreatorToken(creatorUid, intent);
+                token = new IntentCreatorToken(creatorUid, creatorPackage, intent);
                 sIntentCreatorTokenCache.put(key, token.mRef);
             } else {
                 token = ref.get();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 210301e..02e2c39 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -110,6 +110,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.ZygoteProcess;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -442,6 +443,8 @@
                     return runSetForegroundServiceDelegate(pw);
                 case "capabilities":
                     return runCapabilities(pw);
+                case "set-app-zygote-preload-timeout":
+                    return runSetAppZygotePreloadTimeout(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -451,6 +454,15 @@
         return -1;
     }
 
+    int runSetAppZygotePreloadTimeout(PrintWriter pw) throws RemoteException {
+        final String timeout = getNextArgRequired();
+        final int timeoutMs = Integer.parseInt(timeout);
+
+        ZygoteProcess.setAppZygotePreloadTimeout(timeoutMs);
+
+        return 0;
+    }
+
     int runCapabilities(PrintWriter pw) throws RemoteException {
         final PrintWriter err = getErrPrintWriter();
         boolean outputAsProtobuf = false;
@@ -4623,6 +4635,8 @@
             pw.println("  capabilities [--protobuf]");
             pw.println("         Output am supported features (text format). Options are:");
             pw.println("         --protobuf: format output using protobuffer");
+            pw.println("  set-app-zygote-preload-timeout <TIMEOUT_IN_MS>");
+            pw.println("         Set the timeout for preloading code in the app-zygote");
             Intent.printIntentArgsHelp(pw, "");
         }
     }
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index dda48ad..4f2d69e 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1356,6 +1356,7 @@
     @GuardedBy("mService")
     void setMemFactorOverrideLocked(@MemFactor int factor) {
         mMemFactorOverride = factor;
+        mService.mProcessStateController.setIsLastMemoryLevelNormal(isLastMemoryLevelNormal());
     }
 
     @GuardedBy({"mService", "mProcLock"})
@@ -1423,6 +1424,7 @@
         }
 
         mLastMemoryLevel = memFactor;
+        mService.mProcessStateController.setIsLastMemoryLevelNormal(isLastMemoryLevelNormal());
         mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
 
         // Dispatch UI_HIDDEN to processes that need it
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 77ee279..c47cad9 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -860,6 +860,14 @@
         return sService;
     }
 
+    /**
+     * Override the {@link IBatteryStats} service, for testing.
+     */
+    @VisibleForTesting
+    public static void overrideService(IBatteryStats service) {
+        sService = service;
+    }
+
     @Override
     public int getServiceType() {
         return ServiceType.BATTERY_STATS;
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 15f1085..a00cac6 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -258,6 +258,7 @@
             final StringBuilder sb = new StringBuilder("registerReceiver: ");
             sb.append(Binder.getCallingUid()); sb.append('/');
             sb.append(receiverId == null ? "null" : receiverId); sb.append('/');
+            sb.append("p:"); sb.append(filter.getPriority()); sb.append('/');
             final int actionsCount = filter.safeCountActions();
             if (actionsCount > 0) {
                 for (int i = 0; i < actionsCount; ++i) {
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index ae5ae01..4f0ea51 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -40,7 +40,7 @@
     public final String clientPackage;
     public AssociationState.SourceState association;
     public final long createTime;
-    private Object mProcStatsLock;  // Internal lock for accessing AssociationState
+    private volatile Object mProcStatsLock;  // Internal lock for accessing AssociationState
 
     /**
      * Internal lock that guards access to the two counters.
@@ -118,19 +118,25 @@
      * Track the given proc state change.
      */
     public void trackProcState(int procState, int seq) {
-        if (association != null) {
-            synchronized (mProcStatsLock) {
+        if (association == null) {
+            return; // early exit to optimize on oomadj cycles
+        }
+        synchronized (mProcStatsLock) {
+            if (association != null) { // due to race-conditions, association may have become null
                 association.trackProcState(procState, seq, SystemClock.uptimeMillis());
             }
         }
     }
 
     public void stopAssociation() {
-        if (association != null) {
-            synchronized (mProcStatsLock) {
+        if (association == null) {
+            return; // early exit to optimize on oomadj cycles
+        }
+        synchronized (mProcStatsLock) {
+            if (association != null) {  // due to race-conditions, association may have become null
                 association.stop();
+                association = null;
             }
-            association = null;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index da40826..6e09a84 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -90,7 +90,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
-import com.android.server.RescueParty;
+import com.android.server.crashrecovery.CrashRecoveryAdaptor;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.sdksandbox.SdkSandboxManagerLocal;
@@ -325,7 +325,8 @@
                     final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
                     boolean success = !serviceBindingOomAdjPolicy()
                             || mService.mOomAdjuster.evaluateProviderConnectionAdd(r, cpr.proc)
-                            ? mService.updateOomAdjLocked(cpr.proc, OOM_ADJ_REASON_GET_PROVIDER)
+                            ? mService.mProcessStateController.runUpdate(cpr.proc,
+                            OOM_ADJ_REASON_GET_PROVIDER)
                             : true;
                     // XXX things have changed so updateOomAdjLocked doesn't actually tell us
                     // if the process has been successfully adjusted.  So to reduce races with
@@ -534,10 +535,9 @@
                             if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
                                 Slog.d(TAG, "Installing in existing process " + proc);
                             }
-                            final ProcessProviderRecord pr = proc.mProviders;
-                            if (!pr.hasProvider(cpi.name)) {
+                            if (mService.mProcessStateController.addPublishedProvider(proc,
+                                    cpi.name, cpr)) {
                                 checkTime(startTime, "getContentProviderImpl: scheduling install");
-                                pr.installProvider(cpi.name, cpr);
                                 mService.mOomAdjuster.unfreezeTemporarily(proc,
                                         CachedAppOptimizer.UNFREEZE_REASON_GET_PROVIDER);
                                 try {
@@ -881,7 +881,8 @@
             ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
             ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
             if (localCpr.hasExternalProcessHandles()) {
-                if (localCpr.removeExternalProcessHandleLocked(token)) {
+                if (mService.mProcessStateController.removeExternalProviderClient(localCpr,
+                        token)) {
                     mService.updateOomAdjLocked(localCpr.proc, OOM_ADJ_REASON_REMOVE_PROVIDER);
                 } else {
                     Slog.e(TAG, "Attempt to remove content provider " + localCpr
@@ -1381,7 +1382,7 @@
         mService.mOomAdjuster.initSettings();
 
         // Now that the settings provider is published we can consider sending in a rescue party.
-        RescueParty.onSettingsProviderPublished(mService.mContext);
+        CrashRecoveryAdaptor.rescuePartyOnSettingsProviderPublished(mService.mContext);
     }
 
     /**
@@ -1447,7 +1448,8 @@
             String callingPackage, String callingTag, boolean stable, boolean updateLru,
             long startTime, ProcessList processList, @UserIdInt int expectedUserId) {
         if (r == null) {
-            cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
+            mService.mProcessStateController.addExternalProviderClient(cpr, externalProcessToken,
+                    callingUid, callingTag);
             return null;
         }
 
@@ -1470,7 +1472,7 @@
         if (cpr.proc != null) {
             cpr.proc.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
         }
-        pr.addProviderConnection(conn);
+        mService.mProcessStateController.addProviderConnection(r, conn);
         mService.startAssociationLocked(r.uid, r.processName, r.mState.getCurProcState(),
                 cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
         if (updateLru && cpr.proc != null
@@ -1493,7 +1495,8 @@
             ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable,
             boolean enforceDelay, boolean updateOomAdj) {
         if (conn == null) {
-            cpr.removeExternalProcessHandleLocked(externalProcessToken);
+            mService.mProcessStateController.removeExternalProviderClient(cpr,
+                    externalProcessToken);
             return false;
         }
 
@@ -1537,14 +1540,15 @@
             if (cpr.proc != null && !hasProviderConnectionLocked(cpr.proc)) {
                 cpr.proc.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
             }
-            conn.client.mProviders.removeProviderConnection(conn);
+            mService.mProcessStateController.removeProviderConnection(conn.client, conn);
             if (conn.client.mState.getSetProcState()
                     < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                 // The client is more important than last activity -- note the time this
                 // is happening, so we keep the old provider process around a bit as last
                 // activity to avoid thrashing it.
                 if (cpr.proc != null) {
-                    cpr.proc.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
+                    mService.mProcessStateController.setLastProviderTime(cpr.proc,
+                            SystemClock.uptimeMillis());
                 }
             }
             mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
@@ -1821,7 +1825,7 @@
                 }
             }
             if (removed && cpr.proc != null) {
-                cpr.proc.mProviders.removeProvider(cpr.info.name);
+                mService.mProcessStateController.removePublishedProvider(cpr.proc, cpr.info.name);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 2a30ad0..61079fc 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -54,8 +54,9 @@
 per-file *Permission* = patb@google.com
 per-file *Package* = patb@google.com
 
-# OOM Adjuster
+# OOM Adjuster & ProcessStateController
 per-file *Oom* = file:/OOM_ADJUSTER_OWNERS
+per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
 
 # Miscellaneous
 per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 4073ab8..7051714 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -130,6 +130,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal.OomAdjReason;
 import android.app.ActivityThread;
@@ -376,6 +377,7 @@
 
     final ActivityManagerService mService;
     final Injector mInjector;
+    final GlobalState mGlobalState;
     final ProcessList mProcessList;
     final ActivityManagerGlobalLock mProcLock;
 
@@ -470,15 +472,23 @@
 
     }
 
+    // TODO(b/346822474): hook up global state usage.
+    interface GlobalState {
+        /** Is device's screen on. */
+        boolean isAwake();
+
+        /** What process is running a backup for a given userId. */
+        ProcessRecord getBackupTarget(@UserIdInt int userId);
+
+        /** Is memory level normal since last evaluation. */
+        boolean isLastMemoryLevelNormal();
+    }
+
     boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
             ApplicationInfo app, boolean defaultValue) {
         return mInjector.isChangeEnabled(cachedCompatChangeId, app, defaultValue);
     }
 
-    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
-        this(service, processList, activeUids, createAdjusterThread());
-    }
-
     static ServiceThread createAdjusterThread() {
         // The process group is usually critical to the response time of foreground app, so the
         // setter should apply it as soon as possible.
@@ -489,18 +499,9 @@
     }
 
     OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
-            ServiceThread adjusterThread) {
-        this(service, processList, activeUids, adjusterThread, new Injector());
-    }
-
-    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
-            Injector injector) {
-        this(service, processList, activeUids, createAdjusterThread(), injector);
-    }
-
-    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
-            ServiceThread adjusterThread, Injector injector) {
+            ServiceThread adjusterThread, GlobalState globalState, Injector injector) {
         mService = service;
+        mGlobalState = globalState;
         mInjector = injector;
         mProcessList = processList;
         mProcLock = service.mProcLock;
@@ -1161,8 +1162,8 @@
                     if (opt != null && opt.isFreezeExempt()) {
                         // BIND_WAIVE_PRIORITY and the like get oom_adj 900
                         targetAdj += 0;
-                    } else if (state.hasShownUi() && uiTargetAdj < 15) {
-                        // The most recent 5 apps that have shown UI get 910-914
+                    } else if (state.hasShownUi() && uiTargetAdj < 20) {
+                        // The most recent 10 apps that have shown UI get 910-919
                         targetAdj += uiTargetAdj++;
                     } else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ)
                             && (state.getLastStateTime()
@@ -1816,9 +1817,36 @@
         }
     }
 
+    private boolean isDeviceFullyAwake() {
+        if (Flags.pushGlobalStateToOomadjuster()) {
+            return mGlobalState.isAwake();
+        } else {
+            return mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE;
+        }
+    }
+
     private boolean isScreenOnOrAnimatingLocked(ProcessStateRecord state) {
-        return mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE
-                || state.isRunningRemoteAnimation();
+        return isDeviceFullyAwake() || state.isRunningRemoteAnimation();
+    }
+
+    private boolean isBackupProcess(ProcessRecord app) {
+        if (Flags.pushGlobalStateToOomadjuster()) {
+            return app == mGlobalState.getBackupTarget(app.userId);
+        } else {
+            final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
+            if (backupTarget == null) {
+                return false;
+            }
+            return app == backupTarget.app;
+        }
+    }
+
+    private boolean isLastMemoryLevelNormal() {
+        if (Flags.pushGlobalStateToOomadjuster()) {
+            return mGlobalState.isLastMemoryLevelNormal();
+        } else {
+            return mService.mAppProfiler.isLastMemoryLevelNormal();
+        }
     }
 
     @GuardedBy({"mService", "mProcLock"})
@@ -2259,8 +2287,7 @@
         state.setHasStartedServices(false);
         state.setAdjSeq(mAdjSeq);
 
-        final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
-        if (backupTarget != null && app == backupTarget.app) {
+        if (isBackupProcess(app)) {
             // If possible we want to avoid killing apps while they're being backed up
             if (adj > BACKUP_APP_ADJ) {
                 if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
@@ -2526,8 +2553,7 @@
                     double cachedRestoreThreshold =
                             mProcessList.getCachedRestoreThresholdKb() * thresholdModifier;
 
-                    if (!mService.mAppProfiler.isLastMemoryLevelNormal()
-                            && lastPssOrRss >= cachedRestoreThreshold) {
+                    if (!isLastMemoryLevelNormal() && lastPssOrRss >= cachedRestoreThreshold) {
                         state.setServiceHighRam(true);
                         state.setServiceB(true);
                         //Slog.i(TAG, "ADJ " + app + " high ram!");
@@ -2621,7 +2647,7 @@
         // Put bound foreground services in a special sched group for additional
         // restrictions on screen off
         if (state.getCurProcState() >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
-                && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
+                && !isDeviceFullyAwake()
                 && !state.shouldScheduleLikeTopApp()) {
             if (schedGroup > SCHED_GROUP_RESTRICTED) {
                 schedGroup = SCHED_GROUP_RESTRICTED;
@@ -2910,8 +2936,7 @@
                         clientProcState = PROCESS_STATE_FOREGROUND_SERVICE;
                     } else if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
                         clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-                    } else if (mService.mWakefulness.get()
-                            == PowerManagerInternal.WAKEFULNESS_AWAKE
+                    } else if (isDeviceFullyAwake()
                             && cr.hasFlag(Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) {
                         clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                     } else {
@@ -3258,7 +3283,12 @@
                 baseCapabilities = PROCESS_CAPABILITY_ALL; // BFSL allowed
                 break;
             case PROCESS_STATE_BOUND_TOP:
-                baseCapabilities = PROCESS_CAPABILITY_BFSL;
+                if (app.getActiveInstrumentation() != null) {
+                    baseCapabilities = PROCESS_CAPABILITY_BFSL |
+                            PROCESS_CAPABILITY_ALL_IMPLICIT;
+                } else {
+                    baseCapabilities = PROCESS_CAPABILITY_BFSL;
+                }
                 break;
             case PROCESS_STATE_FOREGROUND_SERVICE:
                 if (app.getActiveInstrumentation() != null) {
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index fb1c2e9..e452c45 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -756,18 +756,9 @@
             new ComputeConnectionsConsumer();
 
     OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
-            ActiveUids activeUids) {
-        this(service, processList, activeUids, createAdjusterThread());
-    }
-
-    OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
-            ActiveUids activeUids, ServiceThread adjusterThread) {
-        super(service, processList, activeUids, adjusterThread);
-    }
-
-    OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
-            ActiveUids activeUids, Injector injector) {
-        super(service, processList, activeUids, injector);
+            ActiveUids activeUids, ServiceThread adjusterThread, GlobalState globalState,
+            Injector injector) {
+        super(service, processList, activeUids, adjusterThread, globalState, injector);
     }
 
     private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes(
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 6857b6b..3fb06a7 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -432,6 +432,14 @@
         }
     }
 
+    /**
+     * get package name of the PendingIntent sender.
+     * @return package name of the PendingIntent sender.
+     */
+    public String getPackageName() {
+        return key.packageName;
+    }
+
     @Deprecated
     public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 57922d5..cdb0188 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -813,10 +813,8 @@
     private final Object mProcessChangeLock = new Object();
 
     /**
-     * All of the applications we currently have running organized by name.
-     * The keys are strings of the application package name (as
-     * returned by the package manager), and the keys are ApplicationRecord
-     * objects.
+     * All of the processes that are running organized by name.
+     * The keys are process names and the values are the associated ProcessRecord objects.
      */
     @CompositeRWLock({"mService", "mProcLock"})
     private final MyProcessMap mProcessNames = new MyProcessMap();
@@ -3439,12 +3437,12 @@
             state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
             state.setSetSchedGroup(ProcessList.SCHED_GROUP_DEFAULT);
             r.setPersistent(true);
-            state.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
+            mService.mProcessStateController.setMaxAdj(r, ProcessList.PERSISTENT_PROC_ADJ);
         }
         if (isolated && isolatedUid != 0) {
             // Special case for startIsolatedProcess (internal only) - assume the process
             // is required by the system server to prevent it being killed.
-            state.setMaxAdj(ProcessList.PERSISTENT_SERVICE_ADJ);
+            mService.mProcessStateController.setMaxAdj(r, ProcessList.PERSISTENT_SERVICE_ADJ);
         }
         addProcessNameLocked(r);
         return r;
@@ -3629,42 +3627,68 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    private int updateLruProcessInternalLSP(ProcessRecord app, long now, int index,
-            int lruSeq, String what, Object obj, ProcessRecord srcApp) {
+    private int offerLruProcessInternalLSP(ProcessRecord app, long now, String what, Object obj,
+            ProcessRecord srcApp) {
         app.setLastActivityTime(now);
 
         if (app.hasActivitiesOrRecentTasks()) {
             // Don't want to touch dependent processes that are hosting activities.
-            return index;
+            return -1;
         }
 
-        int lrui = mLruProcesses.lastIndexOf(app);
+        final int lrui = mLruProcesses.lastIndexOf(app);
         if (lrui < 0) {
             Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
                     + what + " " + obj + " from " + srcApp);
-            return index;
         }
+        return lrui;
+    }
 
-        if (lrui >= index) {
-            // Don't want to cause this to move dependent processes *back* in the
-            // list as if they were less frequently used.
-            return index;
-        }
+    /**
+     * This method is called after the indices array is populated by the indices offered by
+     * {@link #offerLruProcessInternalLSP} to actually move the processes to the desired locations
+     * in the LRU list. Since the indices array is a SparseBooleanArray, the indices are sorted
+     * and this allows us to preserve the previous order of the processes relative to each other.
+     * Key of the indices array holds the current index of the process in the LRU list and the value
+     * is a boolean indicating whether the process is an activity process or not. Activity processes
+     * are moved to the nextActivityIndex and non-activity processes are moved to the nextIndex
+     * positions, which are provided by the caller.
+     *
+     * @param indices The indices of the processes to move.
+     * @param nextActivityIndex The next index to insert an activity process.
+     * @param nextIndex The next index to insert a non-activity process.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    private void completeLruProcessInternalLSP(SparseBooleanArray indices, int nextActivityIndex,
+            int nextIndex) {
+        for (int i = indices.size() - 1; i >= 0; i--) {
+            final int lrui = indices.keyAt(i);
+            if (lrui < 0) {
+                // Rest of the indices are invalid, we can return early.
+                return;
+            }
+            final boolean isActivity = indices.valueAt(i);
+            int index = isActivity ? nextActivityIndex : nextIndex;
 
-        if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
-            // Don't want to touch dependent processes that are hosting activities.
-            return index;
-        }
+            if (lrui >= index) {
+                // Don't want to cause this to move dependent processes *back* in the
+                // list as if they were less frequently used.
+                continue;
+            }
 
-        mLruProcesses.remove(lrui);
-        if (index > 0) {
+            final ProcessRecord app = mLruProcesses.remove(lrui);
             index--;
+            if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+                    + " in LRU list: " + app);
+            mLruProcesses.add(index, app);
+            app.setLruSeq(mLruSeq);
+
+            if (isActivity) {
+                nextActivityIndex = index;
+            } else {
+                nextIndex = index;
+            }
         }
-        if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
-                + " in LRU list: " + app);
-        mLruProcesses.add(index, app);
-        app.setLruSeq(lruSeq);
-        return index;
     }
 
     /**
@@ -4058,6 +4082,15 @@
 
         app.setLruSeq(mLruSeq);
 
+        // Key of the indices array holds the current index of the process in the LRU list and the
+        // value is a boolean indicating whether the process is an activity process or not.
+        // Activity processes will be moved to the nextActivityIndex and non-activity processes will
+        // be moved to the nextIndex positions when completeLruProcessInternalLSP is called.
+        // Since SparseBooleanArray's keys are sorted, we'll be able to keep the existing order of
+        // the processes relative to each other after the move.
+        final SparseBooleanArray indices = new SparseBooleanArray(psr.numberOfConnections()
+                + app.mProviders.numberOfProviderConnections());
+
         // If the app is currently using a content provider or service,
         // bump those processes as well.
         for (int j = psr.numberOfConnections() - 1; j >= 0; j--) {
@@ -4069,16 +4102,12 @@
                     && !cr.binding.service.app.isPersistent()) {
                 if (cr.binding.service.app.mServices.hasClientActivities()) {
                     if (nextActivityIndex >= 0) {
-                        nextActivityIndex = updateLruProcessInternalLSP(cr.binding.service.app,
-                                now,
-                                nextActivityIndex, mLruSeq,
-                                "service connection", cr, app);
+                        indices.append(offerLruProcessInternalLSP(cr.binding.service.app, now,
+                                "service connection", cr, app), true);
                     }
                 } else {
-                    nextIndex = updateLruProcessInternalLSP(cr.binding.service.app,
-                            now,
-                            nextIndex, mLruSeq,
-                            "service connection", cr, app);
+                    indices.append(offerLruProcessInternalLSP(cr.binding.service.app, now,
+                            "service connection", cr, app), false);
                 }
             }
         }
@@ -4086,10 +4115,11 @@
         for (int j = ppr.numberOfProviderConnections() - 1; j >= 0; j--) {
             ContentProviderRecord cpr = ppr.getProviderConnectionAt(j).provider;
             if (cpr.proc != null && cpr.proc.getLruSeq() != mLruSeq && !cpr.proc.isPersistent()) {
-                nextIndex = updateLruProcessInternalLSP(cpr.proc, now, nextIndex, mLruSeq,
-                        "provider reference", cpr, app);
+                indices.append(offerLruProcessInternalLSP(cpr.proc, now,
+                        "provider reference", cpr, app), false);
             }
         }
+        completeLruProcessInternalLSP(indices, nextActivityIndex, nextIndex);
     }
 
     @GuardedBy(anyOf = {"mService", "mProcLock"})
@@ -5157,6 +5187,7 @@
                         if (ai != null) {
                             if (ai.packageName.equals(app.info.packageName)) {
                                 app.info = ai;
+                                app.getWindowProcessController().updateApplicationInfo(ai);
                                 PlatformCompatCache.getInstance()
                                         .onApplicationInfoChanged(ai);
                             }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 3e71d00..b51db13 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UI_VISIBILITY;
 
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -1192,7 +1193,7 @@
         setWaitingToKill(null);
 
         mState.onCleanupApplicationRecordLSP();
-        mServices.onCleanupApplicationRecordLocked();
+        mService.mProcessStateController.onCleanupApplicationRecord(mServices);
         mReceivers.onCleanupApplicationRecordLocked();
         mService.mOomAdjuster.onProcessEndLocked(this);
 
@@ -1638,7 +1639,7 @@
             updateProcessInfo(false /* updateServiceConnectionActivities */,
                     true /* activityChange */, true /* updateOomAdj */);
             setPendingUiClean(true);
-            mState.setHasShownUi(true);
+            mService.mProcessStateController.setHasShownUi(this, true);
             mState.forceProcessStateUpTo(topProcessState);
         }
     }
@@ -1657,7 +1658,10 @@
             return;
         }
         synchronized (mService) {
-            mState.setRunningRemoteAnimation(runningRemoteAnimation);
+            if (mService.mProcessStateController.setRunningRemoteAnimation(this,
+                    runningRemoteAnimation)) {
+                mService.mProcessStateController.runUpdate(this, OOM_ADJ_REASON_UI_VISIBILITY);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessStateController.java b/services/core/java/com/android/server/am/ProcessStateController.java
new file mode 100644
index 0000000..01468c6
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessStateController.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.PowerManagerInternal;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.ServiceThread;
+
+/**
+ * ProcessStateController is responsible for maintaining state that can affect the OomAdjuster
+ * computations of a process. Any state that can affect a process's importance must be set by
+ * only ProcessStateController.
+ */
+public class ProcessStateController {
+    public static String TAG = "ProcessStateController";
+
+    private final OomAdjuster mOomAdjuster;
+
+    private final GlobalState mGlobalState = new GlobalState();
+
+    private ProcessStateController(ActivityManagerService ams, ProcessList processList,
+            ActiveUids activeUids, ServiceThread handlerThread, OomAdjuster.Injector oomAdjInjector,
+            boolean useOomAdjusterModernImpl) {
+        mOomAdjuster = useOomAdjusterModernImpl
+                ? new OomAdjusterModernImpl(ams, processList, activeUids, handlerThread,
+                mGlobalState, oomAdjInjector)
+                : new OomAdjuster(ams, processList, activeUids, handlerThread, mGlobalState,
+                        oomAdjInjector);
+    }
+
+    /**
+     * Get the instance of OomAdjuster that ProcessStateController is using.
+     * Must only be interacted with while holding the ActivityManagerService lock.
+     */
+    public OomAdjuster getOomAdjuster() {
+        return mOomAdjuster;
+    }
+
+    /**
+     * Add a process to evaluated the next time an update is run.
+     */
+    public void enqueueUpdateTarget(@NonNull ProcessRecord proc) {
+        mOomAdjuster.enqueueOomAdjTargetLocked(proc);
+    }
+
+    /**
+     * Remove a process that was added by {@link #enqueueUpdateTarget}.
+     */
+    public void removeUpdateTarget(@NonNull ProcessRecord proc, boolean procDied) {
+        mOomAdjuster.removeOomAdjTargetLocked(proc, procDied);
+    }
+
+    /**
+     * Trigger an update on a single process (and any processes that have been enqueued with
+     * {@link #enqueueUpdateTarget}).
+     */
+    public boolean runUpdate(@NonNull ProcessRecord proc,
+            @ActivityManagerInternal.OomAdjReason int oomAdjReason) {
+        return mOomAdjuster.updateOomAdjLocked(proc, oomAdjReason);
+    }
+
+    /**
+     * Trigger an update on all processes that have been enqueued with {@link #enqueueUpdateTarget}.
+     */
+    public void runPendingUpdate(@ActivityManagerInternal.OomAdjReason int oomAdjReason) {
+        mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason);
+    }
+
+    /**
+     * Trigger an update on all processes.
+     */
+    public void runFullUpdate(@ActivityManagerInternal.OomAdjReason int oomAdjReason) {
+        mOomAdjuster.updateOomAdjLocked(oomAdjReason);
+    }
+
+    /**
+     * Trigger an update on any processes that have been marked for follow up during a previous
+     * update.
+     */
+    public void runFollowUpUpdate() {
+        mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+    }
+
+    private static class GlobalState implements OomAdjuster.GlobalState {
+        public boolean isAwake = true;
+        // TODO(b/369300367): Maintaining global state for backup processes is a bit convoluted.
+        //  ideally the state gets migrated to ProcessStateRecord.
+        public final SparseArray<ProcessRecord> backupTargets = new SparseArray<>();
+        public boolean isLastMemoryLevelNormal = true;
+
+        public boolean isAwake() {
+            return isAwake;
+        }
+
+        public ProcessRecord getBackupTarget(@UserIdInt int userId) {
+            return backupTargets.get(userId);
+        }
+
+        public boolean isLastMemoryLevelNormal() {
+            return isLastMemoryLevelNormal;
+        }
+    }
+
+    /*************************** Global State Events ***************************/
+    /**
+     * Set which process state Top processes should get.
+     */
+    public void setTopProcessState(@ActivityManager.ProcessState int procState) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set whether to give Top processes the Top sched group.
+     */
+    public void setUseTopSchedGroupForTopProcess(boolean useTopSchedGroup) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set the Top process.
+     */
+    public void setTopApp(@Nullable ProcessRecord proc) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set which process is considered the Home process, if any.
+     */
+    public void setHomeProcess(@Nullable ProcessRecord proc) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set which process is considered the Heavy Weight process, if any.
+     */
+    public void setHeavyWeightProcess(@Nullable ProcessRecord proc) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set which process is showing UI while the screen is off, if any.
+     */
+    public void setVisibleDozeUiProcess(@Nullable ProcessRecord proc) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set which process is considered the Previous process, if any.
+     */
+    public void setPreviousProcess(@Nullable ProcessRecord proc) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set what wakefulness state the screen is in.
+     */
+    public void setWakefulness(int wakefulness) {
+        mGlobalState.isAwake = (wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mOomAdjuster.onWakefulnessChanged(wakefulness);
+    }
+
+    /**
+     * Set for a given user what process is currently running a backup, if any.
+     */
+    public void setBackupTarget(@NonNull ProcessRecord proc, @UserIdInt int userId) {
+        mGlobalState.backupTargets.put(userId, proc);
+    }
+
+    /**
+     * No longer consider any process running a backup for a given user.
+     */
+    public void stopBackupTarget(@UserIdInt int userId) {
+        mGlobalState.backupTargets.delete(userId);
+    }
+
+    /**
+     * Set whether the last known memory level is normal.
+     */
+    public void setIsLastMemoryLevelNormal(boolean isMemoryNormal) {
+        mGlobalState.isLastMemoryLevelNormal = isMemoryNormal;
+    }
+
+    /***************************** UID State Events ****************************/
+    /**
+     * Set a UID as temp allowlisted.
+     */
+    public void setUidTempAllowlistStateLSP(int uid, boolean allowList) {
+        mOomAdjuster.setUidTempAllowlistStateLSP(uid, allowList);
+    }
+
+    /*********************** Process Miscellaneous Events **********************/
+    /**
+     * Set the maximum adj score a process can be assigned.
+     */
+    public void setMaxAdj(@NonNull ProcessRecord proc, int adj) {
+        proc.mState.setMaxAdj(adj);
+    }
+
+    /**
+     * Initialize a process that is being attached.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    public void setAttachingProcessStatesLSP(@NonNull ProcessRecord proc) {
+        mOomAdjuster.setAttachingProcessStatesLSP(proc);
+    }
+
+    /**
+     * Note whether a process is pending attach or not.
+     */
+    public void setPendingFinishAttach(@NonNull ProcessRecord proc, boolean pendingFinishAttach) {
+        proc.setPendingFinishAttach(pendingFinishAttach);
+    }
+
+    /**
+     * Set what sched group to grant a process due to running a broadcast.
+     * {@link ProcessList.SCHED_GROUP_UNDEFINED} means the process is not running a broadcast.
+     */
+    public void setBroadcastSchedGroup(@NonNull ProcessRecord proc, int schedGroup) {
+        // TODO(b/302575389): Migrate state pulled from BroadcastQueue to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /********************* Process Visibility State Events *********************/
+    /**
+     * Note whether a process has Top UI or not.
+     *
+     * @return true if the state changed, otherwise returns false.
+     */
+    public boolean setHasTopUi(@NonNull ProcessRecord proc, boolean hasTopUi) {
+        if (proc.mState.hasTopUi() == hasTopUi) return false;
+        if (DEBUG_OOM_ADJ) {
+            Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + proc.getPid());
+        }
+        proc.mState.setHasTopUi(hasTopUi);
+        return true;
+    }
+
+    /**
+     * Note whether a process is displaying Overlay UI or not.
+     *
+     * @return true if the state changed, otherwise returns false.
+     */
+    public boolean setHasOverlayUi(@NonNull ProcessRecord proc, boolean hasOverlayUi) {
+        if (proc.mState.hasOverlayUi() == hasOverlayUi) return false;
+        proc.mState.setHasOverlayUi(hasOverlayUi);
+        return true;
+    }
+
+
+    /**
+     * Note whether a process is running a remote animation.
+     *
+     * @return true if the state changed, otherwise returns false.
+     */
+    public boolean setRunningRemoteAnimation(@NonNull ProcessRecord proc,
+            boolean runningRemoteAnimation) {
+        if (proc.mState.isRunningRemoteAnimation() == runningRemoteAnimation) return false;
+        if (DEBUG_OOM_ADJ) {
+            Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
+                    + " for pid=" + proc.getPid());
+        }
+        proc.mState.setRunningRemoteAnimation(runningRemoteAnimation);
+        return true;
+    }
+
+    /**
+     * Note that the process is showing a toast.
+     */
+    public void setForcingToImportant(@NonNull ProcessRecord proc,
+            @Nullable Object forcingToImportant) {
+        if (proc.mState.getForcingToImportant() == forcingToImportant) return;
+        proc.mState.setForcingToImportant(forcingToImportant);
+    }
+
+    /**
+     * Note that the process has shown UI at some point in its life.
+     */
+    public void setHasShownUi(@NonNull ProcessRecord proc, boolean hasShownUi) {
+        // This arguably should be turned into an internal state of OomAdjuster.
+        if (proc.mState.hasShownUi() == hasShownUi) return;
+        proc.mState.setHasShownUi(hasShownUi);
+    }
+
+    /**
+     * Note whether the process has an activity or not.
+     */
+    public void setHasActivity(@NonNull ProcessRecord proc, boolean hasActivity) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        // Possibly not needed, maybe can use ActivityStateFlags.
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Note whether the process has a visibly activity or not.
+     */
+    public void setHasVisibleActivity(@NonNull ProcessRecord proc, boolean hasVisibleActivity) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        // maybe used ActivityStateFlags instead.
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Set the Activity State Flags for a process.
+     */
+    public void setActivityStateFlags(@NonNull ProcessRecord proc, int flags) {
+        // TODO(b/302575389): Migrate state pulled from ATMS to a pushed model
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /********************** Content Provider State Events **********************/
+    /**
+     * Note that a process is hosting a content provider.
+     */
+    public boolean addPublishedProvider(@NonNull ProcessRecord proc, String name,
+            ContentProviderRecord cpr) {
+        final ProcessProviderRecord providers = proc.mProviders;
+        if (providers.hasProvider(name)) return false;
+        providers.installProvider(name, cpr);
+        return true;
+    }
+
+    /**
+     * Remove a published content provider from a process.
+     */
+    public void removePublishedProvider(@NonNull ProcessRecord proc, String name) {
+        final ProcessProviderRecord providers = proc.mProviders;
+        providers.removeProvider(name);
+    }
+
+    /**
+     * Note that a content provider has an external client.
+     */
+    public void addExternalProviderClient(@NonNull ContentProviderRecord cpr,
+            IBinder externalProcessToken, int callingUid, String callingTag) {
+        cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
+    }
+
+    /**
+     * Remove an external client from a conetnt provider.
+     */
+    public boolean removeExternalProviderClient(@NonNull ContentProviderRecord cpr,
+            IBinder externalProcessToken) {
+        return cpr.removeExternalProcessHandleLocked(externalProcessToken);
+    }
+
+    /**
+     * Note the time a process is no longer hosting any content providers.
+     */
+    public void setLastProviderTime(@NonNull ProcessRecord proc, long uptimeMs) {
+        proc.mProviders.setLastProviderTime(uptimeMs);
+    }
+
+    /**
+     * Note that a process has connected to a content provider.
+     */
+    public void addProviderConnection(@NonNull ProcessRecord client,
+            ContentProviderConnection cpc) {
+        client.mProviders.addProviderConnection(cpc);
+    }
+
+    /**
+     * Note that a process is no longer connected to a content provider.
+     */
+    public void removeProviderConnection(@NonNull ProcessRecord client,
+            ContentProviderConnection cpc) {
+        client.mProviders.removeProviderConnection(cpc);
+    }
+
+    /*************************** Service State Events **************************/
+    /**
+     * Note that a process has started hosting a service.
+     */
+    public boolean startService(@NonNull ProcessServiceRecord psr, ServiceRecord sr) {
+        return psr.startService(sr);
+    }
+
+    /**
+     * Note that a process has stopped hosting a service.
+     */
+    public boolean stopService(@NonNull ProcessServiceRecord psr, ServiceRecord sr) {
+        return psr.stopService(sr);
+    }
+
+    /**
+     * Remove all services that the process is hosting.
+     */
+    public void stopAllServices(@NonNull ProcessServiceRecord psr) {
+        psr.stopAllServices();
+    }
+
+    /**
+     * Note that a process's service has started executing.
+     */
+    public void startExecutingService(@NonNull ProcessServiceRecord psr, ServiceRecord sr) {
+        psr.startExecutingService(sr);
+    }
+
+    /**
+     * Note that a process's service has stopped executing.
+     */
+    public void stopExecutingService(@NonNull ProcessServiceRecord psr, ServiceRecord sr) {
+        psr.stopExecutingService(sr);
+    }
+
+    /**
+     * Note all executing services a process has has stopped.
+     */
+    public void stopAllExecutingServices(@NonNull ProcessServiceRecord psr) {
+        psr.stopAllExecutingServices();
+    }
+
+    /**
+     * Note that process has bound to a service.
+     */
+    public void addConnection(@NonNull ProcessServiceRecord psr, ConnectionRecord cr) {
+        psr.addConnection(cr);
+    }
+
+    /**
+     * Note that process has unbound from a service.
+     */
+    public void removeConnection(@NonNull ProcessServiceRecord psr, ConnectionRecord cr) {
+        psr.removeConnection(cr);
+    }
+
+    /**
+     * Remove all bindings a process has to services.
+     */
+    public void removeAllConnections(@NonNull ProcessServiceRecord psr) {
+        psr.removeAllConnections();
+        psr.removeAllSdkSandboxConnections();
+    }
+
+    /**
+     * Note whether an executing service should be considered in the foreground or not.
+     */
+    public void setExecServicesFg(@NonNull ProcessServiceRecord psr, boolean execServicesFg) {
+        psr.setExecServicesFg(execServicesFg);
+    }
+
+    /**
+     * Note whether a service is in the foreground or not and what type of FGS, if so.
+     */
+    public void setHasForegroundServices(@NonNull ProcessServiceRecord psr,
+            boolean hasForegroundServices,
+            int fgServiceTypes, boolean hasTypeNoneFgs) {
+        psr.setHasForegroundServices(hasForegroundServices, fgServiceTypes, hasTypeNoneFgs);
+    }
+
+    /**
+     * Note whether a service has a client activity or not.
+     */
+    public void setHasClientActivities(@NonNull ProcessServiceRecord psr,
+            boolean hasClientActivities) {
+        psr.setHasClientActivities(hasClientActivities);
+    }
+
+    /**
+     * Note whether a service should be treated like an activity or not.
+     */
+    public void setTreatLikeActivity(@NonNull ProcessServiceRecord psr, boolean treatLikeActivity) {
+        psr.setTreatLikeActivity(treatLikeActivity);
+    }
+
+    /**
+     * Note whether a process has bound to a service with
+     * {@link android.content.Context.BIND_ABOVE_CLIENT} or not.
+     */
+    public void setHasAboveClient(@NonNull ProcessServiceRecord psr, boolean hasAboveClient) {
+        psr.setHasAboveClient(hasAboveClient);
+    }
+
+    /**
+     * Recompute whether a process has bound to a service with
+     * {@link android.content.Context.BIND_ABOVE_CLIENT} or not.
+     */
+    public void updateHasAboveClientLocked(@NonNull ProcessServiceRecord psr) {
+        psr.updateHasAboveClientLocked();
+    }
+
+    /**
+     * Cleanup a process's state.
+     */
+    public void onCleanupApplicationRecord(@NonNull ProcessServiceRecord psr) {
+        psr.onCleanupApplicationRecordLocked();
+    }
+
+    /**
+     * Set which process is hosting a service.
+     */
+    public void setHostProcess(@NonNull ServiceRecord sr, @Nullable ProcessRecord host) {
+        sr.app = host;
+    }
+
+    /**
+     * Note whether a service is a Foreground Service or not
+     */
+    public void setIsForegroundService(@NonNull ServiceRecord sr, boolean isFgs) {
+        sr.isForeground = isFgs;
+    }
+
+    /**
+     * Note the Foreground Service type of a service.
+     */
+    public void setForegroundServiceType(@NonNull ServiceRecord sr,
+            @ServiceInfo.ForegroundServiceType int fgsType) {
+        sr.foregroundServiceType = fgsType;
+    }
+
+    /**
+     * Note the start time of a short foreground service.
+     */
+    public void setShortFgsInfo(@NonNull ServiceRecord sr, long uptimeNow) {
+        sr.setShortFgsInfo(uptimeNow);
+    }
+
+    /**
+     * Note that a short foreground service has stopped.
+     */
+    public void clearShortFgsInfo(@NonNull ServiceRecord sr) {
+        sr.clearShortFgsInfo();
+    }
+
+    /**
+     * Note the last time a service was active.
+     */
+    public void setServiceLastActivityTime(@NonNull ServiceRecord sr, long lastActivityUpdateMs) {
+        sr.lastActivity = lastActivityUpdateMs;
+    }
+
+    /**
+     * Note that a service start was requested.
+     */
+    public void setStartRequested(@NonNull ServiceRecord sr, boolean startRequested) {
+        sr.startRequested = startRequested;
+    }
+
+    /**
+     * Note the last time the service was bound by a Top process with
+     * {@link android.content.Context.BIND_ALMOST_PERCEPTIBLE}
+     */
+    public void setLastTopAlmostPerceptibleBindRequest(@NonNull ServiceRecord sr,
+            long lastTopAlmostPerceptibleBindRequestUptimeMs) {
+        sr.lastTopAlmostPerceptibleBindRequestUptimeMs =
+                lastTopAlmostPerceptibleBindRequestUptimeMs;
+    }
+
+    /**
+     * Recompute whether a process has bound to a service with
+     * {@link android.content.Context.BIND_ALMOST_PERCEPTIBLE} or not.
+     */
+    public void updateHasTopStartedAlmostPerceptibleServices(@NonNull ProcessServiceRecord psr) {
+        psr.updateHasTopStartedAlmostPerceptibleServices();
+    }
+
+    /**
+     * Builder for ProcessStateController.
+     */
+    public static class Builder {
+        private final ActivityManagerService mAms;
+        private final ProcessList mProcessList;
+        private final ActiveUids mActiveUids;
+
+        private ServiceThread mHandlerThread = null;
+        private OomAdjuster.Injector mOomAdjInjector = null;
+        private boolean mUseOomAdjusterModernImpl = false;
+
+        public Builder(ActivityManagerService ams, ProcessList processList, ActiveUids activeUids) {
+            mAms = ams;
+            mProcessList = processList;
+            mActiveUids = activeUids;
+        }
+
+        /**
+         * Build the ProcessStateController object.
+         */
+        public ProcessStateController build() {
+            if (mHandlerThread == null) {
+                mHandlerThread = OomAdjuster.createAdjusterThread();
+            }
+            if (mOomAdjInjector == null) {
+                mOomAdjInjector = new OomAdjuster.Injector();
+            }
+            return new ProcessStateController(mAms, mProcessList, mActiveUids, mHandlerThread,
+                    mOomAdjInjector, mUseOomAdjusterModernImpl);
+        }
+
+        /**
+         * For Testing Purposes. Set what thread OomAdjuster will offload tasks on to.
+         */
+        public Builder setHandlerThread(ServiceThread handlerThread) {
+            mHandlerThread = handlerThread;
+            return this;
+        }
+
+        /**
+         * For Testing Purposes. Set an injector for OomAdjuster.
+         */
+        public Builder setOomAdjusterInjector(OomAdjuster.Injector injector) {
+            mOomAdjInjector = injector;
+            return this;
+        }
+
+        /**
+         * Set which implementation of OomAdjuster to use.
+         */
+        public Builder useModernOomAdjuster(boolean use) {
+            mUseOomAdjusterModernImpl = use;
+            return this;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index bc990d9..b0f808b 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -19,14 +19,11 @@
 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UI_VISIBILITY;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_ACTIVITY;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BROADCAST_RECEIVER;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_STARTED_SERVICE;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
 import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
-import static com.android.server.am.ProcessRecord.TAG;
 import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
 import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
 import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING;
@@ -38,7 +35,6 @@
 import android.content.ComponentName;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.util.Slog;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.CompositeRWLock;
@@ -790,15 +786,7 @@
 
     @GuardedBy("mService")
     void setRunningRemoteAnimation(boolean runningRemoteAnimation) {
-        if (mRunningRemoteAnimation == runningRemoteAnimation) {
-            return;
-        }
         mRunningRemoteAnimation = runningRemoteAnimation;
-        if (DEBUG_OOM_ADJ) {
-            Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
-                    + " for pid=" + mApp.getPid());
-        }
-        mService.updateOomAdjLocked(mApp, OOM_ADJ_REASON_UI_VISIBILITY);
     }
 
     @GuardedBy({"mService", "mProcLock"})
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index b9cdf27..92d33c9 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1248,7 +1248,7 @@
             app.mServices.updateBoundClientUids();
             app.mServices.updateHostingComonentTypeForBindingsLocked();
         }
-        app = proc;
+        ams.mProcessStateController.setHostProcess(this, proc);
         updateProcessStateOnRequest();
         if (pendingConnectionGroup > 0 && proc != null) {
             final ProcessServiceRecord psr = proc.mServices;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a815f72..2f4e8bb 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -150,6 +150,7 @@
         "art_mainline",
         "art_performance",
         "attack_tools",
+        "automotive_cast",
         "avic",
         "desktop_firmware",
         "biometrics",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 262c76e..31ae966 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1920,175 +1920,8 @@
                 return false;
             }
 
-            boolean needStart = false;
-            boolean updateUmState = false;
-            UserState uss;
-
-            // If the user we are switching to is not currently started, then
-            // we need to start it now.
-            t.traceBegin("updateStartedUserArrayStarting");
-            synchronized (mLock) {
-                uss = mStartedUsers.get(userId);
-                if (uss == null) {
-                    uss = new UserState(UserHandle.of(userId));
-                    uss.mUnlockProgress.addListener(new UserProgressListener());
-                    mStartedUsers.put(userId, uss);
-                    updateStartedUserArrayLU();
-                    needStart = true;
-                    updateUmState = true;
-                } else if (uss.state == UserState.STATE_SHUTDOWN
-                        || mDoNotAbortShutdownUserIds.contains(userId)) {
-                    Slogf.i(TAG, "User #" + userId
-                            + " is shutting down - will start after full shutdown");
-                    mPendingUserStarts.add(new PendingUserStart(userId, userStartMode,
-                            unlockListener));
-                    t.traceEnd(); // updateStartedUserArrayStarting
-                    return true;
-                }
-            }
-
-            // No matter what, the fact that we're requested to start the user (even if it is
-            // already running) puts it towards the end of the mUserLru list.
-            addUserToUserLru(userId);
-            if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
-                mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG,
-                        Integer.valueOf(userId));
-            }
-
-            if (unlockListener != null) {
-                uss.mUnlockProgress.addListener(unlockListener);
-            }
-            t.traceEnd(); // updateStartedUserArrayStarting
-
-            if (updateUmState) {
-                t.traceBegin("setUserState");
-                mInjector.getUserManagerInternal().setUserState(userId, uss.state);
-                t.traceEnd();
-            }
-            t.traceBegin("updateConfigurationAndProfileIds");
-            if (foreground) {
-                // Make sure the old user is no longer considering the display to be on.
-                mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
-                boolean userSwitchUiEnabled;
-                synchronized (mLock) {
-                    mCurrentUserId = userId;
-                    ActivityManager.invalidateGetCurrentUserIdCache();
-                    userSwitchUiEnabled = mUserSwitchUiEnabled;
-                }
-                mInjector.updateUserConfiguration();
-                // NOTE: updateProfileRelatedCaches() is called on both if and else parts, ideally
-                // it should be moved outside, but for now it's not as there are many calls to
-                // external components here afterwards
-                updateProfileRelatedCaches();
-                dispatchOnBeforeUserSwitching(userId);
-                mInjector.getWindowManager().setCurrentUser(userId);
-                mInjector.reportCurWakefulnessUsageEvent();
-                // Once the internal notion of the active user has switched, we lock the device
-                // with the option to show the user switcher on the keyguard.
-                if (userSwitchUiEnabled) {
-                    mInjector.getWindowManager().setSwitchingUser(true);
-                    // Only lock if the user has a secure keyguard PIN/Pattern/Pwd
-                    if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
-                        // Make sure the device is locked before moving on with the user switch
-                        mInjector.lockDeviceNowAndWaitForKeyguardShown();
-                    }
-                }
-
-            } else {
-                updateProfileRelatedCaches();
-                // We are starting a non-foreground user. They have already been added to the end
-                // of mUserLru, so we need to ensure that the foreground user isn't displaced.
-                addUserToUserLru(mCurrentUserId);
-            }
-            if (userStartMode == USER_START_MODE_BACKGROUND && !userInfo.isProfile()) {
-                scheduleStopOfBackgroundUser(userId);
-            }
-            t.traceEnd();
-
-            // Make sure user is in the started state.  If it is currently
-            // stopping, we need to knock that off.
-            if (uss.state == UserState.STATE_STOPPING) {
-                t.traceBegin("updateStateStopping");
-                // If we are stopping, we haven't sent ACTION_SHUTDOWN,
-                // so we can just fairly silently bring the user back from
-                // the almost-dead.
-                uss.setState(uss.lastState);
-                mInjector.getUserManagerInternal().setUserState(userId, uss.state);
-                synchronized (mLock) {
-                    updateStartedUserArrayLU();
-                }
-                needStart = true;
-                t.traceEnd();
-            } else if (uss.state == UserState.STATE_SHUTDOWN) {
-                t.traceBegin("updateStateShutdown");
-                // This means ACTION_SHUTDOWN has been sent, so we will
-                // need to treat this as a new boot of the user.
-                uss.setState(UserState.STATE_BOOTING);
-                mInjector.getUserManagerInternal().setUserState(userId, uss.state);
-                synchronized (mLock) {
-                    updateStartedUserArrayLU();
-                }
-                needStart = true;
-                t.traceEnd();
-            }
-
-            if (uss.state == UserState.STATE_BOOTING) {
-                t.traceBegin("updateStateBooting");
-                // Give user manager a chance to propagate user restrictions
-                // to other services and prepare app storage
-                mInjector.getUserManager().onBeforeStartUser(userId);
-
-                // Booting up a new user, need to tell system services about it.
-                // Note that this is on the same handler as scheduling of broadcasts,
-                // which is important because it needs to go first.
-                mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, NO_ARG2));
-                t.traceEnd();
-            }
-
-            t.traceBegin("sendMessages");
-            if (foreground) {
-                mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
-                mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
-                mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
-                mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
-                        oldUserId, userId, uss));
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
-                        oldUserId, userId, uss), getUserSwitchTimeoutMs());
-            }
-
-            if (userInfo.preCreated) {
-                needStart = false;
-            }
-
-            // In most cases, broadcast for the system user starting/started is sent by
-            // ActivityManagerService#systemReady(). However on some HSUM devices (e.g. tablets)
-            // the user switches from the system user to a secondary user while running
-            // ActivityManagerService#systemReady(), thus broadcast is not sent for the system user.
-            // Therefore we send the broadcast for the system user here as well in HSUM.
-            // TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user
-            // in HSUM. Ideally it'd be best to have one single place that sends this notification.
-            final boolean isSystemUserInHeadlessMode = (userId == UserHandle.USER_SYSTEM)
-                    && mInjector.isHeadlessSystemUserMode();
-            if (needStart || isSystemUserInHeadlessMode) {
-                sendUserStartedBroadcast(userId, callingUid, callingPid);
-            }
-            t.traceEnd();
-
-            if (foreground) {
-                t.traceBegin("moveUserToForeground");
-                moveUserToForeground(uss, userId);
-                t.traceEnd();
-            } else {
-                t.traceBegin("finishUserBoot");
-                finishUserBoot(uss);
-                t.traceEnd();
-            }
-
-            if (needStart || isSystemUserInHeadlessMode) {
-                t.traceBegin("sendRestartBroadcast");
-                sendUserStartingBroadcast(userId, callingUid, callingPid);
-                t.traceEnd();
-            }
+            mHandler.post(() -> startUserInternalOnHandler(userId, oldUserId, userStartMode,
+                    unlockListener, callingUid, callingPid));
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -2096,6 +1929,183 @@
         return true;
     }
 
+    private void startUserInternalOnHandler(int userId, int oldUserId, int userStartMode,
+            IProgressListener unlockListener, int callingUid, int callingPid) {
+        final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        final boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
+        final UserInfo userInfo = getUserInfo(userId);
+
+        boolean needStart = false;
+        boolean updateUmState = false;
+        UserState uss;
+
+        // If the user we are switching to is not currently started, then
+        // we need to start it now.
+        t.traceBegin("updateStartedUserArrayStarting");
+        synchronized (mLock) {
+            uss = mStartedUsers.get(userId);
+            if (uss == null) {
+                uss = new UserState(UserHandle.of(userId));
+                uss.mUnlockProgress.addListener(new UserProgressListener());
+                mStartedUsers.put(userId, uss);
+                updateStartedUserArrayLU();
+                needStart = true;
+                updateUmState = true;
+            } else if (uss.state == UserState.STATE_SHUTDOWN
+                    || mDoNotAbortShutdownUserIds.contains(userId)) {
+                Slogf.i(TAG, "User #" + userId
+                        + " is shutting down - will start after full shutdown");
+                mPendingUserStarts.add(new PendingUserStart(userId, userStartMode,
+                        unlockListener));
+                t.traceEnd(); // updateStartedUserArrayStarting
+                return;
+            }
+        }
+
+        // No matter what, the fact that we're requested to start the user (even if it is
+        // already running) puts it towards the end of the mUserLru list.
+        addUserToUserLru(userId);
+        if (android.multiuser.Flags.scheduleStopOfBackgroundUser()) {
+            mHandler.removeEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG,
+                    Integer.valueOf(userId));
+        }
+
+        if (unlockListener != null) {
+            uss.mUnlockProgress.addListener(unlockListener);
+        }
+        t.traceEnd(); // updateStartedUserArrayStarting
+
+        if (updateUmState) {
+            t.traceBegin("setUserState");
+            mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+            t.traceEnd();
+        }
+        t.traceBegin("updateConfigurationAndProfileIds");
+        if (foreground) {
+            // Make sure the old user is no longer considering the display to be on.
+            mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
+            boolean userSwitchUiEnabled;
+            synchronized (mLock) {
+                mCurrentUserId = userId;
+                ActivityManager.invalidateGetCurrentUserIdCache();
+                userSwitchUiEnabled = mUserSwitchUiEnabled;
+            }
+            mInjector.updateUserConfiguration();
+            // NOTE: updateProfileRelatedCaches() is called on both if and else parts, ideally
+            // it should be moved outside, but for now it's not as there are many calls to
+            // external components here afterwards
+            updateProfileRelatedCaches();
+            dispatchOnBeforeUserSwitching(userId);
+            mInjector.getWindowManager().setCurrentUser(userId);
+            mInjector.reportCurWakefulnessUsageEvent();
+            // Once the internal notion of the active user has switched, we lock the device
+            // with the option to show the user switcher on the keyguard.
+            if (userSwitchUiEnabled) {
+                mInjector.getWindowManager().setSwitchingUser(true);
+                // Only lock if the user has a secure keyguard PIN/Pattern/Pwd
+                if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
+                    // Make sure the device is locked before moving on with the user switch
+                    mInjector.lockDeviceNowAndWaitForKeyguardShown();
+                }
+            }
+
+        } else {
+            updateProfileRelatedCaches();
+            // We are starting a non-foreground user. They have already been added to the end
+            // of mUserLru, so we need to ensure that the foreground user isn't displaced.
+            addUserToUserLru(mCurrentUserId);
+        }
+        if (userStartMode == USER_START_MODE_BACKGROUND && !userInfo.isProfile()) {
+            scheduleStopOfBackgroundUser(userId);
+        }
+        t.traceEnd();
+
+        // Make sure user is in the started state.  If it is currently
+        // stopping, we need to knock that off.
+        if (uss.state == UserState.STATE_STOPPING) {
+            t.traceBegin("updateStateStopping");
+            // If we are stopping, we haven't sent ACTION_SHUTDOWN,
+            // so we can just fairly silently bring the user back from
+            // the almost-dead.
+            uss.setState(uss.lastState);
+            mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+            synchronized (mLock) {
+                updateStartedUserArrayLU();
+            }
+            needStart = true;
+            t.traceEnd();
+        } else if (uss.state == UserState.STATE_SHUTDOWN) {
+            t.traceBegin("updateStateShutdown");
+            // This means ACTION_SHUTDOWN has been sent, so we will
+            // need to treat this as a new boot of the user.
+            uss.setState(UserState.STATE_BOOTING);
+            mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+            synchronized (mLock) {
+                updateStartedUserArrayLU();
+            }
+            needStart = true;
+            t.traceEnd();
+        }
+
+        if (uss.state == UserState.STATE_BOOTING) {
+            t.traceBegin("updateStateBooting");
+            // Give user manager a chance to propagate user restrictions
+            // to other services and prepare app storage
+            mInjector.getUserManager().onBeforeStartUser(userId);
+
+            // Booting up a new user, need to tell system services about it.
+            // Note that this is on the same handler as scheduling of broadcasts,
+            // which is important because it needs to go first.
+            mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, NO_ARG2));
+            t.traceEnd();
+        }
+
+        t.traceBegin("sendMessages");
+        if (foreground) {
+            mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
+            mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
+            mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+            mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
+                    oldUserId, userId, uss));
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
+                    oldUserId, userId, uss), getUserSwitchTimeoutMs());
+        }
+
+        if (userInfo.preCreated) {
+            needStart = false;
+        }
+
+        // In most cases, broadcast for the system user starting/started is sent by
+        // ActivityManagerService#systemReady(). However on some HSUM devices (e.g. tablets)
+        // the user switches from the system user to a secondary user while running
+        // ActivityManagerService#systemReady(), thus broadcast is not sent for the system user.
+        // Therefore we send the broadcast for the system user here as well in HSUM.
+        // TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user
+        // in HSUM. Ideally it'd be best to have one single place that sends this notification.
+        final boolean isSystemUserInHeadlessMode = (userId == UserHandle.USER_SYSTEM)
+                && mInjector.isHeadlessSystemUserMode();
+        if (needStart || isSystemUserInHeadlessMode) {
+            sendUserStartedBroadcast(userId, callingUid, callingPid);
+        }
+        t.traceEnd();
+
+        if (foreground) {
+            t.traceBegin("moveUserToForeground");
+            moveUserToForeground(uss, userId);
+            t.traceEnd();
+        } else {
+            t.traceBegin("finishUserBoot");
+            finishUserBoot(uss);
+            t.traceEnd();
+        }
+
+        if (needStart || isSystemUserInHeadlessMode) {
+            t.traceBegin("sendRestartBroadcast");
+            sendUserStartingBroadcast(userId, callingUid, callingPid);
+            t.traceEnd();
+        }
+    }
+
     /**
      * Start user, if it's not already running, and bring it to foreground.
      */
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 7873d34..d67fea0 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -218,3 +218,18 @@
     description: "Set reset_on_fork flag."
     bug: "370988407"
 }
+
+flag {
+    name: "push_global_state_to_oomadjuster"
+    namespace: "backstage_power"
+    description: "Migrate OomAdjuster pulled device state to a push model"
+    bug: "302575389"
+}
+
+flag {
+    name: "oomadjuster_cached_app_tiers"
+    namespace: "system_performance"
+    is_fixed_read_only: true
+    description: "Assign cached oom_score_adj in tiers."
+    bug: "369893532"
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e0cf96f..e97629b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -72,6 +72,10 @@
 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
 import static android.permission.flags.Flags.deviceAwareAppOpNewSchemaEnabled;
 
+import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED;
+import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_OPERATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_PROXY_OPERATION;
 import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
 
 import android.Manifest;
@@ -160,6 +164,7 @@
 import com.android.internal.pm.pkg.component.ParsedAttribution;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -2829,12 +2834,26 @@
 
     @Override
     public int checkOperation(int code, int uid, String packageName) {
+        if (Binder.getCallingPid() != Process.myPid()
+                && Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+                    false);
+        }
         return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
                 Context.DEVICE_ID_DEFAULT, false /*raw*/);
     }
 
     @Override
     public int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId) {
+        if (Binder.getCallingPid() != Process.myPid()
+                && Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION,
+                    false);
+        }
         return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
                 virtualDeviceId, false /*raw*/);
     }
@@ -3015,6 +3034,13 @@
     public SyncNotedAppOp noteProxyOperationWithState(int code,
             AttributionSourceState attributionSourceState, boolean shouldCollectAsyncNotedOp,
             String message, boolean shouldCollectMessage, boolean skipProxyOperation) {
+        if (Binder.getCallingPid() != Process.myPid()
+                && Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, attributionSourceState.uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_PROXY_OPERATION,
+                    attributionSourceState.attributionTag != null);
+        }
         AttributionSource attributionSource = new AttributionSource(attributionSourceState);
         return mCheckOpsDelegateDispatcher.noteProxyOperation(code, attributionSource,
                 shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
@@ -3096,6 +3122,13 @@
     public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
             String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
             boolean shouldCollectMessage) {
+        if (Binder.getCallingPid() != Process.myPid()
+                && Flags.appopAccessTrackingLoggingEnabled()) {
+            FrameworkStatsLog.write(
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_OPERATION,
+                    attributionTag != null);
+        }
         return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
                 attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
                 shouldCollectMessage);
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 03c8156..ed41f2e 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -36,6 +36,7 @@
 import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
 import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
 import static android.app.AppOpsManager.UID_STATE_TOP;
+import static android.permission.flags.Flags.delayUidStateChangesFromCapabilityUpdates;
 import static android.permission.flags.Flags.finishRunningOpsForKilledPackages;
 
 import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
@@ -236,20 +237,26 @@
             mPendingUidStates.put(uid, uidState);
             mPendingCapability.put(uid, capability);
 
+            boolean hasLostCapability = (prevCapability & ~capability) != 0;
+
             if (procState == PROCESS_STATE_NONEXISTENT) {
                 mPendingGone.put(uid, true);
                 commitUidPendingState(uid);
-            } else if (uidState < prevUidState
-                    || (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
-                    && prevUidState > UID_STATE_MAX_LAST_NON_RESTRICTED)) {
+            } else if (uidState < prevUidState) {
                 // We are moving to a more important state, or the new state may be in the
                 // foreground and the old state is in the background, then always do it
                 // immediately.
                 commitUidPendingState(uid);
-            } else if (uidState == prevUidState && capability != prevCapability) {
+            } else if (delayUidStateChangesFromCapabilityUpdates()
+                    && uidState == prevUidState && !hasLostCapability) {
+                // No change on process state, but process capability hasn't decreased.
+                commitUidPendingState(uid);
+            } else if (!delayUidStateChangesFromCapabilityUpdates()
+                    && uidState == prevUidState && capability != prevCapability) {
                 // No change on process state, but process capability has changed.
                 commitUidPendingState(uid);
-            } else if (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED) {
+            } else if (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+                    && (!delayUidStateChangesFromCapabilityUpdates() || !hasLostCapability)) {
                 // We are moving to a less important state, but it doesn't cross the restriction
                 // threshold.
                 commitUidPendingState(uid);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 60dbf3f..dbdc614 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -30,6 +30,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
+import android.content.AttributionSource;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -306,14 +307,13 @@
      * @param on true to enable speakerphone
      * @param eventSource for logging purposes
      */
-    /*package*/ void setSpeakerphoneOn(
-            IBinder cb, int uid, boolean on, boolean isPrivileged, String eventSource) {
-
+    /*package*/ void setSpeakerphoneOn(IBinder cb, @NonNull AttributionSource attributionSource,
+            boolean on, boolean isPrivileged, String eventSource) {
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "setSpeakerphoneOn, on: " + on + " uid: " + uid);
+            Log.v(TAG, "setSpeakerphoneOn, on: " + on + " uid: " + attributionSource.getUid());
         }
-        postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
-                cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
+        postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(cb, attributionSource,
+                new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
                 on, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged));
     }
 
@@ -332,16 +332,18 @@
      * @param eventSource for logging purposes
      * @return false if there is no device and no communication client
      */
-    /*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device,
-                                               boolean isPrivileged, String eventSource) {
-
+    /*package*/ boolean setCommunicationDevice(IBinder cb,
+            @NonNull AttributionSource attributionSource, AudioDeviceInfo device,
+            boolean isPrivileged, String eventSource) {
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "setCommunicationDevice, device: " + device + ", uid: " + uid);
+            Log.v(TAG, "setCommunicationDevice, device: " + device
+                    + ", uid: " + attributionSource.getUid());
         }
 
         if (device == null) {
             synchronized (mDeviceStateLock) {
-                CommunicationRouteClient client = getCommunicationRouteClientForUid(uid);
+                CommunicationRouteClient client =
+                        getCommunicationRouteClientForUid(attributionSource.getUid());
                 if (client == null) {
                     return false;
                 }
@@ -351,7 +353,8 @@
             mCommunicationDeviceUpdateCount++;
             AudioDeviceAttributes deviceAttr =
                     (device != null) ? new AudioDeviceAttributes(device) : null;
-            CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, uid, deviceAttr,
+            CommunicationDeviceInfo deviceInfo =
+                    new CommunicationDeviceInfo(cb, attributionSource, deviceAttr,
                     device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged);
             postSetCommunicationDeviceForClient(deviceInfo);
         }
@@ -369,7 +372,8 @@
             Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo);
         }
         if (!deviceInfo.mOn) {
-            CommunicationRouteClient client = getCommunicationRouteClientForUid(deviceInfo.mUid);
+            CommunicationRouteClient client =
+                    getCommunicationRouteClientForUid(deviceInfo.mAttributionSource.getUid());
             if (client == null || (deviceInfo.mDevice != null
                     && !deviceInfo.mDevice.equals(client.getDevice()))) {
                 return;
@@ -377,53 +381,60 @@
         }
 
         AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null;
-        setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mUid, device,
-                deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged, deviceInfo.mEventSource);
+        setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mAttributionSource,
+                device, deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged,
+                deviceInfo.mEventSource);
     }
 
     /**
      * Indicates if a Bluetooth SCO activation request owner is controlling
      * the SCO audio state itself or not.
-     * @param uid the UID of the SOC request owner app
+     * @param attributionSource the AttributionSource of the SCO request owner app
      * @return true if we should control SCO audio state, false otherwise
      */
-    private boolean shouldStartScoForUid(int uid) {
+    private boolean shouldStartScoForAttributionSource(AttributionSource attributionSource) {
+        if (attributionSource == null) {
+            return true;
+        }
+        int uid = attributionSource.getUid();
         return !(UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)
                 || UserHandle.isSameApp(uid, Process.PHONE_UID)
-                || UserHandle.isSameApp(uid, Process.SYSTEM_UID));
+                || (UserHandle.isSameApp(uid, Process.SYSTEM_UID)
+                    && "com.android.server.telecom".equals(attributionSource.getPackageName())));
     }
 
     @GuardedBy("mDeviceStateLock")
     /*package*/ void setCommunicationRouteForClient(
-                            IBinder cb, int uid, AudioDeviceAttributes device,
-                            int scoAudioMode, boolean isPrivileged, String eventSource) {
-
+            IBinder cb, @NonNull AttributionSource attributionSource, AudioDeviceAttributes device,
+            int scoAudioMode, boolean isPrivileged, String eventSource) {
         if (AudioService.DEBUG_COMM_RTE) {
             Log.v(TAG, "setCommunicationRouteForClient: device: " + device
                     + ", eventSource: " + eventSource);
         }
         AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
-                                        "setCommunicationRouteForClient for uid: " + uid
+                                        "setCommunicationRouteForClient for uid: "
+                                        + attributionSource.getUid()
                                         + " device: " + device + " isPrivileged: " + isPrivileged
                                         + " from API: " + eventSource)).printLog(TAG));
 
-        final int previousBtScoRequesterUid = bluetoothScoRequestOwnerUid();
+        final AttributionSource previousBtScoRequesterAS =
+                bluetoothScoRequestOwnerAttributionSource();
         CommunicationRouteClient client;
 
         // Save previous client route in case of failure to start BT SCO audio
         AudioDeviceAttributes prevClientDevice = null;
         boolean prevPrivileged = false;
-        client = getCommunicationRouteClientForUid(uid);
+        client = getCommunicationRouteClientForUid(attributionSource.getUid());
         if (client != null) {
             prevClientDevice = client.getDevice();
             prevPrivileged = client.isPrivileged();
         }
 
         if (device != null) {
-            client = addCommunicationRouteClient(cb, uid, device, isPrivileged);
+            client = addCommunicationRouteClient(cb, attributionSource, device, isPrivileged);
             if (client == null) {
                 Log.w(TAG, "setCommunicationRouteForClient: could not add client for uid: "
-                        + uid + " and device: " + device);
+                        + attributionSource.getUid() + " and device: " + device);
             }
         } else {
             client = removeCommunicationRouteClient(cb, true);
@@ -431,22 +442,23 @@
         if (client == null) {
             return;
         }
-        final int btScoRequesterUid = bluetoothScoRequestOwnerUid();
-        final boolean isBtScoRequested = btScoRequesterUid != -1;
-        final boolean wasBtScoRequested = previousBtScoRequesterUid != -1;
+        final AttributionSource btScoRequesterAS = bluetoothScoRequestOwnerAttributionSource();
+        final boolean isBtScoRequested = btScoRequesterAS != null;
+        final boolean wasBtScoRequested = previousBtScoRequesterAS != null;
 
         if (mScoManagedByAudio) {
             if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive()
                     || !mBtHelper.isBluetoothScoRequestedInternally())) {
                 boolean scoStarted = false;
-                if (shouldStartScoForUid(btScoRequesterUid)) {
+                if (shouldStartScoForAttributionSource(btScoRequesterAS)) {
                     scoStarted = mBtHelper.startBluetoothSco(scoAudioMode, eventSource);
                     if (!scoStarted) {
                         Log.w(TAG, "setCommunicationRouteForClient: "
-                                + "failure to start BT SCO for uid: " + uid);
+                                + "failure to start BT SCO for uid: " + attributionSource.getUid());
                         // clean up or restore previous client selection
                         if (prevClientDevice != null) {
-                            addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
+                            addCommunicationRouteClient(cb, attributionSource,
+                                    prevClientDevice, prevPrivileged);
                         } else {
                             removeCommunicationRouteClient(cb, true);
                         }
@@ -459,7 +471,7 @@
                     setBluetoothScoOn(true, "setCommunicationRouteForClient");
                 }
             } else if (!isBtScoRequested && wasBtScoRequested) {
-                if (shouldStartScoForUid(previousBtScoRequesterUid)) {
+                if (shouldStartScoForAttributionSource(previousBtScoRequesterAS)) {
                     mBtHelper.stopBluetoothSco(eventSource);
                 }
                 setBluetoothScoOn(false, "setCommunicationRouteForClient");
@@ -469,10 +481,11 @@
                     || !mBtHelper.isBluetoothScoRequestedInternally())) {
                 if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
                     Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
-                            + uid);
+                            + attributionSource.getUid());
                     // clean up or restore previous client selection
                     if (prevClientDevice != null) {
-                        addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
+                        addCommunicationRouteClient(cb, attributionSource,
+                                prevClientDevice, prevPrivileged);
                     } else {
                         removeCommunicationRouteClient(cb, true);
                     }
@@ -583,7 +596,7 @@
                 // Cancelling the route for this client will remove it from the stack and update
                 // the communication route.
                 CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(
-                        crc.getBinder(), crc.getUid(), device, false,
+                        crc.getBinder(), crc.getAttributionSource(), device, false,
                         BtHelper.SCO_MODE_UNDEFINED, "onCheckCommunicationDeviceRemoval",
                         crc.isPrivileged());
                 postSetCommunicationDeviceForClient(deviceInfo);
@@ -619,12 +632,11 @@
     @GuardedBy("mDeviceStateLock")
     /*package*/ void updateCommunicationRouteClientState(
                             CommunicationRouteClient client, boolean wasActive) {
-        int btScoRequesterUid = bluetoothScoRequestOwnerUid();
         client.setPlaybackActive(mAudioService.isPlaybackActiveForUid(client.getUid()));
         client.setRecordingActive(mAudioService.isRecordingActiveForUid(client.getUid()));
         if (wasActive != client.isActive()) {
-            postUpdateCommunicationRouteClient(
-                    btScoRequesterUid, "updateCommunicationRouteClientState");
+            postUpdateCommunicationRouteClient(bluetoothScoRequestOwnerAttributionSource(),
+                    "updateCommunicationRouteClientState");
         }
     }
 
@@ -810,21 +822,26 @@
     }
 
     /**
-     * Helper method on top of isBluetoothScoRequested() returning the UID of the
-     * BT SCO route request owner of -1 if SCO is not requested.
-     * @return the UID of the BT SCO route request owner of -1 if SCO is not requested.
+     * Helper method on top of isBluetoothScoRequested() returning the Attribution Source of the
+     * BT SCO route request owner or null if SCO is not requested.
+     * @return the AttributionSource of the BT SCO route request owner of null.
      */
     @GuardedBy("mDeviceStateLock")
-    /*package*/ int bluetoothScoRequestOwnerUid() {
+    /*package*/ @Nullable AttributionSource bluetoothScoRequestOwnerAttributionSource() {
         if (!isBluetoothScoRequested()) {
-            return -1;
+            return null;
         }
         CommunicationRouteClient crc = topCommunicationRouteClient();
         if (crc == null) {
-            return -1;
+            return null;
         }
-        return crc.getUid();
+        return crc.getAttributionSource();
     }
+
+    private static int safeUidFromAttributionSource(AttributionSource attributionSource) {
+        return (attributionSource != null) ? attributionSource.getUid() : -1;
+    }
+
     /**
      * Helper method on top of isDeviceRequestedForCommunication() indicating if
      * Bluetooth LE Audio communication device is currently requested or not.
@@ -1222,14 +1239,15 @@
     @GuardedBy("mDeviceStateLock")
     /*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
         synchronized (mBluetoothAudioStateLock) {
-            int btScoRequesterUId = bluetoothScoRequestOwnerUid();
+            AttributionSource btScoRequesterAS = bluetoothScoRequestOwnerAttributionSource();
             Log.i(TAG, "setBluetoothScoOn: " + on + ", mBluetoothScoOn: "
-                    + mBluetoothScoOn + ", btScoRequesterUId: " + btScoRequesterUId
+                    + mBluetoothScoOn + ", btScoRequesterUId: "
+                    + safeUidFromAttributionSource(btScoRequesterAS)
                     + ", from: " + eventSource);
             mBluetoothScoOn = on;
             updateAudioHalBluetoothState();
             if (!mScoManagedByAudio) {
-                postUpdateCommunicationRouteClient(btScoRequesterUId, eventSource);
+                postUpdateCommunicationRouteClient(btScoRequesterAS, eventSource);
             }
         }
     }
@@ -1332,25 +1350,26 @@
         sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
     }
 
-    /*package*/ void startBluetoothScoForClient(IBinder cb, int uid, int scoAudioMode,
-                                                boolean isPrivileged, @NonNull String eventSource) {
-
+    /*package*/ void startBluetoothScoForClient(IBinder cb,
+            @NonNull AttributionSource attributionSource, int scoAudioMode, boolean isPrivileged,
+            @NonNull String eventSource) {
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "startBluetoothScoForClient, uid: " + uid);
+            Log.v(TAG, "startBluetoothScoForClient, uid: " + attributionSource.getUid());
         }
-        postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
-                cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
+        postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(cb, attributionSource,
+                new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
                 true, scoAudioMode, eventSource, isPrivileged));
     }
 
-    /*package*/ void stopBluetoothScoForClient(
-                        IBinder cb, int uid, boolean isPrivileged, @NonNull String eventSource) {
-
+    /*package*/ void stopBluetoothScoForClient(IBinder cb,
+            @NonNull AttributionSource attributionSource, boolean isPrivileged,
+            @NonNull String eventSource) {
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "stopBluetoothScoForClient, uid: " + uid);
+            Log.v(TAG, "stopBluetoothScoForClient, uid: " + attributionSource.getUid());
         }
         postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
-                cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
+                cb, attributionSource, new AudioDeviceAttributes(
+                                              AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
                 false, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged));
     }
 
@@ -1566,10 +1585,21 @@
         sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
     }
 
+    private static final class UpdateCommRouteClientInfo {
+        @NonNull public final AttributionSource attributionSource;
+        @NonNull public final String eventSource;
+
+        UpdateCommRouteClientInfo(@NonNull AttributionSource attributionSource,
+                @NonNull String eventSource) {
+            this.attributionSource = attributionSource;
+            this.eventSource = eventSource;
+        }
+    }
+
     /*package*/ void postUpdateCommunicationRouteClient(
-            int btScoRequesterUid, String eventSource) {
-        sendILMsgNoDelay(MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE,
-                btScoRequesterUid, eventSource);
+            AttributionSource attributionSource, String eventSource) {
+        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE,
+            new UpdateCommRouteClientInfo(attributionSource, eventSource));
     }
 
     /*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) {
@@ -1600,18 +1630,18 @@
 
     /*package*/ static final class CommunicationDeviceInfo {
         final @NonNull IBinder mCb; // Identifies the requesting client for death handler
-        final int mUid; // Requester UID
+        final @NonNull AttributionSource mAttributionSource; // Requester attribution source
         final @Nullable AudioDeviceAttributes mDevice; // Device being set or reset.
         final boolean mOn; // true if setting, false if resetting
         final int mScoAudioMode; // only used for SCO: requested audio mode
         final boolean mIsPrivileged; // true if the client app has MODIFY_PHONE_STATE permission
         final @NonNull String mEventSource; // caller identifier for logging
 
-        CommunicationDeviceInfo(@NonNull IBinder cb, int uid,
+        CommunicationDeviceInfo(@NonNull IBinder cb, @NonNull AttributionSource attributionSource,
                 @Nullable AudioDeviceAttributes device, boolean on, int scoAudioMode,
                 @NonNull String eventSource, boolean isPrivileged) {
             mCb = cb;
-            mUid = uid;
+            mAttributionSource = attributionSource;
             mDevice = device;
             mOn = on;
             mScoAudioMode = scoAudioMode;
@@ -1633,19 +1663,19 @@
             }
 
             return mCb.equals(((CommunicationDeviceInfo) o).mCb)
-                    && mUid == ((CommunicationDeviceInfo) o).mUid;
+                    && mAttributionSource.equals(((CommunicationDeviceInfo) o).mAttributionSource);
         }
 
         @Override
         public int hashCode() {
             // only hashing on the fields used in equals()
-            return Objects.hash(mCb.hashCode(), mUid);
+            return Objects.hash(mCb.hashCode(), mAttributionSource);
         }
 
         @Override
         public String toString() {
             return "CommunicationDeviceInfo mCb=" + mCb.toString()
-                    + " mUid=" + mUid
+                    + " mAttributionSource=" + mAttributionSource.toString()
                     + " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]"
                     + " mOn=" + mOn
                     + " mScoAudioMode=" + mScoAudioMode
@@ -1929,7 +1959,8 @@
                                         || btInfo.mProfile == BluetoothProfile.HEARING_AID
                                         || (mScoManagedByAudio
                                             && btInfo.mProfile == BluetoothProfile.HEADSET)) {
-                                    onUpdateCommunicationRouteClient(bluetoothScoRequestOwnerUid(),
+                                    onUpdateCommunicationRouteClient(
+                                            bluetoothScoRequestOwnerAttributionSource(),
                                             "setBluetoothActiveDevice");
                                 }
                             }
@@ -1998,11 +2029,11 @@
                 case MSG_IL_SET_MODE_OWNER:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
-                            int btScoRequesterUid = bluetoothScoRequestOwnerUid();
                             mAudioModeOwner = (AudioModeInfo) msg.obj;
                             if (mAudioModeOwner.mMode != AudioSystem.MODE_RINGTONE) {
                                 onUpdateCommunicationRouteClient(
-                                        btScoRequesterUid, "setNewModeOwner");
+                                        bluetoothScoRequestOwnerAttributionSource(),
+                                        "setNewModeOwner");
                             }
                         }
                     }
@@ -2029,10 +2060,12 @@
                     }
                     break;
 
-                case MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT:
+                case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
-                            onUpdateCommunicationRouteClient(msg.arg1, (String) msg.obj);
+                            UpdateCommRouteClientInfo info = (UpdateCommRouteClientInfo) msg.obj;
+                            onUpdateCommunicationRouteClient(
+                                    info.attributionSource, info.eventSource);
                         }
                     }
                     break;
@@ -2182,7 +2215,7 @@
     private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
 
     private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42;
-    private static final int MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
+    private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
 
     private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45;
     //
@@ -2394,20 +2427,20 @@
 
     private class CommunicationRouteClient implements IBinder.DeathRecipient {
         private final IBinder mCb;
-        private final int mUid;
+        @NonNull private final AttributionSource mAttributionSource;
         private final boolean mIsPrivileged;
         private AudioDeviceAttributes mDevice;
         private boolean mPlaybackActive;
         private boolean mRecordingActive;
 
-        CommunicationRouteClient(IBinder cb, int uid, AudioDeviceAttributes device,
-                                 boolean isPrivileged) {
+        CommunicationRouteClient(IBinder cb, @NonNull AttributionSource attributionSource,
+                AudioDeviceAttributes device, boolean isPrivileged) {
             mCb = cb;
-            mUid = uid;
+            mAttributionSource = attributionSource;
             mDevice = device;
             mIsPrivileged = isPrivileged;
-            mPlaybackActive = mAudioService.isPlaybackActiveForUid(uid);
-            mRecordingActive = mAudioService.isRecordingActiveForUid(uid);
+            mPlaybackActive = mAudioService.isPlaybackActiveForUid(attributionSource.getUid());
+            mRecordingActive = mAudioService.isRecordingActiveForUid(attributionSource.getUid());
         }
 
         public boolean registerDeathRecipient() {
@@ -2438,8 +2471,12 @@
             return mCb;
         }
 
+        @NonNull AttributionSource getAttributionSource() {
+            return mAttributionSource;
+        }
+
         int getUid() {
-            return mUid;
+            return mAttributionSource.getUid();
         }
 
         boolean isPrivileged() {
@@ -2464,7 +2501,7 @@
 
         @Override
         public String toString() {
-            return "[CommunicationRouteClient: mUid: " + mUid
+            return "[CommunicationRouteClient: mAttributionSource: " + mAttributionSource
                     + " mDevice: " + mDevice.toString()
                     + " mIsPrivileged: " + mIsPrivileged
                     + " mPlaybackActive: " + mPlaybackActive
@@ -2479,8 +2516,8 @@
             return;
         }
         Log.w(TAG, "Communication client died");
-        setCommunicationRouteForClient(client.getBinder(), client.getUid(), null,
-                BtHelper.SCO_MODE_UNDEFINED, client.isPrivileged(),
+        setCommunicationRouteForClient(client.getBinder(), client.getAttributionSource(),
+                null, BtHelper.SCO_MODE_UNDEFINED, client.isPrivileged(),
                 "onCommunicationRouteClientDied");
     }
 
@@ -2561,21 +2598,22 @@
     // @GuardedBy("mSetModeLock")
     @GuardedBy("mDeviceStateLock")
     private void onUpdateCommunicationRouteClient(
-            int previousBtScoRequesterUid, String eventSource) {
+            @Nullable AttributionSource previousBtScoRequesterAS, String eventSource) {
         CommunicationRouteClient crc = topCommunicationRouteClient();
         if (AudioService.DEBUG_COMM_RTE) {
             Log.v(TAG, "onUpdateCommunicationRouteClient, crc: " + crc
-                    + " previousBtScoRequesterUid: " + previousBtScoRequesterUid
+                    + " previous BT SCO Requester UID: "
+                    + safeUidFromAttributionSource(previousBtScoRequesterAS)
                     + " eventSource: " + eventSource);
         }
         if (crc != null) {
-            setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
-                    BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
+            setCommunicationRouteForClient(crc.getBinder(), crc.getAttributionSource(),
+                    crc.getDevice(), BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
         } else {
-            boolean wasScoRequested = previousBtScoRequesterUid != -1;
+            boolean wasScoRequested = previousBtScoRequesterAS != null;
             if (!isBluetoothScoRequested() && wasScoRequested) {
                 if (mScoManagedByAudio) {
-                    if (shouldStartScoForUid(previousBtScoRequesterUid)) {
+                    if (shouldStartScoForAttributionSource(previousBtScoRequesterAS)) {
                         mBtHelper.stopBluetoothSco(eventSource);
                     }
                     setBluetoothScoOn(false, eventSource);
@@ -2624,12 +2662,13 @@
     }
 
     @GuardedBy("mDeviceStateLock")
-    private CommunicationRouteClient addCommunicationRouteClient(IBinder cb, int uid,
-                AudioDeviceAttributes device, boolean isPrivileged) {
+    private CommunicationRouteClient addCommunicationRouteClient(
+            IBinder cb, @NonNull AttributionSource attributionSource, AudioDeviceAttributes device,
+            boolean isPrivileged) {
         // always insert new request at first position
         removeCommunicationRouteClient(cb, true);
         CommunicationRouteClient client =
-                new CommunicationRouteClient(cb, uid, device, isPrivileged);
+                new CommunicationRouteClient(cb, attributionSource, device, isPrivileged);
         if (client.registerDeathRecipient()) {
             mCommunicationRouteClients.add(0, client);
             if (!client.isActive()) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1563a62..37a2fba 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -69,6 +69,7 @@
 import static com.android.media.audio.Flags.equalScoLeaVcIndexRange;
 import static com.android.media.audio.Flags.replaceStreamBtSco;
 import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
+import static com.android.media.audio.Flags.ringMyCar;
 import static com.android.media.audio.Flags.setStreamVolumeOrder;
 import static com.android.media.audio.Flags.vgsVssSyncMuteOrder;
 import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
@@ -286,7 +287,6 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.CancellationException;
-import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -762,7 +762,7 @@
 
     /** Streams that can be muted by system. Do not resolve to aliases when checking.
      * @see System#MUTE_STREAMS_AFFECTED */
-    private int mMuteAffectedStreams;
+    protected int mMuteAffectedStreams;
 
     /** Streams that can be muted by user. Do not resolve to aliases when checking.
      * @see System#MUTE_STREAMS_AFFECTED */
@@ -1466,7 +1466,8 @@
 
         mPlaybackMonitor =
                 new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM],
-                        device -> onMuteAwaitConnectionTimeout(device));
+                        device -> onMuteAwaitConnectionTimeout(device),
+                        stream -> isStreamMute(stream));
         mPlaybackMonitor.registerPlaybackCallback(mPlaybackActivityMonitor, true);
 
         mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
@@ -2748,6 +2749,11 @@
         }
     }
 
+    @Override
+    protected void onUnhandledException(int code, int flags, Exception e) {
+        Slog.wtf(TAG, "Uncaught exception in AudioService: " + code + ", " + flags, e);
+    }
+
     @Override // Binder call
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
@@ -4842,6 +4848,8 @@
                 + replaceStreamBtSco());
         pw.println("\tcom.android.media.audio.equalScoLeaVcIndexRange:"
                 + equalScoLeaVcIndexRange());
+        pw.println("\tcom.android.media.audio.ringMyCar:"
+                + ringMyCar());
     }
 
     private void dumpAudioMode(PrintWriter pw) {
@@ -6836,9 +6844,13 @@
      * @see AudioManager#setCommunicationDevice(int)
      * @see AudioManager#clearCommunicationDevice()
      */
-    public boolean setCommunicationDevice(IBinder cb, int portId) {
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
+    public boolean setCommunicationDevice(IBinder cb, int portId,
+            @NonNull AttributionSource attributionSource) {
+        if (attributionSource == null) {
+            return false;
+        }
+        final int uid = attributionSource.getUid();
+        final int pid = attributionSource.getPid();
 
         AudioDeviceInfo device = null;
         if (portId != 0) {
@@ -6888,7 +6900,8 @@
                 == PackageManager.PERMISSION_GRANTED;
         final long ident = Binder.clearCallingIdentity();
         try {
-            return mDeviceBroker.setCommunicationDevice(cb, uid, device, isPrivileged, eventSource);
+            return mDeviceBroker.setCommunicationDevice(
+                    cb, attributionSource, device, isPrivileged, eventSource);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -6930,7 +6943,11 @@
     }
 
     /** @see AudioManager#setSpeakerphoneOn(boolean) */
-    public void setSpeakerphoneOn(IBinder cb, boolean on) {
+    public void setSpeakerphoneOn(IBinder cb, boolean on,
+            @NonNull AttributionSource attributionSource) {
+        if (attributionSource == null) {
+            return;
+        }
         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
             return;
         }
@@ -6938,8 +6955,8 @@
                 == PackageManager.PERMISSION_GRANTED;
 
         // for logging only
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
+        final int uid = attributionSource.getUid();
+        final int pid = attributionSource.getPid();
 
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                 .append(") from u/pid:").append(uid).append("/")
@@ -6954,7 +6971,7 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mDeviceBroker.setSpeakerphoneOn(cb, uid, on, isPrivileged, eventSource);
+            mDeviceBroker.setSpeakerphoneOn(cb, attributionSource, on, isPrivileged, eventSource);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -7058,13 +7075,17 @@
     }
 
     /** @see AudioManager#startBluetoothSco() */
-    public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+    public void startBluetoothSco(IBinder cb, int targetSdkVersion,
+            @NonNull AttributionSource attributionSource) {
+        if (attributionSource == null) {
+            return;
+        }
         if (!checkAudioSettingsPermission("startBluetoothSco()")) {
             return;
         }
 
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
+        final int uid = attributionSource.getUid();
+        final int pid = attributionSource.getPid();
         final int scoAudioMode =
                 (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
                         BtHelper.SCO_MODE_VIRTUAL_CALL : BtHelper.SCO_MODE_UNDEFINED;
@@ -7079,18 +7100,22 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(scoAudioMode))
                 .record();
-        startBluetoothScoInt(cb, uid, scoAudioMode, eventSource);
+        startBluetoothScoInt(cb, attributionSource, scoAudioMode, eventSource);
 
     }
 
     /** @see AudioManager#startBluetoothScoVirtualCall() */
-    public void startBluetoothScoVirtualCall(IBinder cb) {
+    public void startBluetoothScoVirtualCall(IBinder cb,
+            @NonNull AttributionSource attributionSource) {
+        if (attributionSource == null) {
+            return;
+        }
         if (!checkAudioSettingsPermission("startBluetoothScoVirtualCall()")) {
             return;
         }
 
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
+        final int uid = attributionSource.getUid();
+        final int pid = attributionSource.getPid();
         final String eventSource = new StringBuilder("startBluetoothScoVirtualCall()")
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
@@ -7102,10 +7127,11 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
                 .record();
-        startBluetoothScoInt(cb, uid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
+        startBluetoothScoInt(cb, attributionSource, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
     }
 
-    void startBluetoothScoInt(IBinder cb, int uid, int scoAudioMode, @NonNull String eventSource) {
+    void startBluetoothScoInt(IBinder cb, AttributionSource attributionSource,
+            int scoAudioMode, @NonNull String eventSource) {
         MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -7121,7 +7147,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             mDeviceBroker.startBluetoothScoForClient(
-                    cb, uid, scoAudioMode, isPrivileged, eventSource);
+                    cb, attributionSource, scoAudioMode, isPrivileged, eventSource);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -7129,13 +7155,17 @@
     }
 
     /** @see AudioManager#stopBluetoothSco() */
-    public void stopBluetoothSco(IBinder cb){
+    public void stopBluetoothSco(IBinder cb,
+            @NonNull AttributionSource attributionSource) {
+        if (attributionSource == null) {
+            return;
+        }
         if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
                 !mSystemReady) {
             return;
         }
-        final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
+        final int uid = attributionSource.getUid();
+        final int pid = attributionSource.getPid();
         final String eventSource =  new StringBuilder("stopBluetoothSco()")
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
@@ -7143,7 +7173,8 @@
                 == PackageManager.PERMISSION_GRANTED;
         final long ident = Binder.clearCallingIdentity();
         try {
-            mDeviceBroker.stopBluetoothScoForClient(cb, uid, isPrivileged, eventSource);
+            mDeviceBroker.stopBluetoothScoForClient(
+                    cb, attributionSource, isPrivileged, eventSource);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -8044,7 +8075,14 @@
         }
         synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
             if (mAbsoluteVolumeDeviceInfoMap.containsKey(audioSystemDeviceOut)) {
-                return mAbsoluteVolumeDeviceInfoMap.get(audioSystemDeviceOut).mDeviceVolumeBehavior;
+                final AbsoluteVolumeDeviceInfo deviceInfo = mAbsoluteVolumeDeviceInfoMap.get(
+                        audioSystemDeviceOut);
+                if (deviceInfo != null) {
+                    return deviceInfo.mDeviceVolumeBehavior;
+                }
+
+                Log.e(TAG,
+                        "Null absolute volume device info stored for key " + audioSystemDeviceOut);
             }
         }
 
@@ -8661,9 +8699,14 @@
             // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
             // This allows RX path muting by the audio HAL only when explicitly muted but not when
             // index is just set to 0 to repect BT requirements
+            boolean muted = false;
             if (mHasValidStreamType && isVssMuteBijective(mPublicStreamType)
                     && getVssForStreamOrDefault(mPublicStreamType).isFullyMuted()) {
-                index = 0;
+                if (ringMyCar()) {
+                    muted = true;
+                } else {
+                    index = 0;
+                }
             } else if (isStreamBluetoothSco(mPublicStreamType) && index == 0) {
                 index = 1;
             }
@@ -8673,13 +8716,14 @@
                         / getVssForStreamOrDefault(mPublicStreamType).getIndexStepFactor());
             }
 
+
             if (DEBUG_VOL) {
                 Log.d(TAG, "setVolumeIndexInt(" + mAudioVolumeGroup.getId() + ", " + index + ", "
-                        + device + ")");
+                        + muted + ", " + device + ")");
             }
 
             // Set the volume index
-            mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+            mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, muted, device);
         }
 
         @GuardedBy("AudioService.VolumeStreamState.class")
@@ -9263,6 +9307,13 @@
             }
         }
 
+        /**
+         * Sends the new volume index on the given device to native.
+         *
+         * <p>Make sure the index is consistent with the muting state. When ringMyCar is enabled
+         * will send the non-zero index together with muted state. Otherwise, index 0  will be sent
+         * to native for signalising a muted stream.
+         **/
         @GuardedBy("VolumeStreamState.class")
         private void setStreamVolumeIndex(int index, int device) {
             // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
@@ -9277,18 +9328,19 @@
                         / 10;
             }
 
+            boolean muted = ringMyCar() ? isFullyMuted() : false;
             if (DEBUG_VOL) {
-                Log.d(TAG, "setStreamVolumeIndexAS(" + mStreamType + ", " + index + ", " + device
-                        + ")");
+                Log.d(TAG, "setStreamVolumeIndexAS(streamType=" + mStreamType + ", index=" + index
+                        + ", muted=" + muted + ", device=" + device + ")");
             }
-            mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
+            mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, muted, device);
         }
 
         // must be called while synchronized VolumeStreamState.class
         @GuardedBy("VolumeStreamState.class")
         /*package*/ void applyDeviceVolume_syncVSS(int device) {
             int index;
-            if (isFullyMuted()) {
+            if (isFullyMuted() && !ringMyCar()) {
                 index = 0;
             } else if (isAbsoluteVolumeDevice(device)
                     || isA2dpAbsoluteVolumeDevice(device)
@@ -9322,7 +9374,7 @@
                 for (int i = 0; i < mIndexMap.size(); i++) {
                     final int device = mIndexMap.keyAt(i);
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
-                        if (isFullyMuted()) {
+                        if (isFullyMuted() && !ringMyCar()) {
                             index = 0;
                         } else if (isAbsoluteVolumeDevice(device)
                                 || isA2dpAbsoluteVolumeDevice(device)
@@ -9357,7 +9409,7 @@
                 }
                 // apply default volume last: by convention , default device volume will be used
                 // by audio policy manager if no explicit volume is present for a given device type
-                if (isFullyMuted()) {
+                if (isFullyMuted() && !ringMyCar()) {
                     index = 0;
                 } else {
                     index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
@@ -15038,6 +15090,11 @@
 
     private void addAudioSystemDeviceOutToAbsVolumeDevices(int audioSystemDeviceOut,
             AbsoluteVolumeDeviceInfo info) {
+        if (info == null) {
+            Log.e(TAG, "Cannot add null absolute volume info for audioSystemDeviceOut "
+                    + audioSystemDeviceOut);
+            return;
+        }
         if (DEBUG_VOL) {
             Log.d(TAG, "Adding DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
                     + " to mAbsoluteVolumeDeviceInfoMap with behavior "
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 5cabdde..e86c34c 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -547,13 +547,14 @@
      * @param device
      * @return
      */
-    public int setStreamVolumeIndexAS(int stream, int index, int device) {
-        return AudioSystem.setStreamVolumeIndexAS(stream, index, device);
+    public int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) {
+        return AudioSystem.setStreamVolumeIndexAS(stream, index, muted, device);
     }
 
     /** Same as {@link AudioSystem#setVolumeIndexForAttributes(AudioAttributes, int, int)} */
-    public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
-        return AudioSystem.setVolumeIndexForAttributes(attributes, index, device);
+    public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, boolean muted,
+            int device) {
+        return AudioSystem.setVolumeIndexForAttributes(attributes, index, muted, device);
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index 01f770b..9fa5da4 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -133,6 +133,9 @@
     private static final EventLogger sLogger = new EventLogger(
             AudioService.LOG_NB_EVENTS_LOUDNESS_CODEC, "Loudness updates");
 
+    private final Object mDispatcherLock = new Object();
+
+    @GuardedBy("mDispatcherLock")
     private final LoudnessRemoteCallbackList mLoudnessUpdateDispatchers =
             new LoudnessRemoteCallbackList(this);
 
@@ -339,12 +342,16 @@
     }
 
     void registerLoudnessCodecUpdatesDispatcher(ILoudnessCodecUpdatesDispatcher dispatcher) {
-        mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid());
+        synchronized (mDispatcherLock) {
+            mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid());
+        }
     }
 
     void unregisterLoudnessCodecUpdatesDispatcher(
             ILoudnessCodecUpdatesDispatcher dispatcher) {
-        mLoudnessUpdateDispatchers.unregister(dispatcher);
+        synchronized (mDispatcherLock) {
+            mLoudnessUpdateDispatchers.unregister(dispatcher);
+        }
     }
 
     void startLoudnessCodecUpdates(int sessionId) {
@@ -640,17 +647,20 @@
             Log.d(TAG,
                     "dispatchNewLoudnessParameters: sessionId " + sessionId + " bundle: " + bundle);
         }
-        final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast();
-        for (int i = 0; i < nbDispatchers; ++i) {
-            try {
-                mLoudnessUpdateDispatchers.getBroadcastItem(i)
-                        .dispatchLoudnessCodecParameterChange(sessionId, bundle);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error dispatching for sessionId " + sessionId + " bundle: " + bundle,
-                        e);
+        synchronized (mDispatcherLock) {
+            final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast();
+            for (int i = 0; i < nbDispatchers; ++i) {
+                try {
+                    mLoudnessUpdateDispatchers.getBroadcastItem(i)
+                            .dispatchLoudnessCodecParameterChange(sessionId, bundle);
+                } catch (RemoteException e) {
+                    Log.e(TAG,
+                            "Error dispatching for sessionId " + sessionId + " bundle: " + bundle,
+                            e);
+                }
             }
+            mLoudnessUpdateDispatchers.finishBroadcast();
         }
-        mLoudnessUpdateDispatchers.finishBroadcast();
     }
 
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a734e73..b63b07f 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -72,6 +72,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Class to receive and dispatch updates from AudioSystem about recording configurations.
@@ -160,18 +161,22 @@
 
     private final Context mContext;
     private int mSavedAlarmVolume = -1;
+    private boolean mSavedAlarmMuted = false;
+    private final Function<Integer, Boolean> mIsStreamMutedCb;
     private final int mMaxAlarmVolume;
     private int mPrivilegedAlarmActiveCount = 0;
     private final Consumer<AudioDeviceAttributes> mMuteAwaitConnectionTimeoutCb;
     private final FadeOutManager mFadeOutManager = new FadeOutManager();
 
     PlaybackActivityMonitor(Context context, int maxAlarmVolume,
-            Consumer<AudioDeviceAttributes> muteTimeoutCallback) {
+            Consumer<AudioDeviceAttributes> muteTimeoutCallback,
+            Function<Integer, Boolean> isStreamMutedCb) {
         mContext = context;
         mMaxAlarmVolume = maxAlarmVolume;
         PlayMonitorClient.sListenerDeathMonitor = this;
         AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
         mMuteAwaitConnectionTimeoutCb = muteTimeoutCallback;
+        mIsStreamMutedCb = isStreamMutedCb;
         initEventHandler();
     }
 
@@ -332,8 +337,9 @@
                     if (mPrivilegedAlarmActiveCount++ == 0) {
                         mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex(
                                 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER);
+                        mSavedAlarmMuted = mIsStreamMutedCb.apply(AudioSystem.STREAM_ALARM);
                         AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
-                                mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+                                mMaxAlarmVolume, /*muted=*/false, AudioSystem.DEVICE_OUT_SPEAKER);
                     }
                 } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
                         apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
@@ -342,7 +348,8 @@
                                 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) ==
                                 mMaxAlarmVolume) {
                             AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
-                                    mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
+                                    mSavedAlarmVolume, mSavedAlarmMuted,
+                                    AudioSystem.DEVICE_OUT_SPEAKER);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index 1ea72d7..f66c7e1 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -36,6 +36,7 @@
 import android.util.Slog;
 
 import com.android.server.backup.Flags;
+import com.android.server.notification.NotificationBackupHelper;
 
 import com.google.android.collect.Sets;
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 09386ae28..8a98585 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -115,7 +115,7 @@
     }
 
     @LockoutTracker.LockoutMode
-    public int handleFailedAttempt(int userId) {
+    private int handleFailedAttempt(int userId) {
         if (mLockoutTracker != null) {
             mLockoutTracker.addFailedAttemptForUser(getTargetUserId());
         }
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index a9fe8cb..8d64383 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -242,7 +242,8 @@
         boolean enabled = true;
         final int userId = UserHandle.getUserId(uid);
         for (String packageName : packages) {
-            final var appInfo = getApplicationInfo(packageName, userId);
+            final var appInfo =
+                fixTargetSdk(getApplicationInfo(packageName, userId), uid);
             enabled &= isChangeEnabledInternal(changeId, appInfo);
         }
         return enabled;
@@ -261,7 +262,8 @@
         boolean enabled = true;
         final int userId = UserHandle.getUserId(uid);
         for (String packageName : packages) {
-            final var appInfo = getApplicationInfo(packageName, userId);
+            final var appInfo =
+                fixTargetSdk(getApplicationInfo(packageName, userId), uid);
             enabled &= isChangeEnabledInternalNoLogging(changeId, appInfo);
         }
         return enabled;
@@ -504,6 +506,15 @@
                 packageName, 0, Process.myUid(), userId);
     }
 
+    private ApplicationInfo fixTargetSdk(ApplicationInfo appInfo, int uid) {
+        // b/282922910 - we don't want apps sharing system uid and targeting
+        // older target sdk to impact all system uid apps
+        if (Flags.systemUidTargetSystemSdk() && uid == Process.SYSTEM_UID) {
+            appInfo.targetSdkVersion = Build.VERSION.SDK_INT;
+        }
+        return appInfo;
+    }
+
     private void killPackage(String packageName) {
         int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName,
                 0, UserHandle.myUserId());
diff --git a/services/core/java/com/android/server/compat/platform_compat_flags.aconfig b/services/core/java/com/android/server/compat/platform_compat_flags.aconfig
new file mode 100644
index 0000000..fb32323
--- /dev/null
+++ b/services/core/java/com/android/server/compat/platform_compat_flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.server.compat"
+container: "system"
+
+flag {
+    name: "system_uid_target_system_sdk"
+    namespace: "app_compat"
+    description: "Compat framework feature flag for forcing all system uid apps to target system sdk"
+    bug: "29702703"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/content/OWNERS b/services/core/java/com/android/server/content/OWNERS
index b6a9fe86..5642382 100644
--- a/services/core/java/com/android/server/content/OWNERS
+++ b/services/core/java/com/android/server/content/OWNERS
@@ -1 +1,3 @@
-include /services/core/java/com/android/server/am/OWNERS
\ No newline at end of file
+include /services/core/java/com/android/server/am/OWNERS
+
+per-file Sync* = file:/apex/jobscheduler/JOB_OWNERS
\ No newline at end of file
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 1094bee..8b9c664 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -778,15 +778,16 @@
         processRecord.notifyRequestActiveAsync(request.getToken());
     }
 
-    private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
+    @Nullable
+    private DeviceStateInfo registerProcess(int pid, IDeviceStateManagerCallback callback) {
         synchronized (mLock) {
             if (mProcessRecords.contains(pid)) {
                 throw new SecurityException("The calling process has already registered an"
                         + " IDeviceStateManagerCallback.");
             }
 
-            ProcessRecord record = new ProcessRecord(callback, pid, this::handleProcessDied,
-                    mHandler);
+            final ProcessRecord record =
+                    new ProcessRecord(callback, pid, this::handleProcessDied, mHandler);
             try {
                 callback.asBinder().linkToDeath(record, 0);
             } catch (RemoteException ex) {
@@ -794,15 +795,20 @@
             }
             mProcessRecords.put(pid, record);
 
-            // Callback clients should not be notified of invalid device states, so calls to
-            // #getDeviceStateInfoLocked should be gated on checks if a committed state is present
-            // before getting the device state info.
-            DeviceStateInfo currentInfo = mCommittedState.isPresent()
-                    ? getDeviceStateInfoLocked() : null;
-            if (currentInfo != null) {
-                // If there is not a committed state we'll wait to notify the process of the initial
-                // value.
-                record.notifyDeviceStateInfoAsync(currentInfo);
+            final DeviceStateInfo currentInfo =
+                    mCommittedState.isPresent() ? getDeviceStateInfoLocked() : null;
+            if (com.android.window.flags.Flags.wlinfoOncreate()) {
+                return currentInfo;
+            } else {
+                // Callback clients should not be notified of invalid device states, so calls to
+                // #getDeviceStateInfoLocked should be gated on checks if a committed state is
+                // present before getting the device state info.
+                if (currentInfo != null) {
+                    // If there is not a committed state we'll wait to notify the process of the
+                    // initial value.
+                    record.notifyDeviceStateInfoAsync(currentInfo);
+                }
+                return null;
             }
         }
     }
@@ -1286,8 +1292,9 @@
             }
         }
 
+        @Nullable
         @Override // Binder call
-        public void registerCallback(IDeviceStateManagerCallback callback) {
+        public DeviceStateInfo registerCallback(IDeviceStateManagerCallback callback) {
             if (callback == null) {
                 throw new IllegalArgumentException("Device state callback must not be null.");
             }
@@ -1295,7 +1302,7 @@
             final int callingPid = Binder.getCallingPid();
             final long token = Binder.clearCallingIdentity();
             try {
-                registerProcess(callingPid, callback);
+                return registerProcess(callingPid, callback);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 8d96ba9..c4e1036 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -150,7 +150,7 @@
  *      <screenBrightnessDefault>0.65</screenBrightnessDefault>
  *      <powerThrottlingConfig>
  *        <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>
- *        <customAnimationRateSec>0.004</customAnimationRateSec>
+ *        <customAnimationRate>0.004</customAnimationRate>
  *        <pollingWindowMaxMillis>30000</pollingWindowMaxMillis>
  *        <pollingWindowMinMillis>10000</pollingWindowMinMillis>
  *          <powerThrottlingMap>
@@ -2193,11 +2193,11 @@
             return;
         }
         float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue();
-        float customAnimationRateSec = powerThrottlingCfg.getCustomAnimationRateSec().floatValue();
+        float customAnimationRate = powerThrottlingCfg.getCustomAnimationRate().floatValue();
         int pollingWindowMaxMillis = powerThrottlingCfg.getPollingWindowMaxMillis().intValue();
         int pollingWindowMinMillis = powerThrottlingCfg.getPollingWindowMinMillis().intValue();
         mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap,
-                                                                   customAnimationRateSec,
+                                                                   customAnimationRate,
                                                                    pollingWindowMaxMillis,
                                                                    pollingWindowMinMillis);
     }
@@ -3012,16 +3012,16 @@
         /** Lowest brightness cap allowed for this device. */
         public final float brightnessLowestCapAllowed;
         /** Time take to animate brightness in seconds. */
-        public final float customAnimationRateSec;
+        public final float customAnimationRate;
         /** Time window for maximum polling power in milliseconds. */
         public final int pollingWindowMaxMillis;
         /** Time window for minimum polling power in milliseconds. */
         public final int pollingWindowMinMillis;
         public PowerThrottlingConfigData(float brightnessLowestCapAllowed,
-                float customAnimationRateSec, int pollingWindowMaxMillis,
+                float customAnimationRate, int pollingWindowMaxMillis,
                 int pollingWindowMinMillis) {
             this.brightnessLowestCapAllowed = brightnessLowestCapAllowed;
-            this.customAnimationRateSec = customAnimationRateSec;
+            this.customAnimationRate = customAnimationRate;
             this.pollingWindowMaxMillis = pollingWindowMaxMillis;
             this.pollingWindowMinMillis = pollingWindowMinMillis;
         }
@@ -3031,7 +3031,7 @@
             return "PowerThrottlingConfigData{"
                     + "brightnessLowestCapAllowed: "
                     + brightnessLowestCapAllowed
-                    + ", customAnimationRateSec: " + customAnimationRateSec
+                    + ", customAnimationRate: " + customAnimationRate
                     + ", pollingWindowMaxMillis: " + pollingWindowMaxMillis
                     + ", pollingWindowMinMillis: " + pollingWindowMinMillis
                     + "} ";
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index acf4db3..0807c70 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -292,6 +292,13 @@
      */
     public float renderFrameRate;
 
+
+    /**
+     * If {@code true}, this Display supports adaptive refresh rates.
+     * @see android.view.DisplayInfo#hasArrSupport for more details.
+     */
+    public boolean hasArrSupport;
+
     /**
      * The default mode of the display.
      */
@@ -540,7 +547,8 @@
                 other.brightnessDefault)
                 || !Objects.equals(roundedCorners, other.roundedCorners)
                 || installOrientation != other.installOrientation
-                || !Objects.equals(displayShape, other.displayShape)) {
+                || !Objects.equals(displayShape, other.displayShape)
+                || hasArrSupport != other.hasArrSupport) {
             diff |= DIFF_OTHER;
         }
         return diff;
@@ -558,6 +566,7 @@
         height = other.height;
         modeId = other.modeId;
         renderFrameRate = other.renderFrameRate;
+        hasArrSupport = other.hasArrSupport;
         defaultModeId = other.defaultModeId;
         userPreferredModeId = other.userPreferredModeId;
         supportedModes = other.supportedModes;
@@ -602,6 +611,7 @@
         sb.append(width).append(" x ").append(height);
         sb.append(", modeId ").append(modeId);
         sb.append(", renderFrameRate ").append(renderFrameRate);
+        sb.append(", hasArrSupport ").append(hasArrSupport);
         sb.append(", defaultModeId ").append(defaultModeId);
         sb.append(", userPreferredModeId ").append(userPreferredModeId);
         sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 179ec63..3603cdb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1769,10 +1769,11 @@
             flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
         }
         // Put the display in the virtual device's display group only if it's not a mirror display,
-        // and if it doesn't need its own display group. So effectively, mirror displays go into the
-        // default display group.
+        // it is a trusted display, and it doesn't need its own display group. So effectively,
+        // mirror and untrusted displays go into the default display group.
         if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) == 0
                 && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0
+                && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == VIRTUAL_DISPLAY_FLAG_TRUSTED
                 && virtualDevice != null) {
             flags |= VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP;
         }
@@ -1848,9 +1849,7 @@
 
         if (callingUid != Process.SYSTEM_UID
                 && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
-            // The virtualDevice instance has been validated above using isValidVirtualDevice
-            if (virtualDevice == null
-                    && !checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
+            if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
                 throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                         + "create a virtual display which is not in the default DisplayGroup.");
             }
@@ -2302,6 +2301,11 @@
         updateLogicalDisplayState(display);
 
         mExternalDisplayPolicy.handleLogicalDisplayAddedLocked(display);
+
+        if (mFlags.isApplyDisplayChangedDuringDisplayAddedEnabled()) {
+            applyDisplayChangedLocked(display);
+        }
+
         if (mDisplayTopologyCoordinator != null) {
             mDisplayTopologyCoordinator.onDisplayAdded(display.getDisplayInfoLocked());
         }
@@ -5667,6 +5671,11 @@
                 displayPowerController.stylusGestureStarted(eventTime);
             }
         }
+
+        @Override
+        public boolean isDisplayReadyForMirroring(int displayId) {
+            return mExternalDisplayPolicy.isDisplayReadyForMirroring(displayId);
+        }
     }
 
     class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index 28a0b28..f34d2cc 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -375,6 +375,54 @@
         }
     }
 
+    boolean isDisplayReadyForMirroring(int displayId) {
+        if (!mFlags.isWaitingConfirmationBeforeMirroringEnabled()) {
+            if (DEBUG) {
+                Slog.d(TAG, "isDisplayReadyForMirroring: mirroring CONFIRMED - "
+                        + " flag 'waiting for confirmation before mirroring' is disabled");
+            }
+            return true;
+        }
+
+        synchronized (mSyncRoot) {
+            if (!mIsBootCompleted) {
+                if (DEBUG) {
+                    Slog.d(TAG, "isDisplayReadyForMirroring: mirroring is not confirmed - "
+                            + "boot is in progress");
+                }
+                return false;
+            }
+
+            var logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
+            if (logicalDisplay == null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "isDisplayReadyForMirroring: mirroring is not confirmed - "
+                            + "logicalDisplay is null");
+                }
+                return false;
+            }
+
+            if (!isExternalDisplayLocked(logicalDisplay)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "isDisplayReadyForMirroring: mirroring is not confirmed - "
+                            + "logicalDisplay" + logicalDisplay.getDisplayIdLocked()
+                            + " type is " + logicalDisplay.getDisplayInfoLocked().type);
+                }
+                return false;
+            }
+
+            if (!logicalDisplay.isEnabledLocked()) {
+                if (DEBUG) {
+                    Slog.d(TAG, "isDisplayReadyForMirroring: mirroring is not confirmed - "
+                            + "logicalDisplay is disabled");
+                }
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
         @Override
         public void notifyThrottling(@NonNull final Temperature temp) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 5edea0a..f9c3a46 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -246,6 +246,7 @@
         private int mActiveModeId = INVALID_MODE_ID;
         private boolean mDisplayModeSpecsInvalid;
         private int mActiveColorMode;
+        private boolean mHasArrSupport;
         private Display.HdrCapabilities mHdrCapabilities;
         private boolean mAllmSupported;
         private boolean mGameContentTypeSupported;
@@ -311,6 +312,7 @@
             changed |= updateHdrCapabilitiesLocked(dynamicInfo.hdrCapabilities);
             changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported);
             changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported);
+            changed |= updateHasArrSupportLocked(dynamicInfo.hasArrSupport);
 
             if (changed) {
                 mHavePendingChanges = true;
@@ -602,6 +604,14 @@
             return true;
         }
 
+        private boolean updateHasArrSupportLocked(boolean newHasArrSupport) {
+            if (mHasArrSupport == newHasArrSupport) {
+                return false;
+            }
+            mHasArrSupport = newHasArrSupport;
+            return true;
+        }
+
         private boolean updateAllmSupport(boolean supported) {
             if (mAllmSupported == supported) {
                 return false;
@@ -684,6 +694,7 @@
                     mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
                 }
                 mInfo.hdrCapabilities = mHdrCapabilities;
+                mInfo.hasArrSupport = mHasArrSupport;
                 mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos;
                 mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos;
                 mInfo.state = mState;
@@ -1274,6 +1285,7 @@
             pw.println("mActiveColorMode=" + mActiveColorMode);
             pw.println("mDefaultModeId=" + mDefaultModeId);
             pw.println("mUserPreferredModeId=" + mUserPreferredModeId);
+            pw.println("mHasArrSupport=" + mHasArrSupport);
             pw.println("mState=" + Display.stateToString(mState));
             pw.println("mCommittedState=" + Display.stateToString(mCommittedState));
             pw.println("mBrightnessState=" + mBrightnessState);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 007e3a8..074a4d8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -506,12 +506,13 @@
             mBaseDisplayInfo.rotation = Surface.ROTATION_0;
             mBaseDisplayInfo.modeId = deviceInfo.modeId;
             mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate;
+            mBaseDisplayInfo.hasArrSupport = deviceInfo.hasArrSupport;
             mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
             mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId;
             mBaseDisplayInfo.supportedModes = Arrays.copyOf(
                     deviceInfo.supportedModes, deviceInfo.supportedModes.length);
             mBaseDisplayInfo.appsSupportedModes = syntheticModeManager.createAppSupportedModes(
-                    config, mBaseDisplayInfo.supportedModes
+                    config, mBaseDisplayInfo.supportedModes, mBaseDisplayInfo.hasArrSupport
             );
             mBaseDisplayInfo.colorMode = deviceInfo.colorMode;
             mBaseDisplayInfo.supportedColorModes = Arrays.copyOf(
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 06a9103..09fa4e6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY;
 import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP;
 import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE;
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
@@ -594,6 +595,13 @@
     boolean shouldDeviceBeWoken(DeviceState pendingState, DeviceState currentState,
             boolean isInteractive, boolean isBootCompleted) {
         if (mDeviceStateManagerFlags.deviceStatePropertyMigration()) {
+            if (currentState.hasProperties(PROPERTY_EMULATED_ONLY)
+                    && !pendingState.hasProperties(PROPERTY_EMULATED_ONLY)) {
+                // Do not wake the device, since this transition may occur due to the user pressing
+                // the power button to exit an emulated state.
+                return false;
+            }
+
             return pendingState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE)
                     && !currentState.equals(INVALID_DEVICE_STATE)
                     && !currentState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE)
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index 9a0ee03..a4804e1 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -50,8 +50,10 @@
     public static final int MODIFIER_THROTTLED = 0x8;
     public static final int MODIFIER_MIN_LUX = 0x10;
     public static final int MODIFIER_MIN_USER_SET_LOWER_BOUND = 0x20;
+    public static final int MODIFIER_STYLUS_UNDER_USE = 0x40;
     public static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR
-            | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND;
+            | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND
+            | MODIFIER_STYLUS_UNDER_USE;
 
     // ADJUSTMENT_*
     // These things can happen at any point, even if the main brightness reason doesn't
@@ -158,6 +160,9 @@
         if ((mModifier & MODIFIER_MIN_USER_SET_LOWER_BOUND) != 0) {
             sb.append(" user_min_pref");
         }
+        if ((mModifier & MODIFIER_STYLUS_UNDER_USE) != 0) {
+            sb.append(" stylus_under_use");
+        }
         int strlen = sb.length();
         if (sb.charAt(strlen - 1) == '[') {
             sb.setLength(strlen - 2);
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 71fdaf3..4bd9808 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -110,6 +110,9 @@
     @VisibleForTesting
     AutomaticBrightnessController mAutomaticBrightnessController;
 
+    // True if the stylus is being used
+    private boolean mIsStylusBeingUsed;
+
     /**
      * The constructor of DisplayBrightnessController.
      */
@@ -460,6 +463,8 @@
         writer.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
         writer.println("  mPersistBrightnessNitsForDefaultDisplay="
                 + mPersistBrightnessNitsForDefaultDisplay);
+        writer.println("  mIsStylusBeingUsed="
+                + mIsStylusBeingUsed);
         synchronized (mLock) {
             writer.println("  mPendingScreenBrightness=" + mPendingScreenBrightness);
             writer.println("  mCurrentScreenBrightness=" + mCurrentScreenBrightness);
@@ -505,7 +510,12 @@
      * Notifies if the stylus is currently being used or not.
      */
     public void setStylusBeingUsed(boolean isEnabled) {
-        // Todo(b/369977976) - Disable the auto-brightness strategy
+        mIsStylusBeingUsed = isEnabled;
+    }
+
+    @VisibleForTesting
+    boolean isStylusBeingUsed() {
+        return mIsStylusBeingUsed;
     }
 
     @VisibleForTesting
@@ -626,13 +636,14 @@
             lastUserSetScreenBrightness = mLastUserSetScreenBrightness;
         }
         return new StrategySelectionRequest(displayPowerRequest, targetDisplayState,
-                lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession);
+                lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession,
+                mIsStylusBeingUsed);
     }
 
     private StrategyExecutionRequest constructStrategyExecutionRequest(
             DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
         float currentScreenBrightness = getCurrentBrightness();
         return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness,
-                mUserSetScreenBrightnessUpdated);
+                mUserSetScreenBrightnessUpdated, mIsStylusBeingUsed);
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 06890e7..ded7447 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -306,7 +306,8 @@
                 strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze,
                 strategySelectionRequest.getLastUserSetScreenBrightness(),
                 strategySelectionRequest.isUserSetBrightnessChanged());
-        return mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
+        return !strategySelectionRequest.isStylusBeingUsed()
+                && mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
     }
 
     private StrategySelectionNotifyRequest constructStrategySelectionNotifyRequest(
diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
index 304640b..7a07c4f 100644
--- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
@@ -32,11 +32,15 @@
     // Represents if the user set screen brightness was changed or not.
     private boolean mUserSetBrightnessChanged;
 
+    private boolean mIsStylusBeingUsed;
+
     public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
-            float currentScreenBrightness, boolean userSetBrightnessChanged) {
+            float currentScreenBrightness, boolean userSetBrightnessChanged,
+            boolean isStylusBeingUsed) {
         mDisplayPowerRequest = displayPowerRequest;
         mCurrentScreenBrightness = currentScreenBrightness;
         mUserSetBrightnessChanged = userSetBrightnessChanged;
+        mIsStylusBeingUsed = isStylusBeingUsed;
     }
 
     public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -51,6 +55,10 @@
         return mUserSetBrightnessChanged;
     }
 
+    public boolean isStylusBeingUsed() {
+        return mIsStylusBeingUsed;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof StrategyExecutionRequest)) {
@@ -59,12 +67,13 @@
         StrategyExecutionRequest other = (StrategyExecutionRequest) obj;
         return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest())
                 && mCurrentScreenBrightness == other.getCurrentScreenBrightness()
-                && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged();
+                && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged()
+                && mIsStylusBeingUsed == other.isStylusBeingUsed();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness,
-                mUserSetBrightnessChanged);
+                mUserSetBrightnessChanged, mIsStylusBeingUsed);
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
index aa2f23e..5c1f03d 100644
--- a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
@@ -40,15 +40,19 @@
 
     private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
 
+    private boolean mIsStylusBeingUsed;
+
     public StrategySelectionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
             int targetDisplayState, float lastUserSetScreenBrightness,
             boolean userSetBrightnessChanged,
-            DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
+            DisplayManagerInternal.DisplayOffloadSession displayOffloadSession,
+            boolean isStylusBeingUsed) {
         mDisplayPowerRequest = displayPowerRequest;
         mTargetDisplayState = targetDisplayState;
         mLastUserSetScreenBrightness = lastUserSetScreenBrightness;
         mUserSetBrightnessChanged = userSetBrightnessChanged;
         mDisplayOffloadSession = displayOffloadSession;
+        mIsStylusBeingUsed = isStylusBeingUsed;
     }
 
     public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -72,6 +76,10 @@
         return mDisplayOffloadSession;
     }
 
+    public boolean isStylusBeingUsed() {
+        return mIsStylusBeingUsed;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof StrategySelectionRequest)) {
@@ -82,12 +90,14 @@
                 && mTargetDisplayState == other.getTargetDisplayState()
                 && mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness()
                 && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged()
-                && mDisplayOffloadSession.equals(other.getDisplayOffloadSession());
+                && mDisplayOffloadSession.equals(other.getDisplayOffloadSession())
+                && mIsStylusBeingUsed == other.isStylusBeingUsed();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mDisplayPowerRequest, mTargetDisplayState,
-                mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession);
+                mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession,
+                mIsStylusBeingUsed);
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
index 85e81f9..1a18b00 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
@@ -85,7 +85,7 @@
     private String mDataId = null;
     private float mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
     private float mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
-    private float mCustomAnimationRateSecDeviceConfig =
+    private float mCustomAnimationRateDeviceConfig =
                         DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
     private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
         try {
@@ -117,7 +117,7 @@
         };
         mPowerThrottlingConfigData = powerData.getPowerThrottlingConfigData();
         if (mPowerThrottlingConfigData != null) {
-            mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+            mCustomAnimationRateDeviceConfig = mPowerThrottlingConfigData.customAnimationRate;
         }
         mThermalLevelListener = new ThermalLevelListener(handler);
         mPmicMonitor =
@@ -228,10 +228,6 @@
         }
 
         mPowerThrottlingConfigData = data.getPowerThrottlingConfigData();
-        if (mPowerThrottlingConfigData == null) {
-            Slog.d(TAG,
-                    "Power throttling data is missing for configuration data.");
-        }
     }
 
     private void recalculateBrightnessCap() {
@@ -282,13 +278,13 @@
             mIsActive = isActive;
             Slog.i(TAG, "Power clamper changing current brightness cap mBrightnessCap: "
                     + mBrightnessCap + " to target brightness cap:" + targetBrightnessCap
-                    + " for current screen brightness: " + mCurrentBrightness);
-            mBrightnessCap = targetBrightnessCap;
-            Slog.i(TAG, "Power clamper changed state: thermalStatus:" + mCurrentThermalLevel
+                    + " for current screen brightness: " + mCurrentBrightness + "\n"
+                    + "Power clamper changed state: thermalStatus:" + mCurrentThermalLevel
                     + " mCurrentThermalLevelChanged:" + mCurrentThermalLevelChanged
                     + " mCurrentAvgPowerConsumed:" + mCurrentAvgPowerConsumed
-                    + " mCustomAnimationRateSec:" + mCustomAnimationRateSecDeviceConfig);
-            mCustomAnimationRateSec = mCustomAnimationRateSecDeviceConfig;
+                    + " mCustomAnimationRateSec:" + mCustomAnimationRateDeviceConfig);
+            mBrightnessCap = targetBrightnessCap;
+            mCustomAnimationRateSec = mCustomAnimationRateDeviceConfig;
             mChangeListener.onChanged();
         } else {
             mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
@@ -344,7 +340,7 @@
                     + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
             return;
         }
-        mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+        mCustomAnimationRateDeviceConfig = mPowerThrottlingConfigData.customAnimationRate;
         mThermalLevelListener.start();
     }
 
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
index 7c0c931..b9de34a 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -37,6 +37,9 @@
             StrategyExecutionRequest strategyExecutionRequest) {
         BrightnessReason brightnessReason = new BrightnessReason();
         brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+        if (strategyExecutionRequest.isStylusBeingUsed()) {
+            brightnessReason.setModifier(BrightnessReason.MODIFIER_STYLUS_UNDER_USE);
+        }
         return new DisplayBrightnessState.Builder()
                 .setBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
                 .setBrightnessReason(brightnessReason)
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 99ced7f..07343f4 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -217,11 +217,26 @@
             Flags::enableUserRefreshRateForExternalDisplay
     );
 
+    private final FlagState mEnableWaitingConfirmationBeforeMirroring = new FlagState(
+            Flags.FLAG_ENABLE_WAITING_CONFIRMATION_BEFORE_MIRRORING,
+            Flags::enableWaitingConfirmationBeforeMirroring
+    );
+
+    private final FlagState mEnableApplyDisplayChangedDuringDisplayAdded = new FlagState(
+            Flags.FLAG_ENABLE_APPLY_DISPLAY_CHANGED_DURING_DISPLAY_ADDED,
+            Flags::enableApplyDisplayChangedDuringDisplayAdded
+    );
+
     private final FlagState mEnableBatteryStatsForAllDisplays = new FlagState(
             Flags.FLAG_ENABLE_BATTERY_STATS_FOR_ALL_DISPLAYS,
             Flags::enableBatteryStatsForAllDisplays
     );
 
+    private final FlagState mHasArrSupport = new FlagState(
+            Flags.FLAG_ENABLE_HAS_ARR_SUPPORT,
+            Flags::enableHasArrSupport
+    );
+
     /**
      * @return {@code true} if 'port' is allowed in display layout configuration file.
      */
@@ -445,6 +460,14 @@
     }
 
     /**
+      * @return {@code true} if mirroring won't be enabled until boot completes and the user enables
+      * the display.
+      */
+    public boolean isWaitingConfirmationBeforeMirroringEnabled() {
+        return mEnableWaitingConfirmationBeforeMirroring.isEnabled();
+    }
+
+    /**
       * @return {@code true} if battery stats is enabled for all displays, not just the primary
       * display.
       */
@@ -453,6 +476,13 @@
     }
 
     /**
+      * @return {@code true} if need to apply display changes during display added event.
+      */
+    public boolean isApplyDisplayChangedDuringDisplayAddedEnabled() {
+        return mEnableApplyDisplayChangedDuringDisplayAdded.isEnabled();
+    }
+
+    /**
      * @return {@code true} if autobrightness is to be blocked when stylus is being used
      */
     public boolean isBlockAutobrightnessChangesOnStylusUsage() {
@@ -468,6 +498,12 @@
     }
 
     /**
+     * @return {@code true} if hasArrSupport API is enabled.
+     */
+    public boolean hasArrSupportFlag() {
+        return mHasArrSupport.isEnabled();
+    }
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
@@ -511,9 +547,12 @@
         pw.println(" " + mVirtualDisplayLimit);
         pw.println(" " + mNormalBrightnessForDozeParameter);
         pw.println(" " + mIdleScreenConfigInSubscribingLightSensor);
+        pw.println(" " + mEnableWaitingConfirmationBeforeMirroring);
         pw.println(" " + mEnableBatteryStatsForAllDisplays);
+        pw.println(" " + mEnableApplyDisplayChangedDuringDisplayAdded);
         pw.println(" " + mBlockAutobrightnessChangesOnStylusUsage);
         pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled);
+        pw.println(" " + mHasArrSupport);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 2f04d9e..ddb2969 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -367,6 +367,17 @@
 }
 
 flag {
+    name: "enable_waiting_confirmation_before_mirroring"
+    namespace: "display_manager"
+    description: "Allow ContentRecorder checking whether user confirmed mirroring after boot"
+    bug: "361698995"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_battery_stats_for_all_displays"
     namespace: "display_manager"
     description: "Flag to enable battery stats for all displays."
@@ -375,6 +386,17 @@
 }
 
 flag {
+    name: "enable_apply_display_changed_during_display_added"
+    namespace: "display_manager"
+    description: "Apply display changes after display added"
+    bug: "368131655"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "block_autobrightness_changes_on_stylus_usage"
     namespace: "display_manager"
     description: "Block the usage of ALS to control the display brightness when stylus is being used"
@@ -392,3 +414,11 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "enable_has_arr_support"
+    namespace: "core_graphics"
+    description: "Flag for an API to get whether display supports ARR or not"
+    bug: "361433651"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index ffa64bf..88562ab 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -156,6 +156,8 @@
     // a map from display id to display device config
     private SparseArray<DisplayDeviceConfig> mDisplayDeviceConfigByDisplay = new SparseArray<>();
 
+    private SparseBooleanArray mHasArrSupport;
+
     private BrightnessObserver mBrightnessObserver;
 
     private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
@@ -194,6 +196,8 @@
 
     private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled;
 
+    private final boolean mHasArrSupportFlagEnabled;
+
     private final DisplayManagerFlags mDisplayManagerFlags;
 
     private final DisplayDeviceConfigProvider mDisplayDeviceConfigProvider;
@@ -218,6 +222,7 @@
             .isDisplaysRefreshRatesSynchronizationEnabled();
         mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags
                 .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled();
+        mHasArrSupportFlagEnabled = displayManagerFlags.hasArrSupportFlag();
         mDisplayManagerFlags = displayManagerFlags;
         mDisplayDeviceConfigProvider = displayDeviceConfigProvider;
         mContext = context;
@@ -228,6 +233,7 @@
         mSupportedModesByDisplay = new SparseArray<>();
         mAppSupportedModesByDisplay = new SparseArray<>();
         mDefaultModeByDisplay = new SparseArray<>();
+        mHasArrSupport = new SparseBooleanArray();
         mAppRequestObserver = new AppRequestObserver(displayManagerFlags);
         mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
@@ -452,7 +458,13 @@
         return mAppRequestObserver;
     }
 
+    // TODO(b/372019752) Rename all the occurrences of the VRR with ARR.
     private boolean isVrrSupportedLocked(int displayId) {
+        if (mHasArrSupportFlagEnabled) {
+            Boolean hasArrSupport = mHasArrSupport.get(displayId);
+            return hasArrSupport != null && hasArrSupport;
+        }
+        // TODO(b/371041638) Remove config.isVrrSupportEnabled once hasArrSupport is rolled out
         DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.get(displayId);
         return config != null && config.isVrrSupportEnabled();
     }
@@ -1469,6 +1481,7 @@
             DisplayInfo displayInfo = getDisplayInfo(displayId);
             registerExternalDisplay(displayInfo);
             updateDisplayModes(displayId, displayInfo);
+            updateHasArrSupport(displayId, displayInfo);
             updateLayoutLimitedFrameRate(displayId, displayInfo);
             updateUserSettingDisplayPreferredSize(displayInfo);
             updateDisplaysPeakRefreshRateAndResolution(displayInfo);
@@ -1482,6 +1495,7 @@
                 mDefaultModeByDisplay.remove(displayId);
                 mDisplayDeviceConfigByDisplay.remove(displayId);
                 mSettingsObserver.removeRefreshRateSetting(displayId);
+                mHasArrSupport.delete(displayId);
             }
             updateLayoutLimitedFrameRate(displayId, null);
             removeUserSettingDisplayPreferredSize(displayId);
@@ -1493,6 +1507,7 @@
         public void onDisplayChanged(int displayId) {
             updateDisplayDeviceConfig(displayId);
             DisplayInfo displayInfo = getDisplayInfo(displayId);
+            updateHasArrSupport(displayId, displayInfo);
             updateDisplayModes(displayId, displayInfo);
             updateLayoutLimitedFrameRate(displayId, displayInfo);
             updateUserSettingDisplayPreferredSize(displayInfo);
@@ -1691,6 +1706,16 @@
                 }
             }
         }
+
+        private void updateHasArrSupport(int displayId, @Nullable DisplayInfo info) {
+            if (info == null) {
+                return;
+            }
+            synchronized (mLock) {
+                mHasArrSupport.put(displayId, info.hasArrSupport);
+            }
+        }
+
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
index a83b939..71b34679 100644
--- a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
+++ b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
@@ -37,17 +37,22 @@
             SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE;
 
     private final boolean mSynthetic60HzModesEnabled;
+    private final boolean mHasArrSupportEnabled;
 
     public SyntheticModeManager(DisplayManagerFlags flags) {
         mSynthetic60HzModesEnabled = flags.isSynthetic60HzModesEnabled();
+        mHasArrSupportEnabled = flags.hasArrSupportFlag();
     }
 
     /**
      * creates display supportedModes array, exposed to applications
      */
     public Display.Mode[] createAppSupportedModes(DisplayDeviceConfig config,
-            Display.Mode[] modes) {
-        if (!config.isVrrSupportEnabled() || !mSynthetic60HzModesEnabled) {
+            Display.Mode[] modes, boolean hasArrSupport) {
+        // TODO(b/361433651) Remove config.isVrrSupportEnabled once hasArrSupport is rolled out
+        boolean isArrSupported =
+                mHasArrSupportEnabled ? hasArrSupport : config.isVrrSupportEnabled();
+        if (!isArrSupported || !mSynthetic60HzModesEnabled) {
             return modes;
         }
         List<Display.Mode> appSupportedModes = new ArrayList<>();
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index d70bd8b..d1a6d3b 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -63,6 +63,12 @@
         mObservers = Map.ofEntries(
                 Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SPEED),
                         (reason) -> updateMousePointerSpeed()),
+                Map.entry(Settings.System.getUriFor(
+                        Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING),
+                        (reason) -> updateMouseReverseVerticalScrolling()),
+                Map.entry(Settings.System.getUriFor(
+                                Settings.System.MOUSE_SWAP_PRIMARY_BUTTON),
+                        (reason) -> updateMouseSwapPrimaryButton()),
                 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED),
                         (reason) -> updateTouchpadPointerSpeed()),
                 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING),
@@ -163,6 +169,16 @@
         mNative.setPointerSpeed(constrainPointerSpeedValue(speed));
     }
 
+    private void updateMouseReverseVerticalScrolling() {
+        mNative.setMouseReverseVerticalScrollingEnabled(
+                InputSettings.isMouseReverseVerticalScrollingEnabled(mContext));
+    }
+
+    private void updateMouseSwapPrimaryButton() {
+        mNative.setMouseSwapPrimaryButtonEnabled(
+                InputSettings.isMouseSwapPrimaryButtonEnabled(mContext));
+    }
+
     private void updateTouchpadPointerSpeed() {
         mNative.setTouchpadPointerSpeed(
                 constrainPointerSpeedValue(InputSettings.getTouchpadPointerSpeed(mContext)));
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 4404d63..21e8bcc 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -127,6 +127,10 @@
 
     void setMousePointerAccelerationEnabled(int displayId, boolean enabled);
 
+    void setMouseReverseVerticalScrollingEnabled(boolean enabled);
+
+    void setMouseSwapPrimaryButtonEnabled(boolean enabled);
+
     void setTouchpadPointerSpeed(int speed);
 
     void setTouchpadNaturalScrollingEnabled(boolean enabled);
@@ -388,6 +392,12 @@
         public native void setMousePointerAccelerationEnabled(int displayId, boolean enabled);
 
         @Override
+        public native void setMouseReverseVerticalScrollingEnabled(boolean enabled);
+
+        @Override
+        public native void setMouseSwapPrimaryButtonEnabled(boolean enabled);
+
+        @Override
         public native void setTouchpadPointerSpeed(int speed);
 
         @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index f0fb33e..83044c2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -84,6 +84,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
 import android.inputmethodservice.InputMethodService;
 import android.inputmethodservice.InputMethodService.BackDispositionMode;
@@ -119,6 +120,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -185,7 +187,6 @@
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
-import com.android.server.inputmethod.InputMethodMenuControllerNew.MenuItem;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
@@ -448,6 +449,8 @@
     private AudioManagerInternal mAudioManagerInternal = null;
     @Nullable
     private VirtualDeviceManagerInternal mVdmInternal = null;
+    @Nullable
+    private DisplayManagerInternal mDisplayManagerInternal = null;
 
     // Mapping from deviceId to the device-specific imeId for that device.
     @GuardedBy("ImfLock.class")
@@ -1533,7 +1536,16 @@
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        return settings.getMethodMap().get(settings.getSelectedInputMethod());
+        final String selectedImeId;
+        if (Flags.consistentGetCurrentInputMethodInfo()) {
+            final var bindingController = getInputMethodBindingController(userId);
+            synchronized (ImfLock.class) {
+                selectedImeId = bindingController.getSelectedMethodId();
+            }
+        } else {
+            selectedImeId = settings.getSelectedInputMethod();
+        }
+        return settings.getMethodMap().get(selectedImeId);
     }
 
     @BinderThread
@@ -2165,7 +2177,18 @@
         final var bindingController = getInputMethodBindingController(userId);
         final int oldDeviceId = bindingController.getDeviceIdToShowIme();
         final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
-        final int newDeviceId = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme);
+        int newDeviceId = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme);
+        if (newDeviceId != DEVICE_ID_DEFAULT) {
+            // Only show custom IME on trusted displays.
+            if (mDisplayManagerInternal == null) {
+                mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+            }
+            int displayFlags = mDisplayManagerInternal.getDisplayInfo(displayIdToShowIme).flags;
+            if ((displayFlags & Display.FLAG_TRUSTED) != Display.FLAG_TRUSTED) {
+                // If the display is not trusted, fallback to the default device IME.
+                newDeviceId = DEVICE_ID_DEFAULT;
+            }
+        }
         bindingController.setDeviceIdToShowIme(newDeviceId);
         if (newDeviceId == DEVICE_ID_DEFAULT) {
             if (oldDeviceId == DEVICE_ID_DEFAULT) {
@@ -4063,65 +4086,6 @@
         }
     }
 
-    /**
-     * Gets the list of Input Method Switcher Menu items and the index of the selected item.
-     *
-     * @param items                the list of input method and subtype items.
-     * @param selectedImeId        the ID of the selected input method.
-     * @param selectedSubtypeIndex the index of the selected subtype in the input method's array of
-     *                             subtypes, or {@link InputMethodUtils#NOT_A_SUBTYPE_INDEX} if no
-     *                             subtype is selected.
-     * @param userId               the ID of the user for which to get the menu items.
-     * @return the list of menu items, and the index of the selected item,
-     * or {@code -1} if no item is selected.
-     */
-    @GuardedBy("ImfLock.class")
-    @NonNull
-    private Pair<List<MenuItem>, Integer> getInputMethodPickerItems(
-            @NonNull List<ImeSubtypeListItem> items, @Nullable String selectedImeId,
-            int selectedSubtypeIndex, @UserIdInt int userId) {
-        final var bindingController = getInputMethodBindingController(userId);
-        final var settings = InputMethodSettingsRepository.get(userId);
-
-        if (selectedSubtypeIndex == NOT_A_SUBTYPE_INDEX) {
-            // TODO(b/351124299): Check if this fallback logic is still necessary.
-            final var curSubtype = bindingController.getCurrentInputMethodSubtype();
-            if (curSubtype != null) {
-                final var curMethodId = bindingController.getSelectedMethodId();
-                final var curImi = settings.getMethodMap().get(curMethodId);
-                selectedSubtypeIndex = SubtypeUtils.getSubtypeIndexFromHashCode(
-                        curImi, curSubtype.hashCode());
-            }
-        }
-
-        // No item is selected by default. When we have a list of explicitly enabled
-        // subtypes, the implicit subtype is no longer listed. If the implicit one
-        // is still selected, no items will be shown as selected.
-        int selectedIndex = -1;
-        String prevImeId = null;
-        final var menuItems = new ArrayList<MenuItem>();
-        for (int i = 0; i < items.size(); i++) {
-            final var item = items.get(i);
-            final var imeId = item.mImi.getId();
-            if (imeId.equals(selectedImeId)) {
-                final int subtypeIndex = item.mSubtypeIndex;
-                // Check if this is the selected IME-subtype pair.
-                if ((subtypeIndex == 0 && selectedSubtypeIndex == NOT_A_SUBTYPE_INDEX)
-                        || subtypeIndex == NOT_A_SUBTYPE_INDEX
-                        || subtypeIndex == selectedSubtypeIndex) {
-                    selectedIndex = i;
-                }
-            }
-            final boolean hasHeader = !imeId.equals(prevImeId);
-            final boolean hasDivider = hasHeader && prevImeId != null;
-            prevImeId = imeId;
-            menuItems.add(new MenuItem(item.mImeName, item.mSubtypeName, item.mImi,
-                    item.mSubtypeIndex, hasHeader, hasDivider));
-        }
-
-        return new Pair<>(menuItems, selectedIndex);
-    }
-
     @IInputMethodManagerImpl.PermissionVerified(allOf = {
             Manifest.permission.INTERACT_ACROSS_USERS_FULL,
             Manifest.permission.WRITE_SECURE_SETTINGS})
@@ -4958,18 +4922,21 @@
                         + " preferredInputMethodSubtypeIndex=" + lastInputMethodSubtypeIndex);
             }
 
-            final var itemsAndIndex = getInputMethodPickerItems(imList,
-                    lastInputMethodId, lastInputMethodSubtypeIndex, userId);
-            final var menuItems = itemsAndIndex.first;
-            final int selectedIndex = itemsAndIndex.second;
-
-            if (selectedIndex == -1) {
-                Slog.w(TAG, "Switching menu shown with no item selected"
-                        + ", IME id: " + lastInputMethodId
-                        + ", subtype index: " + lastInputMethodSubtypeIndex);
+            int selectedSubtypeIndex = lastInputMethodSubtypeIndex;
+            if (selectedSubtypeIndex == NOT_A_SUBTYPE_INDEX) {
+                // TODO(b/351124299): Check if this fallback logic is still necessary.
+                final var bindingController = getInputMethodBindingController(userId);
+                final var curSubtype = bindingController.getCurrentInputMethodSubtype();
+                if (curSubtype != null) {
+                    final var curMethodId = bindingController.getSelectedMethodId();
+                    final var curImi = settings.getMethodMap().get(curMethodId);
+                    selectedSubtypeIndex = SubtypeUtils.getSubtypeIndexFromHashCode(
+                            curImi, curSubtype.hashCode());
+                }
             }
 
-            mMenuControllerNew.show(menuItems, selectedIndex, displayId, userId);
+            mMenuControllerNew.show(imList, lastInputMethodId, selectedSubtypeIndex, displayId,
+                    userId);
         } else {
             mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId,
                     lastInputMethodId, lastInputMethodSubtypeIndex, imList, userId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
index cf2cdc1..1d0e3c6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
@@ -30,7 +30,6 @@
 import android.annotation.UserIdInt;
 import android.app.AlertDialog;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -48,8 +47,11 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.RecyclerView;
+import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -80,18 +82,27 @@
     /**
      * Shows the Input Method Switcher Menu, with a list of IMEs and their subtypes.
      *
-     * @param items         the list of menu items.
-     * @param selectedIndex the index of the menu item that is selected.
-     *                      If no other IMEs are enabled, this index will be out of reach.
-     * @param displayId     the ID of the display where the menu was requested.
-     * @param userId        the ID of the user that requested the menu.
+     * @param items                the list of input method and subtype items.
+     * @param selectedImeId        the ID of the selected input method.
+     * @param selectedSubtypeIndex the index of the selected subtype in the input method's array of
+     *                             subtypes, or {@link InputMethodUtils#NOT_A_SUBTYPE_INDEX} if no
+     *                             subtype is selected.
+     * @param displayId            the ID of the display where the menu was requested.
+     * @param userId               the ID of the user that requested the menu.
      */
     @RequiresPermission(allOf = {INTERACT_ACROSS_USERS, HIDE_OVERLAY_WINDOWS})
-    void show(@NonNull List<MenuItem> items, int selectedIndex, int displayId,
-            @UserIdInt int userId) {
+    void show(@NonNull List<ImeSubtypeListItem> items, @Nullable String selectedImeId,
+            int selectedSubtypeIndex, int displayId, @UserIdInt int userId) {
         // Hide the menu in case it was already showing.
         hide(displayId, userId);
 
+        final var menuItems = getMenuItems(items);
+        final int selectedIndex = getSelectedIndex(menuItems, selectedImeId, selectedSubtypeIndex);
+        if (selectedIndex == -1) {
+            Slog.w(TAG, "Switching menu shown with no item selected, IME id: " + selectedImeId
+                    + ", subtype index: " + selectedSubtypeIndex);
+        }
+
         final Context dialogWindowContext = mDialogWindowContext.get(displayId);
         final var builder = new AlertDialog.Builder(dialogWindowContext,
                 com.android.internal.R.style.Theme_DeviceDefault_InputMethodSwitcherDialog);
@@ -104,52 +115,28 @@
                 dialogWindowContext.getText(com.android.internal.R.string.select_input_method));
         builder.setView(contentView);
 
-        final DialogInterface.OnClickListener onClickListener = (dialog, which) -> {
-            if (which != selectedIndex) {
-                final var item = items.get(which);
+        final OnClickListener onClickListener = (item, isSelected) -> {
+            if (!isSelected) {
                 InputMethodManagerInternal.get()
                         .switchToInputMethod(item.mImi.getId(), item.mSubtypeIndex, userId);
             }
             hide(displayId, userId);
         };
 
-        final var selectedImi = selectedIndex >= 0 ? items.get(selectedIndex).mImi : null;
-        final var languageSettingsIntent = selectedImi != null
-                ? selectedImi.createImeLanguageSettingsActivityIntent() : null;
-        final boolean isDeviceProvisioned = Settings.Global.getInt(
-                dialogWindowContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
-                0) != 0;
-        final boolean hasLanguageSettingsButton = languageSettingsIntent != null
-                && isDeviceProvisioned;
-        if (hasLanguageSettingsButton) {
-            final View buttonBar = contentView
-                    .requireViewById(com.android.internal.R.id.button_bar);
-            buttonBar.setVisibility(View.VISIBLE);
-
-            languageSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            final Button languageSettingsButton = contentView
-                    .requireViewById(com.android.internal.R.id.button1);
-            languageSettingsButton.setVisibility(View.VISIBLE);
-            languageSettingsButton.setOnClickListener(v -> {
-                v.getContext().startActivityAsUser(languageSettingsIntent, UserHandle.of(userId));
-                hide(displayId, userId);
-            });
-        }
-
         // Create the current IME subtypes list.
         final RecyclerView recyclerView = contentView
                 .requireViewById(com.android.internal.R.id.list);
-        recyclerView.setAdapter(new Adapter(items, selectedIndex, inflater, onClickListener));
+        recyclerView.setAdapter(new Adapter(menuItems, selectedIndex, inflater, onClickListener));
         // Scroll to the currently selected IME. This must run after the recycler view is laid out.
         recyclerView.post(() -> recyclerView.scrollToPosition(selectedIndex));
-        // Indicate that the list can be scrolled.
-        recyclerView.setScrollIndicators(
-                hasLanguageSettingsButton ? View.SCROLL_INDICATOR_BOTTOM : 0);
         // Request focus to enable rotary scrolling on watches.
         recyclerView.requestFocus();
 
+        final var selectedItem = selectedIndex > -1 ? menuItems.get(selectedIndex) : null;
+        updateLanguageSettingsButton(selectedItem, contentView, displayId, userId);
+
         builder.setOnCancelListener(dialog -> hide(displayId, userId));
-        mMenuItems = items;
+        mMenuItems = menuItems;
         mDialog = builder.create();
         mDialog.setCanceledOnTouchOutside(true);
         final Window w = mDialog.getWindow();
@@ -208,98 +195,303 @@
     }
 
     /**
-     * Item to be shown in the Input Method Switcher Menu, containing an input method and
-     * optionally an input method subtype.
+     * Creates the list of menu items from the given list of input methods and subtypes. This
+     * handles adding headers and dividers between groups of items from different input methods
+     * as follows:
+     *
+     * <li>If there is only one group, no divider or header will be added.</li>
+     * <li>A divider is added before each group, except the first one.</li>
+     * <li>A header is added before each group (after the divider, if it exists) if the group has
+     * at least two items, or a single item with a subtype name.</li>
+     *
+     * @param items the list of input method and subtype items.
      */
-    static class MenuItem {
+    @VisibleForTesting
+    @NonNull
+    static List<MenuItem> getMenuItems(@NonNull List<ImeSubtypeListItem> items) {
+        final var menuItems = new ArrayList<MenuItem>();
+        if (items.isEmpty()) {
+            return menuItems;
+        }
+
+        final var itemsArray = (ArrayList<ImeSubtypeListItem>) items;
+        final int numItems = itemsArray.size();
+        // Initialize to the last IME id to avoid headers if there is only a single IME.
+        String prevImeId = itemsArray.getLast().mImi.getId();
+        boolean firstGroup = true;
+        for (int i = 0; i < numItems; i++) {
+            final var item = itemsArray.get(i);
+
+            final var imeId = item.mImi.getId();
+            final boolean groupChange = !imeId.equals(prevImeId);
+            if (groupChange) {
+                if (!firstGroup) {
+                    menuItems.add(DividerItem.getInstance());
+                }
+                // Add a header if we have at least two items, or a single item with a subtype name.
+                final var nextItemId = i + 1 < numItems ? itemsArray.get(i + 1).mImi.getId() : null;
+                final boolean addHeader = item.mSubtypeName != null || imeId.equals(nextItemId);
+                if (addHeader) {
+                    menuItems.add(new HeaderItem(item.mImeName));
+                }
+                firstGroup = false;
+                prevImeId = imeId;
+            }
+
+            menuItems.add(new SubtypeItem(item.mImeName, item.mSubtypeName, item.mImi,
+                    item.mSubtypeIndex));
+        }
+
+        return menuItems;
+    }
+
+    /**
+     * Gets the index of the selected item.
+     *
+     * @param items                the list of menu items.
+     * @param selectedImeId        the ID of the selected input method.
+     * @param selectedSubtypeIndex the index of the selected subtype in the input method's array of
+     *                             subtypes, or {@link InputMethodUtils#NOT_A_SUBTYPE_INDEX} if no
+     *                             subtype is selected.
+     * @return the index of the selected item, or {@code -1} if no item is selected.
+     */
+    @VisibleForTesting
+    @IntRange(from = -1)
+    static int getSelectedIndex(@NonNull List<MenuItem> items, @Nullable String selectedImeId,
+            int selectedSubtypeIndex) {
+        for (int i = 0; i < items.size(); i++) {
+            final var item = items.get(i);
+            if (item instanceof SubtypeItem subtypeItem) {
+                final var imeId = subtypeItem.mImi.getId();
+                final int subtypeIndex = subtypeItem.mSubtypeIndex;
+                if (imeId.equals(selectedImeId)
+                        && ((subtypeIndex == 0 && selectedSubtypeIndex == NOT_A_SUBTYPE_INDEX)
+                            || subtypeIndex == NOT_A_SUBTYPE_INDEX
+                            || subtypeIndex == selectedSubtypeIndex)) {
+                    return i;
+                }
+            }
+        }
+        // Either there is no selected IME, or the selected subtype is enabled but not in the list.
+        // This can happen if an implicit subtype is selected, but we got a list of explicit
+        // subtypes. In this case, the implicit subtype will no longer be included in the list.
+        return -1;
+    }
+
+    /**
+     * Updates the visibility of the Language Settings button to visible if the currently selected
+     * item specifies a (language) settings activity and the device is provisioned. Otherwise,
+     * the button won't be shown.
+     *
+     * @param selectedItem the currently selected item, or {@code null} if no item is selected.
+     * @param view         the menu dialog view.
+     * @param displayId    the ID of the display where the menu was requested.
+     * @param userId       the ID of the user that requested the menu.
+     */
+    @RequiresPermission(allOf = {INTERACT_ACROSS_USERS})
+    private void updateLanguageSettingsButton(@Nullable MenuItem selectedItem, @NonNull View view,
+            int displayId, @UserIdInt int userId) {
+        final var settingsIntent = (selectedItem instanceof SubtypeItem selectedSubtypeItem)
+                ? selectedSubtypeItem.mImi.createImeLanguageSettingsActivityIntent() : null;
+        final boolean isDeviceProvisioned = Settings.Global.getInt(
+                view.getContext().getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+                0) != 0;
+        final boolean hasButton = settingsIntent != null && isDeviceProvisioned;
+        final View buttonBar = view.requireViewById(com.android.internal.R.id.button_bar);
+        final Button button = view.requireViewById(com.android.internal.R.id.button1);
+        final RecyclerView recyclerView = view.requireViewById(com.android.internal.R.id.list);
+        if (hasButton) {
+            settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            buttonBar.setVisibility(View.VISIBLE);
+            button.setOnClickListener(v -> {
+                v.getContext().startActivityAsUser(settingsIntent, UserHandle.of(userId));
+                hide(displayId, userId);
+            });
+            // Indicate that the list can be scrolled.
+            recyclerView.setScrollIndicators(View.SCROLL_INDICATOR_BOTTOM);
+        } else {
+            buttonBar.setVisibility(View.GONE);
+            button.setOnClickListener(null);
+            // Remove scroll indicator as there is nothing drawn below the list.
+            recyclerView.setScrollIndicators(0 /* indicators */);
+        }
+    }
+
+    /**
+     * Interface definition for callbacks to be invoked when a {@link SubtypeItem} is clicked.
+     */
+    private interface OnClickListener {
+
+        /**
+         * Called when an item is clicked.
+         *
+         * @param item       The item that was clicked.
+         * @param isSelected Whether the item is the currently selected one.
+         */
+        void onClick(@NonNull SubtypeItem item, boolean isSelected);
+    }
+
+    /** Item to be displayed in the menu. */
+    sealed interface MenuItem {}
+
+    /** Subtype item containing an input method and optionally an input method subtype. */
+    static final class SubtypeItem implements MenuItem {
 
         /** The name of the input method. */
         @NonNull
-        private final CharSequence mImeName;
+        final CharSequence mImeName;
 
         /**
          * The name of the input method subtype, or {@code null} if this item doesn't have a
          * subtype.
          */
         @Nullable
-        private final CharSequence mSubtypeName;
+        final CharSequence mSubtypeName;
 
         /** The info of the input method. */
         @NonNull
-        private final InputMethodInfo mImi;
+        final InputMethodInfo mImi;
 
         /**
          * The index of the subtype in the input method's array of subtypes,
          * or {@link InputMethodUtils#NOT_A_SUBTYPE_INDEX} if this item doesn't have a subtype.
          */
         @IntRange(from = NOT_A_SUBTYPE_INDEX)
-        private final int mSubtypeIndex;
+        final int mSubtypeIndex;
 
-        /** Whether this item has a group header (only the first item of each input method). */
-        private final boolean mHasHeader;
-
-        /**
-         * Whether this item should has a group divider (same as {@link #mHasHeader},
-         * excluding the first IME).
-         */
-        private final boolean mHasDivider;
-
-        MenuItem(@NonNull CharSequence imeName, @Nullable CharSequence subtypeName,
+        SubtypeItem(@NonNull CharSequence imeName, @Nullable CharSequence subtypeName,
                 @NonNull InputMethodInfo imi,
-                @IntRange(from = NOT_A_SUBTYPE_INDEX) int subtypeIndex, boolean hasHeader,
-                boolean hasDivider) {
+                @IntRange(from = NOT_A_SUBTYPE_INDEX) int subtypeIndex) {
             mImeName = imeName;
             mSubtypeName = subtypeName;
             mImi = imi;
             mSubtypeIndex = subtypeIndex;
-            mHasHeader = hasHeader;
-            mHasDivider = hasDivider;
         }
 
         @Override
         public String toString() {
-            return "MenuItem{"
+            return "SubtypeItem{"
                     + "mImeName=" + mImeName
                     + " mSubtypeName=" + mSubtypeName
                     + " mSubtypeIndex=" + mSubtypeIndex
-                    + " mHasHeader=" + mHasHeader
-                    + " mHasDivider=" + mHasDivider
                     + "}";
         }
     }
 
-    private static class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
+    /** Header item displayed before a group of {@link SubtypeItem} of the same input method. */
+    static final class HeaderItem implements MenuItem {
+
+        /** The header title. */
+        @NonNull
+        final CharSequence mTitle;
+
+        HeaderItem(@NonNull CharSequence title) {
+            mTitle = title;
+        }
+
+        @Override
+        public String toString() {
+            return "HeaderItem{"
+                    + "mTitle=" + mTitle
+                    + "}";
+        }
+    }
+
+    /** Divider item displayed before a {@link HeaderItem}. */
+    static final class DividerItem implements MenuItem {
+
+        private static DividerItem sInstance;
+
+        /** Gets a singleton instance of DividerItem. */
+        @NonNull
+        static DividerItem getInstance() {
+            if (sInstance == null) {
+                sInstance = new DividerItem();
+            }
+            return sInstance;
+        }
+
+        @Override
+        public String toString() {
+            return "DividerItem{}";
+        }
+    }
+
+    private static final class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+        /** View type for unknown item. */
+        private static final int TYPE_UNKNOWN = -1;
+
+        /** View type for {@link SubtypeItem}. */
+        private static final int TYPE_SUBTYPE = 0;
+
+        /** View type for {@link HeaderItem}. */
+        private static final int TYPE_HEADER = 1;
+
+        /** View type for {@link DividerItem}. */
+        private static final int TYPE_DIVIDER = 2;
 
         /** The list of items to show. */
         @NonNull
         private final List<MenuItem> mItems;
         /** The index of the selected item. */
+        @IntRange(from = -1)
         private final int mSelectedIndex;
         @NonNull
         private final LayoutInflater mInflater;
+        /** The listener used to handle clicks on {@link SubtypeViewHolder} items. */
         @NonNull
-        private final DialogInterface.OnClickListener mOnClickListener;
+        private final OnClickListener mListener;
 
-        Adapter(@NonNull List<MenuItem> items, int selectedIndex,
+        Adapter(@NonNull List<MenuItem> items, @IntRange(from = -1) int selectedIndex,
                 @NonNull LayoutInflater inflater,
-                @NonNull DialogInterface.OnClickListener onClickListener) {
+                @NonNull OnClickListener listener) {
             mItems = items;
             mSelectedIndex = selectedIndex;
             mInflater = inflater;
-            mOnClickListener = onClickListener;
+            mListener = listener;
         }
 
         @Override
-        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            final View view = mInflater.inflate(
-                    com.android.internal.R.layout.input_method_switch_item_new, parent, false);
-
-            return new ViewHolder(view, mOnClickListener);
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            switch (viewType) {
+                case TYPE_SUBTYPE -> {
+                    final View view = mInflater.inflate(
+                            com.android.internal.R.layout.input_method_switch_item_new, parent,
+                            false);
+                    return new SubtypeViewHolder(view, mListener);
+                }
+                case TYPE_HEADER -> {
+                    final View view = mInflater.inflate(
+                            com.android.internal.R.layout.input_method_switch_item_header, parent,
+                            false);
+                    return new HeaderViewHolder(view);
+                }
+                case TYPE_DIVIDER -> {
+                    final View view = mInflater.inflate(
+                            com.android.internal.R.layout.input_method_switch_item_divider, parent,
+                            false);
+                    return new DividerViewHolder(view);
+                }
+                default -> throw new IllegalArgumentException("Unknown viewType: " + viewType);
+            }
         }
 
         @Override
-        public void onBindViewHolder(ViewHolder holder, int position) {
-            holder.bind(mItems.get(position), position == mSelectedIndex /* isSelected */);
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+            final var item = mItems.get(position);
+            if (holder instanceof SubtypeViewHolder subtypeHolder
+                    && item instanceof SubtypeItem subtypeItem) {
+                subtypeHolder.bind(subtypeItem, position == mSelectedIndex /* isSelected */);
+            } else if (holder instanceof HeaderViewHolder headerHolder
+                    && item instanceof HeaderItem headerItem) {
+                headerHolder.bind(headerItem);
+            } else if (holder instanceof DividerViewHolder && item instanceof DividerItem) {
+                // Nothing to bind for dividers.
+                return;
+            } else {
+                Slog.w(TAG, "Holder type: " + holder + " doesn't match item type: " + item);
+            }
         }
 
         @Override
@@ -307,7 +499,21 @@
             return mItems.size();
         }
 
-        private static class ViewHolder extends RecyclerView.ViewHolder {
+        @Override
+        public int getItemViewType(int position) {
+            final var item = mItems.get(position);
+            if (item instanceof SubtypeItem) {
+                return TYPE_SUBTYPE;
+            } else if (item instanceof HeaderItem) {
+                return TYPE_HEADER;
+            } else if (item instanceof DividerItem) {
+                return TYPE_DIVIDER;
+            } else {
+                return TYPE_UNKNOWN;
+            }
+        }
+
+        private static final class SubtypeViewHolder extends RecyclerView.ViewHolder {
 
             /** The container of the item. */
             @NonNull
@@ -318,46 +524,74 @@
             /** Indicator for the selected status of the item. */
             @NonNull
             private final ImageView mCheckmark;
-            /** The group header optionally drawn above the item. */
-            @NonNull
-            private final TextView mHeader;
-            /** The group divider optionally drawn above the item. */
-            @NonNull
-            private final View mDivider;
 
-            private ViewHolder(@NonNull View itemView,
-                    @NonNull DialogInterface.OnClickListener onClickListener) {
+            /** The bound item data, or {@code null} if no item was bound yet. */
+            @Nullable
+            private SubtypeItem mItem;
+            /** Whether this item is the currently selected one. */
+            private boolean mIsSelected;
+
+            SubtypeViewHolder(@NonNull View itemView, @NonNull OnClickListener listener) {
                 super(itemView);
 
-                mContainer = itemView.requireViewById(com.android.internal.R.id.list_item);
+                mContainer = itemView;
                 mName = itemView.requireViewById(com.android.internal.R.id.text);
                 mCheckmark = itemView.requireViewById(com.android.internal.R.id.image);
-                mHeader = itemView.requireViewById(com.android.internal.R.id.header_text);
-                mDivider = itemView.requireViewById(com.android.internal.R.id.divider);
 
-                mContainer.setOnClickListener((v) ->
-                        onClickListener.onClick(null /* dialog */, getAdapterPosition()));
+                mContainer.setOnClickListener((v) -> {
+                    if (mItem != null) {
+                        listener.onClick(mItem, mIsSelected);
+                    }
+                });
             }
 
             /**
              * Binds the given item to the current view.
              *
              * @param item       the item to bind.
-             * @param isSelected whether this is selected.
+             * @param isSelected whether the item is selected.
              */
-            private void bind(@NonNull MenuItem item, boolean isSelected) {
+            void bind(@NonNull SubtypeItem item, boolean isSelected) {
+                mItem = item;
+                mIsSelected = isSelected;
                 // Use the IME name for subtypes with an empty subtype name.
                 final var name = TextUtils.isEmpty(item.mSubtypeName)
                         ? item.mImeName : item.mSubtypeName;
                 mContainer.setActivated(isSelected);
                 // Activated is the correct state, but we also set selected for accessibility info.
                 mContainer.setSelected(isSelected);
+                // Trigger the ellipsize marquee behaviour by selecting the name.
                 mName.setSelected(isSelected);
                 mName.setText(name);
                 mCheckmark.setVisibility(isSelected ? View.VISIBLE : View.GONE);
-                mHeader.setText(item.mImeName);
-                mHeader.setVisibility(item.mHasHeader ? View.VISIBLE : View.GONE);
-                mDivider.setVisibility(item.mHasDivider ? View.VISIBLE : View.GONE);
+            }
+        }
+
+        private static final class HeaderViewHolder extends RecyclerView.ViewHolder {
+
+            /** The title view, only visible if the bound item has a title. */
+            private final TextView mTitle;
+
+            HeaderViewHolder(@NonNull View itemView) {
+                super(itemView);
+
+                mTitle = itemView.requireViewById(com.android.internal.R.id.header_text);
+            }
+
+            /**
+             * Binds the given item to the current view.
+             *
+             * @param item the item to bind.
+             */
+            void bind(@NonNull HeaderItem item) {
+                mTitle.setText(item.mTitle);
+            }
+        }
+
+        private static final class DividerViewHolder extends RecyclerView.ViewHolder {
+
+            DividerViewHolder(@NonNull View itemView) {
+                super(itemView);
             }
         }
     }
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 5514ec7..d1576c5 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -17,121 +17,63 @@
 package com.android.server.integrity;
 
 import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION;
-import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
-import static android.content.Intent.EXTRA_ORIGINATING_UID;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
 import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
 import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
 import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
-import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
 import static android.content.integrity.IntegrityUtils.getHexDigest;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
 
 import android.annotation.BinderThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.integrity.AppInstallMetadata;
 import android.content.integrity.IAppIntegrityManager;
 import android.content.integrity.Rule;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.provider.Settings;
 import android.util.Pair;
 import android.util.Slog;
-import android.util.apk.SourceStampVerificationResult;
-import android.util.apk.SourceStampVerifier;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.parsing.PackageParser2;
-import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
-import com.android.server.integrity.engine.RuleEvaluationEngine;
-import com.android.server.integrity.model.IntegrityCheckResult;
 import com.android.server.integrity.model.RuleMetadata;
-import com.android.server.pm.PackageManagerServiceUtils;
-import com.android.server.pm.parsing.PackageParserUtils;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 /** Implementation of {@link AppIntegrityManagerService}. */
 public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
-    /**
-     * This string will be used as the "installer" for formula evaluation when the app's installer
-     * cannot be determined.
-     *
-     * <p>This may happen for various reasons. e.g., the installing app's package name may not match
-     * its UID.
-     */
-    private static final String UNKNOWN_INSTALLER = "";
-    /**
-     * This string will be used as the "installer" for formula evaluation when the app is being
-     * installed via ADB.
-     */
-    public static final String ADB_INSTALLER = "adb";
 
     private static final String TAG = "AppIntegrityManagerServiceImpl";
 
     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
-    private static final String BASE_APK_FILE = "base.apk";
-    private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
-    private static final String ALLOWED_INSTALLER_DELIMITER = ",";
-    private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
 
     public static final boolean DEBUG_INTEGRITY_COMPONENT = false;
 
-    private static final Set<String> PACKAGE_INSTALLER =
-            new HashSet<>(
-                    Arrays.asList(
-                            "com.google.android.packageinstaller", "com.android.packageinstaller"));
-
     // Access to files inside mRulesDir is protected by mRulesLock;
     private final Context mContext;
     private final Handler mHandler;
     private final PackageManagerInternal mPackageManagerInternal;
-    private final Supplier<PackageParser2> mParserSupplier;
-    private final RuleEvaluationEngine mEvaluationEngine;
     private final IntegrityFileManager mIntegrityFileManager;
 
     /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
@@ -142,8 +84,6 @@
         return new AppIntegrityManagerServiceImpl(
                 context,
                 LocalServices.getService(PackageManagerInternal.class),
-                PackageParserUtils::forParsingFileWithDefaults,
-                RuleEvaluationEngine.getRuleEvaluationEngine(),
                 IntegrityFileManager.getInstance(),
                 handlerThread.getThreadHandler());
     }
@@ -152,14 +92,10 @@
     AppIntegrityManagerServiceImpl(
             Context context,
             PackageManagerInternal packageManagerInternal,
-            Supplier<PackageParser2> parserSupplier,
-            RuleEvaluationEngine evaluationEngine,
             IntegrityFileManager integrityFileManager,
             Handler handler) {
         mContext = context;
         mPackageManagerInternal = packageManagerInternal;
-        mParserSupplier = parserSupplier;
-        mEvaluationEngine = evaluationEngine;
         mIntegrityFileManager = integrityFileManager;
         mHandler = handler;
 
@@ -214,12 +150,6 @@
                                         version, ruleProvider));
                     }
 
-                    FrameworkStatsLog.write(
-                            FrameworkStatsLog.INTEGRITY_RULES_PUSHED,
-                            success,
-                            ruleProvider,
-                            version);
-
                     Intent intent = new Intent();
                     intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE);
                     try {
@@ -275,157 +205,8 @@
 
     private void handleIntegrityVerification(Intent intent) {
         int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
-
-        try {
-            if (DEBUG_INTEGRITY_COMPONENT) {
-                Slog.d(TAG, "Received integrity verification intent " + intent.toString());
-                Slog.d(TAG, "Extras " + intent.getExtras());
-            }
-
-            String installerPackageName = getInstallerPackageName(intent);
-
-            // Skip integrity verification if the verifier is doing the install.
-            if (!integrityCheckIncludesRuleProvider() && isRuleProvider(installerPackageName)) {
-                if (DEBUG_INTEGRITY_COMPONENT) {
-                    Slog.i(TAG, "Verifier doing the install. Skipping integrity check.");
-                }
-                mPackageManagerInternal.setIntegrityVerificationResult(
-                        verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-                return;
-            }
-
-            String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
-
-            Pair<SigningDetails, Bundle> packageSigningAndMetadata =
-                    getPackageSigningAndMetadata(intent.getData());
-            if (packageSigningAndMetadata == null) {
-                Slog.w(TAG, "Cannot parse package " + packageName);
-                // We can't parse the package.
-                mPackageManagerInternal.setIntegrityVerificationResult(
-                        verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-                return;
-            }
-
-            var signingDetails = packageSigningAndMetadata.first;
-            List<String> appCertificates = getCertificateFingerprint(packageName, signingDetails);
-            List<String> appCertificateLineage = getCertificateLineage(packageName, signingDetails);
-            List<String> installerCertificates =
-                    getInstallerCertificateFingerprint(installerPackageName);
-
-            AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
-
-            builder.setPackageName(getPackageNameNormalized(packageName));
-            builder.setAppCertificates(appCertificates);
-            builder.setAppCertificateLineage(appCertificateLineage);
-            builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1));
-            builder.setInstallerName(getPackageNameNormalized(installerPackageName));
-            builder.setInstallerCertificates(installerCertificates);
-            builder.setIsPreInstalled(isSystemApp(packageName));
-
-            Map<String, String> allowedInstallers =
-                    getAllowedInstallers(packageSigningAndMetadata.second);
-            builder.setAllowedInstallersAndCert(allowedInstallers);
-            extractSourceStamp(intent.getData(), builder);
-
-            AppInstallMetadata appInstallMetadata = builder.build();
-
-            if (DEBUG_INTEGRITY_COMPONENT) {
-                Slog.i(
-                        TAG,
-                        "To be verified: "
-                                + appInstallMetadata
-                                + " installers "
-                                + allowedInstallers);
-            }
-            IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata);
-            if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) {
-                Slog.i(
-                        TAG,
-                        String.format(
-                                "Integrity check of %s result: %s due to %s",
-                                packageName, result.getEffect(), result.getMatchedRules()));
-            }
-
-            FrameworkStatsLog.write(
-                    FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED,
-                    packageName,
-                    appCertificates.toString(),
-                    appInstallMetadata.getVersionCode(),
-                    installerPackageName,
-                    result.getLoggingResponse(),
-                    result.isCausedByAppCertRule(),
-                    result.isCausedByInstallerRule());
-            mPackageManagerInternal.setIntegrityVerificationResult(
-                    verificationId,
-                    result.getEffect() == IntegrityCheckResult.Effect.ALLOW
-                            ? PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW
-                            : PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
-        } catch (IllegalArgumentException e) {
-            // This exception indicates something is wrong with the input passed by package manager.
-            // e.g., someone trying to trick the system. We block installs in this case.
-            Slog.e(TAG, "Invalid input to integrity verification", e);
-            mPackageManagerInternal.setIntegrityVerificationResult(
-                    verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
-        } catch (Exception e) {
-            // Other exceptions indicate an error within the integrity component implementation and
-            // we allow them.
-            Slog.e(TAG, "Error handling integrity verification", e);
-            mPackageManagerInternal.setIntegrityVerificationResult(
-                    verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-        }
-    }
-
-    /**
-     * Verify the UID and return the installer package name.
-     *
-     * @return the package name of the installer, or null if it cannot be determined or it is
-     * installed via adb.
-     */
-    @Nullable
-    private String getInstallerPackageName(Intent intent) {
-        String installer =
-                intent.getStringExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE);
-        if (PackageManagerServiceUtils.isInstalledByAdb(installer)) {
-            return ADB_INSTALLER;
-        }
-        int installerUid = intent.getIntExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, -1);
-        if (installerUid < 0) {
-            Slog.e(
-                    TAG,
-                    "Installer cannot be determined: installer: "
-                            + installer
-                            + " installer UID: "
-                            + installerUid);
-            return UNKNOWN_INSTALLER;
-        }
-
-        // Verify that the installer UID actually contains the package. Note that comparing UIDs
-        // is not safe since context's uid can change in different settings; e.g. Android Auto.
-        if (!getPackageListForUid(installerUid).contains(installer)) {
-            return UNKNOWN_INSTALLER;
-        }
-
-        // At this time we can trust "installer".
-
-        // A common way for apps to install packages is to send an intent to PackageInstaller. In
-        // that case, the installer will always show up as PackageInstaller which is not what we
-        // want.
-        if (PACKAGE_INSTALLER.contains(installer)) {
-            int originatingUid = intent.getIntExtra(EXTRA_ORIGINATING_UID, -1);
-            if (originatingUid < 0) {
-                Slog.e(TAG, "Installer is package installer but originating UID not found.");
-                return UNKNOWN_INSTALLER;
-            }
-            List<String> installerPackages = getPackageListForUid(originatingUid);
-            if (installerPackages.isEmpty()) {
-                Slog.e(TAG, "No package found associated with originating UID " + originatingUid);
-                return UNKNOWN_INSTALLER;
-            }
-            // In the case of multiple package sharing a UID, we just return the first one.
-            return installerPackages.get(0);
-        }
-
-        return installer;
+        mPackageManagerInternal.setIntegrityVerificationResult(
+                verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
     }
 
     /** We will use the SHA256 digest of a package name if it is more than 32 bytes long. */
@@ -443,264 +224,6 @@
         }
     }
 
-    private List<String> getInstallerCertificateFingerprint(String installer) {
-        if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) {
-            return Collections.emptyList();
-        }
-        var installerPkg = mPackageManagerInternal.getPackage(installer);
-        if (installerPkg == null) {
-            Slog.w(TAG, "Installer package " + installer + " not found.");
-            return Collections.emptyList();
-        }
-        return getCertificateFingerprint(installerPkg.getPackageName(),
-                installerPkg.getSigningDetails());
-    }
-
-    private List<String> getCertificateFingerprint(@NonNull String packageName,
-            @NonNull SigningDetails signingDetails) {
-        ArrayList<String> certificateFingerprints = new ArrayList();
-        for (Signature signature : getSignatures(packageName, signingDetails)) {
-            certificateFingerprints.add(getFingerprint(signature));
-        }
-        return certificateFingerprints;
-    }
-
-    private List<String> getCertificateLineage(@NonNull String packageName,
-            @NonNull SigningDetails signingDetails) {
-        ArrayList<String> certificateLineage = new ArrayList();
-        for (Signature signature : getSignatureLineage(packageName, signingDetails)) {
-            certificateLineage.add(getFingerprint(signature));
-        }
-        return certificateLineage;
-    }
-
-    /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */
-    private Map<String, String> getAllowedInstallers(@Nullable Bundle metaData) {
-        Map<String, String> packageCertMap = new HashMap<>();
-        if (metaData != null) {
-            String allowedInstallers = metaData.getString(ALLOWED_INSTALLERS_METADATA_NAME);
-            if (allowedInstallers != null) {
-                // parse the metadata for certs.
-                String[] installerCertPairs = allowedInstallers.split(ALLOWED_INSTALLER_DELIMITER);
-                for (String packageCertPair : installerCertPairs) {
-                    String[] packageAndCert =
-                            packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER);
-                    if (packageAndCert.length == 2) {
-                        String packageName = getPackageNameNormalized(packageAndCert[0]);
-                        String cert = packageAndCert[1];
-                        packageCertMap.put(packageName, cert);
-                    } else if (packageAndCert.length == 1) {
-                        packageCertMap.put(
-                                getPackageNameNormalized(packageAndCert[0]),
-                                INSTALLER_CERTIFICATE_NOT_EVALUATED);
-                    }
-                }
-            }
-        }
-
-        return packageCertMap;
-    }
-
-    /** Extract the source stamp embedded in the APK, if present. */
-    private void extractSourceStamp(Uri dataUri, AppInstallMetadata.Builder appInstallMetadata) {
-        File installationPath = getInstallationPath(dataUri);
-        if (installationPath == null) {
-            throw new IllegalArgumentException("Installation path is null, package not found");
-        }
-
-        SourceStampVerificationResult sourceStampVerificationResult;
-        if (installationPath.isDirectory()) {
-            try (Stream<Path> filesList = Files.list(installationPath.toPath())) {
-                List<String> apkFiles =
-                        filesList
-                                .map(path -> path.toAbsolutePath().toString())
-                                .filter(str -> str.endsWith(".apk"))
-                                .collect(Collectors.toList());
-                sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles);
-            } catch (IOException e) {
-                throw new IllegalArgumentException("Could not read APK directory");
-            }
-        } else {
-            sourceStampVerificationResult =
-                    SourceStampVerifier.verify(installationPath.getAbsolutePath());
-        }
-
-        appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent());
-        appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified());
-        // A verified stamp is set to be trusted.
-        appInstallMetadata.setIsStampTrusted(sourceStampVerificationResult.isVerified());
-        if (sourceStampVerificationResult.isVerified()) {
-            X509Certificate sourceStampCertificate =
-                    (X509Certificate) sourceStampVerificationResult.getCertificate();
-            // Sets source stamp certificate digest.
-            try {
-                MessageDigest digest = MessageDigest.getInstance("SHA-256");
-                byte[] certificateDigest = digest.digest(sourceStampCertificate.getEncoded());
-                appInstallMetadata.setStampCertificateHash(getHexDigest(certificateDigest));
-            } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
-                throw new IllegalArgumentException(
-                        "Error computing source stamp certificate digest", e);
-            }
-        }
-    }
-
-    private static Signature[] getSignatures(@NonNull String packageName,
-            @NonNull SigningDetails signingDetails) {
-        Signature[] signatures = signingDetails.getSignatures();
-        if (signatures == null || signatures.length < 1) {
-            throw new IllegalArgumentException("Package signature not found in " + packageName);
-        }
-
-        // We are only interested in evaluating the active signatures.
-        return signatures;
-    }
-
-    private static Signature[] getSignatureLineage(@NonNull String packageName,
-            @NonNull SigningDetails signingDetails) {
-        // Obtain the active signatures of the package.
-        Signature[] signatureLineage = getSignatures(packageName, signingDetails);
-
-        var pastSignatures = signingDetails.getPastSigningCertificates();
-        // Obtain the past signatures of the package.
-        if (signatureLineage.length == 1 && !ArrayUtils.isEmpty(pastSignatures)) {
-            // Merge the signatures and return.
-            Signature[] allSignatures =
-                    new Signature[signatureLineage.length + pastSignatures.length];
-            int i;
-            for (i = 0; i < signatureLineage.length; i++) {
-                allSignatures[i] = signatureLineage[i];
-            }
-            for (int j = 0; j < pastSignatures.length; j++) {
-                allSignatures[i] = pastSignatures[j];
-                i++;
-            }
-            signatureLineage = allSignatures;
-        }
-
-        return signatureLineage;
-    }
-
-    private static String getFingerprint(Signature cert) {
-        InputStream input = new ByteArrayInputStream(cert.toByteArray());
-
-        CertificateFactory factory;
-        try {
-            factory = CertificateFactory.getInstance("X509");
-        } catch (CertificateException e) {
-            throw new RuntimeException("Error getting CertificateFactory", e);
-        }
-        X509Certificate certificate = null;
-        try {
-            if (factory != null) {
-                certificate = (X509Certificate) factory.generateCertificate(input);
-            }
-        } catch (CertificateException e) {
-            throw new RuntimeException("Error getting X509Certificate", e);
-        }
-
-        if (certificate == null) {
-            throw new RuntimeException("X509 Certificate not found");
-        }
-
-        try {
-            MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            byte[] publicKey = digest.digest(certificate.getEncoded());
-            return getHexDigest(publicKey);
-        } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
-            throw new IllegalArgumentException("Error error computing fingerprint", e);
-        }
-    }
-
-    @Nullable
-    private Pair<SigningDetails, Bundle> getPackageSigningAndMetadata(Uri dataUri) {
-        File installationPath = getInstallationPath(dataUri);
-        if (installationPath == null) {
-            throw new IllegalArgumentException("Installation path is null, package not found");
-        }
-
-        try (PackageParser2 parser = mParserSupplier.get()) {
-            var pkg = parser.parsePackage(installationPath, 0, false);
-            // APK signatures is already verified elsewhere in PackageManager. We do not need to
-            // verify it again since it could cause a timeout for large APKs.
-            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
-            final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
-                    input, pkg, /* skipVerify= */ true);
-            if (result.isError()) {
-                Slog.w(TAG, result.getErrorMessage(), result.getException());
-                return null;
-            }
-            return Pair.create(result.getResult(), pkg.getMetaData());
-        } catch (Exception e) {
-            Slog.w(TAG, "Exception reading " + dataUri, e);
-            return null;
-        }
-    }
-
-    private PackageInfo getMultiApkInfo(File multiApkDirectory) {
-        // The base apk will normally be called base.apk
-        File baseFile = new File(multiApkDirectory, BASE_APK_FILE);
-        PackageInfo basePackageInfo =
-                mContext.getPackageManager()
-                        .getPackageArchiveInfo(
-                                baseFile.getAbsolutePath(),
-                                PackageManager.GET_SIGNING_CERTIFICATES
-                                        | PackageManager.GET_META_DATA);
-
-        if (basePackageInfo == null) {
-            for (File apkFile : multiApkDirectory.listFiles()) {
-                if (apkFile.isDirectory()) {
-                    continue;
-                }
-
-                // If we didn't find a base.apk, then try to parse each apk until we find the one
-                // that succeeds.
-                try {
-                    basePackageInfo =
-                            mContext.getPackageManager()
-                                    .getPackageArchiveInfo(
-                                            apkFile.getAbsolutePath(),
-                                            PackageManager.GET_SIGNING_CERTIFICATES
-                                                    | PackageManager.GET_META_DATA);
-                } catch (Exception e) {
-                    // Some of the splits may not contain a valid android manifest. It is an
-                    // expected exception. We still log it nonetheless but we should keep looking.
-                    Slog.w(TAG, "Exception reading " + apkFile, e);
-                }
-                if (basePackageInfo != null) {
-                    Slog.i(TAG, "Found package info from " + apkFile);
-                    break;
-                }
-            }
-        }
-
-        if (basePackageInfo == null) {
-            throw new IllegalArgumentException(
-                    "Base package info cannot be found from installation directory");
-        }
-
-        return basePackageInfo;
-    }
-
-    private File getInstallationPath(Uri dataUri) {
-        if (dataUri == null) {
-            throw new IllegalArgumentException("Null data uri");
-        }
-
-        String scheme = dataUri.getScheme();
-        if (!"file".equalsIgnoreCase(scheme)) {
-            throw new IllegalArgumentException("Unsupported scheme for " + dataUri);
-        }
-
-        File installationPath = new File(dataUri.getPath());
-        if (!installationPath.exists()) {
-            throw new IllegalArgumentException("Cannot find file for " + dataUri);
-        }
-        if (!installationPath.canRead()) {
-            throw new IllegalArgumentException("Cannot read file for " + dataUri);
-        }
-        return installationPath;
-    }
-
     private String getCallerPackageNameOrThrow(int callingUid) {
         String callerPackageName = getCallingRulePusherPackageName(callingUid);
         if (callerPackageName == null) {
@@ -736,15 +259,6 @@
         return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0);
     }
 
-    private boolean isRuleProvider(String installerPackageName) {
-        for (String ruleProvider : getAllowedRuleProviderSystemApps()) {
-            if (ruleProvider.matches(installerPackageName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private List<String> getAllowedRuleProviderSystemApps() {
         List<String> integrityRuleProviders =
                 Arrays.asList(
@@ -772,14 +286,6 @@
         }
     }
 
-    private boolean integrityCheckIncludesRuleProvider() {
-        return Settings.Global.getInt(
-                mContext.getContentResolver(),
-                Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
-                0)
-                == 1;
-    }
-
     private List<String> getPackageListForUid(int uid) {
         try {
             return Arrays.asList(mContext.getPackageManager().getPackagesForUid(uid));
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
deleted file mode 100644
index 61da45d..0000000
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.engine;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.Rule;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.integrity.IntegrityFileManager;
-import com.android.server.integrity.model.IntegrityCheckResult;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The engine used to evaluate rules against app installs.
- *
- * <p>Every app install is evaluated against rules (pushed by the verifier) by the evaluation engine
- * to allow/block that install.
- */
-public class RuleEvaluationEngine {
-    private static final String TAG = "RuleEvaluation";
-
-    // The engine for loading rules, retrieving metadata for app installs, and evaluating app
-    // installs against rules.
-    private static RuleEvaluationEngine sRuleEvaluationEngine;
-
-    private final IntegrityFileManager mIntegrityFileManager;
-
-    @VisibleForTesting
-    RuleEvaluationEngine(IntegrityFileManager integrityFileManager) {
-        mIntegrityFileManager = integrityFileManager;
-    }
-
-    /** Provide a singleton instance of the rule evaluation engine. */
-    public static synchronized RuleEvaluationEngine getRuleEvaluationEngine() {
-        if (sRuleEvaluationEngine == null) {
-            return new RuleEvaluationEngine(IntegrityFileManager.getInstance());
-        }
-        return sRuleEvaluationEngine;
-    }
-
-    /**
-     * Load, and match the list of rules against an app install metadata.
-     *
-     * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
-     *                           against.
-     * @return result of the integrity check
-     */
-    public IntegrityCheckResult evaluate(
-            AppInstallMetadata appInstallMetadata) {
-        List<Rule> rules = loadRules(appInstallMetadata);
-        return RuleEvaluator.evaluateRules(rules, appInstallMetadata);
-    }
-
-    private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
-        if (!mIntegrityFileManager.initialized()) {
-            Slog.w(TAG, "Integrity rule files are not available.");
-            return Collections.emptyList();
-        }
-
-        try {
-            return mIntegrityFileManager.readRules(appInstallMetadata);
-        } catch (Exception e) {
-            Slog.e(TAG, "Error loading rules.", e);
-            return new ArrayList<>();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
deleted file mode 100644
index 9d94304..0000000
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.engine;
-
-import static android.content.integrity.Rule.DENY;
-import static android.content.integrity.Rule.FORCE_ALLOW;
-
-import android.annotation.NonNull;
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.Rule;
-
-import com.android.server.integrity.model.IntegrityCheckResult;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * A helper class for evaluating rules against app install metadata to find if there are matching
- * rules.
- */
-final class RuleEvaluator {
-
-    /**
-     * Match the list of rules against an app install metadata.
-     *
-     * <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas
-     * only. All rules are OR'ed together by default.
-     *
-     * @param rules The list of rules to evaluate.
-     * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
-     *     against.
-     * @return result of the integrity check
-     */
-    @NonNull
-    static IntegrityCheckResult evaluateRules(
-            List<Rule> rules, AppInstallMetadata appInstallMetadata) {
-
-        // Identify the rules that match the {@code appInstallMetadata}.
-        List<Rule> matchedRules =
-                rules.stream()
-                        .filter(rule -> rule.getFormula().matches(appInstallMetadata))
-                        .collect(Collectors.toList());
-
-        // Identify the matched power allow rules and terminate early if we have any.
-        List<Rule> matchedPowerAllowRules =
-                matchedRules.stream()
-                        .filter(rule -> rule.getEffect() == FORCE_ALLOW)
-                        .collect(Collectors.toList());
-
-        if (!matchedPowerAllowRules.isEmpty()) {
-            return IntegrityCheckResult.allow(matchedPowerAllowRules);
-        }
-
-        // Identify the matched deny rules.
-        List<Rule> matchedDenyRules =
-                matchedRules.stream()
-                        .filter(rule -> rule.getEffect() == DENY)
-                        .collect(Collectors.toList());
-
-        if (!matchedDenyRules.isEmpty()) {
-            return IntegrityCheckResult.deny(matchedDenyRules);
-        }
-
-        // When no rules are denied, return default allow result.
-        return IntegrityCheckResult.allow();
-    }
-}
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
index 1fa0670..b0647fc 100644
--- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
+++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
@@ -19,8 +19,6 @@
 import android.annotation.Nullable;
 import android.content.integrity.Rule;
 
-import com.android.internal.util.FrameworkStatsLog;
-
 import java.util.Collections;
 import java.util.List;
 
@@ -82,21 +80,6 @@
         return new IntegrityCheckResult(Effect.DENY, ruleList);
     }
 
-    /**
-     * Returns the in value of the integrity check result for logging purposes.
-     */
-    public int getLoggingResponse() {
-        if (getEffect() == Effect.DENY) {
-            return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED;
-        } else if (getEffect() == Effect.ALLOW && getMatchedRules().isEmpty()) {
-            return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
-        } else if (getEffect() == Effect.ALLOW && !getMatchedRules().isEmpty()) {
-            return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED;
-        } else {
-            throw new IllegalStateException("IntegrityCheckResult is not valid.");
-        }
-    }
-
     /** Returns true when the {@code mEffect} is caused by an app certificate mismatch. */
     public boolean isCausedByAppCertRule() {
         return mRuleList.stream().anyMatch(rule -> rule.getFormula().isAppCertificateFormula());
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 741513c..7e80cbc 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -221,7 +221,7 @@
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
                 ResultReceiver resultReceiver) {
-            (new LocaleManagerShellCommand(mBinderService))
+            (new LocaleManagerShellCommand(mBinderService, mContext))
                     .exec(this, in, out, err, args, callback, resultReceiver);
         }
 
diff --git a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
index 09f2ffa..926b647 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManager;
 import android.app.ILocaleManager;
 import android.app.LocaleConfig;
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.LocaleList;
 import android.os.RemoteException;
 import android.os.ShellCommand;
@@ -31,9 +33,11 @@
  */
 public class LocaleManagerShellCommand extends ShellCommand {
     private final ILocaleManager mBinderService;
+    private final Context mContext;
 
-    LocaleManagerShellCommand(ILocaleManager localeManager) {
+    LocaleManagerShellCommand(ILocaleManager localeManager, Context context) {
         mBinderService = localeManager;
+        mContext = context;
     }
     @Override
     public int onCommand(String cmd) {
@@ -49,6 +53,8 @@
                 return runSetAppOverrideLocaleConfig();
             case "get-app-localeconfig":
                 return runGetAppOverrideLocaleConfig();
+            case "get-app-localeconfig-ignore-override":
+                return runGetAppLocaleConfigIgnoreOverride();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -261,6 +267,55 @@
         return 0;
     }
 
+    private int runGetAppLocaleConfigIgnoreOverride() {
+        String packageName = getNextArg();
+        final PrintWriter err = getErrPrintWriter();
+
+        if (packageName != null) {
+            int userId = ActivityManager.getCurrentUser();
+            do {
+                String option = getNextOption();
+                if (option == null) {
+                    break;
+                }
+                if ("--user".equals(option)) {
+                    userId = UserHandle.parseUserArg(getNextArgRequired());
+                    break;
+                } else {
+                    throw new IllegalArgumentException("Unknown option: " + option);
+                }
+            } while (true);
+            LocaleConfig resLocaleConfig = null;
+            try {
+                resLocaleConfig = LocaleConfig.fromContextIgnoringOverride(
+                    mContext.createPackageContextAsUser(packageName, /* flags= */ 0,
+                        UserHandle.of(userId)));
+            } catch (PackageManager.NameNotFoundException e) {
+                err.println("Unknown package name " + packageName + " for user " + userId);
+                return -1;
+            }
+            if (resLocaleConfig == null) {
+                getOutPrintWriter().println(
+                        "LocaleConfig for " + packageName + " for user " + userId + " is null");
+            } else {
+                LocaleList locales = resLocaleConfig.getSupportedLocales();
+                if (locales == null) {
+                    getOutPrintWriter().println(
+                            "Locales within the LocaleConfig for " + packageName + " for user "
+                                    + userId + " are null");
+                } else {
+                    getOutPrintWriter().println(
+                            "Locales within the LocaleConfig for " + packageName + " for user "
+                                    + userId + " are [" + locales.toLanguageTags() + "]");
+                }
+            }
+        } else {
+            err.println("Error: no package specified");
+            return -1;
+        }
+        return 0;
+    }
+
     private LocaleList parseOverrideLocales() {
         String locales = getNextArg();
         if (locales == null) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 3f4a9bb..ed69f7a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -444,8 +444,17 @@
         mSupportedContextHubPerms = hubInfo.second;
         mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values());
         mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
-        mTransactionManager = new ContextHubTransactionManager(
-                mContextHubWrapper, mClientManager, mNanoAppStateManager);
+
+        if (Flags.reduceLockingContextHubTransactionManager()) {
+            mTransactionManager =
+                    new ContextHubTransactionManager(
+                            mContextHubWrapper, mClientManager, mNanoAppStateManager);
+        } else {
+            mTransactionManager =
+                    new ContextHubTransactionManagerOld(
+                            mContextHubWrapper, mClientManager, mNanoAppStateManager);
+        }
+
         mSensorPrivacyManagerInternal =
                 LocalServices.getService(SensorPrivacyManagerInternal.class);
         return true;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index 2a0b1af..da31bf2 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -26,6 +26,8 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.time.Duration;
 import java.util.ArrayDeque;
 import java.util.Collections;
@@ -43,8 +45,8 @@
 
 /**
  * Manages transactions at the Context Hub Service.
- * <p>
- * This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ *
+ * <p>This class maintains a queue of transaction requests made to the ContextHubService by clients,
  * and executes them through the Context Hub. At any point in time, either the transaction queue is
  * empty, or there is a pending transaction that is waiting for an asynchronous response from the
  * hub. This class also handles synchronous errors and timeouts of each transaction.
@@ -52,66 +54,80 @@
  * @hide
  */
 /* package */ class ContextHubTransactionManager {
-    private static final String TAG = "ContextHubTransactionManager";
+    protected static final String TAG = "ContextHubTransactionManager";
 
     public static final Duration RELIABLE_MESSAGE_TIMEOUT = Duration.ofSeconds(1);
 
     public static final Duration RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT =
             RELIABLE_MESSAGE_TIMEOUT.multipliedBy(3);
 
-    private static final int MAX_PENDING_REQUESTS = 10000;
+    // TODO: b/362299144: When cleaning up the flag
+    // reduce_locking_context_hub_transaction_manager, change these to private
+    protected static final int MAX_PENDING_REQUESTS = 10000;
 
-    private static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
+    protected static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
 
-    private static final Duration RELIABLE_MESSAGE_RETRY_WAIT_TIME = Duration.ofMillis(250);
+    protected static final Duration RELIABLE_MESSAGE_RETRY_WAIT_TIME = Duration.ofMillis(250);
 
-    private static final Duration RELIABLE_MESSAGE_MIN_WAIT_TIME = Duration.ofNanos(1000);
+    protected static final Duration RELIABLE_MESSAGE_MIN_WAIT_TIME = Duration.ofNanos(1000);
 
-    private final IContextHubWrapper mContextHubProxy;
+    protected final IContextHubWrapper mContextHubProxy;
 
-    private final ContextHubClientManager mClientManager;
+    protected final ContextHubClientManager mClientManager;
 
-    private final NanoAppStateManager mNanoAppStateManager;
+    protected final NanoAppStateManager mNanoAppStateManager;
 
-    private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
+    @GuardedBy("mTransactionLock")
+    protected final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
 
-    private final Map<Integer, ContextHubServiceTransaction> mReliableMessageTransactionMap =
+    @GuardedBy("mReliableMessageLock")
+    protected final Map<Integer, ContextHubServiceTransaction> mReliableMessageTransactionMap =
             new HashMap<>();
 
     /** A set of host endpoint IDs that have an active pending transaction. */
-    private final Set<Short> mReliableMessageHostEndpointIdActiveSet = new HashSet<>();
+    @GuardedBy("mReliableMessageLock")
+    protected final Set<Short> mReliableMessageHostEndpointIdActiveSet = new HashSet<>();
 
-    private final AtomicInteger mNextAvailableId = new AtomicInteger();
+    protected final AtomicInteger mNextAvailableId = new AtomicInteger();
 
     /**
-     * The next available message sequence number. We choose a random
-     * number to start with to avoid collisions and limit the bound to
-     * half of the max value to avoid overflow.
+     * The next available message sequence number. We choose a random number to start with to avoid
+     * collisions and limit the bound to half of the max value to avoid overflow.
      */
-    private final AtomicInteger mNextAvailableMessageSequenceNumber =
+    protected final AtomicInteger mNextAvailableMessageSequenceNumber =
             new AtomicInteger(new Random().nextInt(Integer.MAX_VALUE / 2));
 
     /*
-     * An executor and the future object for scheduling timeout timers and
+     * An executor and the future objects for scheduling timeout timers and
      * for scheduling the processing of reliable message transactions.
      */
-    private final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(1);
-    private ScheduledFuture<?> mTimeoutFuture = null;
-    private ScheduledFuture<?> mReliableMessageTransactionFuture = null;
+    protected final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(2);
+
+    @GuardedBy("mTransactionLock")
+    protected ScheduledFuture<?> mTimeoutFuture = null;
+
+    @GuardedBy("mReliableMessageLock")
+    protected ScheduledFuture<?> mReliableMessageTransactionFuture = null;
 
     /*
      * The list of previous transaction records.
      */
-    private static final int NUM_TRANSACTION_RECORDS = 20;
-    private final ConcurrentLinkedEvictingDeque<TransactionRecord> mTransactionRecordDeque =
+    protected static final int NUM_TRANSACTION_RECORDS = 20;
+    protected final ConcurrentLinkedEvictingDeque<TransactionRecord> mTransactionRecordDeque =
             new ConcurrentLinkedEvictingDeque<>(NUM_TRANSACTION_RECORDS);
 
-    /**
-     * A container class to store a record of transactions.
+    /*
+     * Locks for synchronization of normal transactions separately from reliable message
+     * transactions.
      */
-    private class TransactionRecord {
-        private final String mTransaction;
-        private final long mTimestamp;
+    protected final Object mTransactionLock = new Object();
+    protected final Object mReliableMessageLock = new Object();
+    protected final Object mTransactionRecordLock = new Object();
+
+    /** A container class to store a record of transactions. */
+    protected static class TransactionRecord {
+        protected final String mTransaction;
+        protected final long mTimestamp;
 
         TransactionRecord(String transaction) {
             mTransaction = transaction;
@@ -126,8 +142,18 @@
         }
     }
 
+    /** Used when finishing a transaction. */
+    interface TransactionAcceptConditions {
+        /**
+         * Returns whether to accept the found transaction when receiving a response from the
+         * Context Hub.
+         */
+        boolean acceptTransaction(ContextHubServiceTransaction transaction);
+    }
+
     /* package */ ContextHubTransactionManager(
-            IContextHubWrapper contextHubProxy, ContextHubClientManager clientManager,
+            IContextHubWrapper contextHubProxy,
+            ContextHubClientManager clientManager,
             NanoAppStateManager nanoAppStateManager) {
         mContextHubProxy = contextHubProxy;
         mClientManager = clientManager;
@@ -409,34 +435,47 @@
 
     /**
      * Adds a new transaction to the queue.
-     * <p>
-     * If there was no pending transaction at the time, the transaction that was added will be
+     *
+     * <p>If there was no pending transaction at the time, the transaction that was added will be
      * started in this method. If there were too many transactions in the queue, an exception will
      * be thrown.
      *
      * @param transaction the transaction to add
-     * @throws IllegalStateException if the queue is full
      */
     /* package */
-    synchronized void addTransaction(
-            ContextHubServiceTransaction transaction) throws IllegalStateException {
+    void addTransaction(ContextHubServiceTransaction transaction) {
         if (transaction == null) {
             return;
         }
 
-        if (mTransactionQueue.size() >= MAX_PENDING_REQUESTS
-                || mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
-            throw new IllegalStateException("Transaction queue is full (capacity = "
-                    + MAX_PENDING_REQUESTS + ")");
+        synchronized (mTransactionRecordLock) {
+            mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
         }
 
-        mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
         if (Flags.reliableMessageRetrySupportService()
                 && transaction.getTransactionType()
                         == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
-            mReliableMessageTransactionMap.put(transaction.getMessageSequenceNumber(), transaction);
+            synchronized (mReliableMessageLock) {
+                if (mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
+                    throw new IllegalStateException(
+                            "Reliable message transaction queue is full "
+                                    + "(capacity = "
+                                    + MAX_PENDING_REQUESTS
+                                    + ")");
+                }
+                mReliableMessageTransactionMap.put(
+                        transaction.getMessageSequenceNumber(), transaction);
+            }
             mExecutor.execute(() -> processMessageTransactions());
-        } else {
+            return;
+        }
+
+        synchronized (mTransactionLock) {
+            if (mTransactionQueue.size() >= MAX_PENDING_REQUESTS) {
+                throw new IllegalStateException(
+                        "Transaction queue is full (capacity = " + MAX_PENDING_REQUESTS + ")");
+            }
+
             mTransactionQueue.add(transaction);
             if (mTransactionQueue.size() == 1) {
                 startNextTransaction();
@@ -448,62 +487,85 @@
      * Handles a transaction response from a Context Hub.
      *
      * @param transactionId the transaction ID of the response
-     * @param success       true if the transaction succeeded
+     * @param success true if the transaction succeeded
      */
     /* package */
-    synchronized void onTransactionResponse(int transactionId, boolean success) {
-        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+    void onTransactionResponse(int transactionId, boolean success) {
+        TransactionAcceptConditions conditions =
+                transaction -> transaction.getTransactionId() == transactionId;
+        ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
         if (transaction == null) {
-            Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
-            return;
-        }
-        if (transaction.getTransactionId() != transactionId) {
             Log.w(TAG, "Received unexpected transaction response (expected ID = "
-                    + transaction.getTransactionId() + ", received ID = " + transactionId + ")");
+                    + transactionId
+                    + ", received ID = "
+                    + transaction.getTransactionId()
+                    + ")");
             return;
         }
 
-        transaction.onTransactionComplete(success ? ContextHubTransaction.RESULT_SUCCESS :
-                        ContextHubTransaction.RESULT_FAILED_AT_HUB);
-        removeTransactionAndStartNext();
+        synchronized (transaction) {
+            transaction.onTransactionComplete(
+                    success
+                            ? ContextHubTransaction.RESULT_SUCCESS
+                            : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+            transaction.setComplete();
+        }
     }
 
+    /**
+     * Handles a message delivery response from a Context Hub.
+     *
+     * @param messageSequenceNumber the message sequence number of the response
+     * @param success true if the message was delivered successfully
+     */
     /* package */
-    synchronized void onMessageDeliveryResponse(int messageSequenceNumber, boolean success) {
+    void onMessageDeliveryResponse(int messageSequenceNumber, boolean success) {
         if (!Flags.reliableMessageRetrySupportService()) {
-            ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+            TransactionAcceptConditions conditions =
+                    transaction -> transaction.getTransactionType()
+                            == ContextHubTransaction.TYPE_RELIABLE_MESSAGE
+                    && transaction.getMessageSequenceNumber()
+                            == messageSequenceNumber;
+            ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
             if (transaction == null) {
-                Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+                Log.w(TAG, "Received unexpected message delivery response (expected"
+                        + " message sequence number = "
+                        + messageSequenceNumber
+                        + ", received messageSequenceNumber = "
+                        + messageSequenceNumber
+                        + ")");
                 return;
             }
 
-            int transactionMessageSequenceNumber = transaction.getMessageSequenceNumber();
-            if (transaction.getTransactionType() != ContextHubTransaction.TYPE_RELIABLE_MESSAGE
-                    || transactionMessageSequenceNumber != messageSequenceNumber) {
-                Log.w(TAG, "Received unexpected message transaction response (expected message "
-                        + "sequence number = "
-                        + transaction.getMessageSequenceNumber()
-                        + ", received messageSequenceNumber = " + messageSequenceNumber + ")");
+            synchronized (transaction) {
+                transaction.onTransactionComplete(
+                        success
+                                ? ContextHubTransaction.RESULT_SUCCESS
+                                : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+                transaction.setComplete();
+            }
+            return;
+        }
+
+        ContextHubServiceTransaction transaction = null;
+        synchronized (mReliableMessageLock) {
+            transaction = mReliableMessageTransactionMap.get(messageSequenceNumber);
+            if (transaction == null) {
+                Log.w(
+                        TAG,
+                        "Could not find reliable message transaction with "
+                                + "message sequence number = "
+                                + messageSequenceNumber);
                 return;
             }
 
-            transaction.onTransactionComplete(success ? ContextHubTransaction.RESULT_SUCCESS :
-                            ContextHubTransaction.RESULT_FAILED_AT_HUB);
-            removeTransactionAndStartNext();
-            return;
+            removeMessageTransaction(transaction);
         }
 
-        ContextHubServiceTransaction transaction =
-                mReliableMessageTransactionMap.get(messageSequenceNumber);
-        if (transaction == null) {
-            Log.w(TAG, "Could not find reliable message transaction with "
-                    + "message sequence number = "
-                    + messageSequenceNumber);
-            return;
-        }
-
-        completeMessageTransaction(transaction,
-                success ? ContextHubTransaction.RESULT_SUCCESS
+        completeMessageTransaction(
+                transaction,
+                success
+                        ? ContextHubTransaction.RESULT_SUCCESS
                         : ContextHubTransaction.RESULT_FAILED_AT_HUB);
         mExecutor.execute(() -> processMessageTransactions());
     }
@@ -514,77 +576,116 @@
      * @param nanoAppStateList the list of nanoapps included in the response
      */
     /* package */
-    synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
-        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+    void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+        TransactionAcceptConditions conditions = transaction ->
+                transaction.getTransactionType() == ContextHubTransaction.TYPE_QUERY_NANOAPPS;
+        ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
         if (transaction == null) {
-            Log.w(TAG, "Received unexpected query response (no transaction pending)");
-            return;
-        }
-        if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
             Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
             return;
         }
 
-        transaction.onQueryResponse(ContextHubTransaction.RESULT_SUCCESS, nanoAppStateList);
-        removeTransactionAndStartNext();
+        synchronized (transaction) {
+            transaction.onQueryResponse(ContextHubTransaction.RESULT_SUCCESS, nanoAppStateList);
+            transaction.setComplete();
+        }
     }
 
-    /**
-     * Handles a hub reset event by stopping a pending transaction and starting the next.
-     */
+    /** Handles a hub reset event by stopping a pending transaction and starting the next. */
     /* package */
-    synchronized void onHubReset() {
+    void onHubReset() {
         if (Flags.reliableMessageRetrySupportService()) {
-            Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
-                    mReliableMessageTransactionMap.entrySet().iterator();
-            while (iter.hasNext()) {
-                completeMessageTransaction(iter.next().getValue(),
-                        ContextHubTransaction.RESULT_FAILED_AT_HUB, iter);
+            synchronized (mReliableMessageLock) {
+                Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+                        mReliableMessageTransactionMap.entrySet().iterator();
+                while (iter.hasNext()) {
+                    removeAndCompleteMessageTransaction(
+                            iter.next().getValue(),
+                            ContextHubTransaction.RESULT_FAILED_AT_HUB,
+                            iter);
+                }
             }
         }
 
-        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
-        if (transaction == null) {
-            return;
-        }
+        synchronized (mTransactionLock) {
+            ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+            if (transaction == null) {
+                return;
+            }
 
-        removeTransactionAndStartNext();
+            removeTransactionAndStartNext();
+        }
+    }
+
+    /**
+     * This function also starts the next transaction and removes the active transaction from the
+     * queue. The caller should complete the transaction.
+     *
+     * <p>Returns the active transaction if the transaction queue is not empty, the transaction is
+     * not null, and the transaction matches the conditions.
+     */
+    private ContextHubServiceTransaction getTransactionAndHandleNext(
+            TransactionAcceptConditions conditions) {
+        ContextHubServiceTransaction transaction = null;
+        synchronized (mTransactionLock) {
+            transaction = mTransactionQueue.peek();
+            if (transaction == null
+                    || (conditions != null && !conditions.acceptTransaction(transaction))) {
+                return null;
+            }
+
+            cancelTimeoutFuture();
+            mTransactionQueue.remove();
+            if (!mTransactionQueue.isEmpty()) {
+                startNextTransaction();
+            }
+        }
+        return transaction;
     }
 
     /**
      * Pops the front transaction from the queue and starts the next pending transaction request.
-     * <p>
-     * Removing elements from the transaction queue must only be done through this method. When a
+     *
+     * <p>Removing elements from the transaction queue must only be done through this method. When a
      * pending transaction is removed, the timeout timer is cancelled and the transaction is marked
      * complete.
-     * <p>
-     * It is assumed that the transaction queue is non-empty when this method is invoked, and that
-     * the caller has obtained a lock on this ContextHubTransactionManager object.
+     *
+     * <p>It is assumed that the transaction queue is non-empty when this method is invoked, and
+     * that the caller has obtained mTransactionLock.
      */
+    @GuardedBy("mTransactionLock")
     private void removeTransactionAndStartNext() {
-        if (mTimeoutFuture != null) {
-            mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
-            mTimeoutFuture = null;
-        }
+        cancelTimeoutFuture();
 
         ContextHubServiceTransaction transaction = mTransactionQueue.remove();
-        transaction.setComplete();
+        synchronized (transaction) {
+            transaction.setComplete();
+        }
 
         if (!mTransactionQueue.isEmpty()) {
             startNextTransaction();
         }
     }
 
+    /** Cancels the timeout future. */
+    @GuardedBy("mTransactionLock")
+    private void cancelTimeoutFuture() {
+        if (mTimeoutFuture != null) {
+            mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
+            mTimeoutFuture = null;
+        }
+    }
+
     /**
      * Starts the next pending transaction request.
-     * <p>
-     * Starting new transactions must only be done through this method. This method continues to
+     *
+     * <p>Starting new transactions must only be done through this method. This method continues to
      * process the transaction queue as long as there are pending requests, and no transaction is
      * pending.
-     * <p>
-     * It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
-     * object.
+     *
+     * <p>It is assumed that the caller has obtained a lock on mTransactionLock.
      */
+    @GuardedBy("mTransactionLock")
     private void startNextTransaction() {
         int result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
         while (result != ContextHubTransaction.RESULT_SUCCESS && !mTransactionQueue.isEmpty()) {
@@ -592,28 +693,36 @@
             result = transaction.onTransact();
 
             if (result == ContextHubTransaction.RESULT_SUCCESS) {
-                Runnable onTimeoutFunc = () -> {
-                    synchronized (this) {
-                        if (!transaction.isComplete()) {
-                            Log.d(TAG, transaction + " timed out");
-                            transaction.onTransactionComplete(
-                                    ContextHubTransaction.RESULT_FAILED_TIMEOUT);
+                Runnable onTimeoutFunc =
+                        () -> {
+                            synchronized (transaction) {
+                                if (!transaction.isComplete()) {
+                                    Log.d(TAG, transaction + " timed out");
+                                    transaction.onTransactionComplete(
+                                            ContextHubTransaction.RESULT_FAILED_TIMEOUT);
+                                    transaction.setComplete();
+                                }
+                            }
 
-                            removeTransactionAndStartNext();
-                        }
-                    }
-                };
+                            synchronized (mTransactionLock) {
+                                removeTransactionAndStartNext();
+                            }
+                        };
 
                 long timeoutMs = transaction.getTimeout(TimeUnit.MILLISECONDS);
                 try {
-                    mTimeoutFuture = mExecutor.schedule(
-                            onTimeoutFunc, timeoutMs, TimeUnit.MILLISECONDS);
+                    mTimeoutFuture =
+                            mExecutor.schedule(onTimeoutFunc, timeoutMs, TimeUnit.MILLISECONDS);
                 } catch (Exception e) {
                     Log.e(TAG, "Error when schedule a timer", e);
                 }
             } else {
-                transaction.onTransactionComplete(
-                        ContextHubServiceUtil.toTransactionResult(result));
+                synchronized (transaction) {
+                    transaction.onTransactionComplete(
+                            ContextHubServiceUtil.toTransactionResult(result));
+                    transaction.setComplete();
+                }
+
                 mTransactionQueue.remove();
             }
         }
@@ -621,81 +730,97 @@
 
     /**
      * Processes message transactions, starting and completing them as needed.
-     * <p>
-     * This function is called when adding a message transaction or when a timer
-     * expires for an existing message transaction's retry or timeout. The
-     * internal processing loop will iterate at most twice as if one iteration
-     * completes a transaction, the next iteration can only start new transactions.
-     * If the first iteration does not complete any transaction, the loop will
-     * only iterate once.
+     *
+     * <p>This function is called when adding a message transaction or when a timer expires for an
+     * existing message transaction's retry or timeout. The internal processing loop will iterate at
+     * most twice as if one iteration completes a transaction, the next iteration can only start new
+     * transactions. If the first iteration does not complete any transaction, the loop will only
+     * iterate once.
+     *
      * <p>
      */
-    private synchronized void processMessageTransactions() {
-        if (!Flags.reliableMessageRetrySupportService()) {
-            return;
-        }
+    private void processMessageTransactions() {
+        synchronized (mReliableMessageLock) {
+            if (!Flags.reliableMessageRetrySupportService()) {
+                return;
+            }
 
-        if (mReliableMessageTransactionFuture != null) {
-            mReliableMessageTransactionFuture.cancel(/* mayInterruptIfRunning= */ false);
-            mReliableMessageTransactionFuture = null;
-        }
+            if (mReliableMessageTransactionFuture != null) {
+                mReliableMessageTransactionFuture.cancel(/* mayInterruptIfRunning= */ false);
+                mReliableMessageTransactionFuture = null;
+            }
 
-        long now = SystemClock.elapsedRealtimeNanos();
-        long nextExecutionTime = Long.MAX_VALUE;
-        boolean continueProcessing;
-        do {
-            continueProcessing = false;
-            Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
-                    mReliableMessageTransactionMap.entrySet().iterator();
-            while (iter.hasNext()) {
-                ContextHubServiceTransaction transaction = iter.next().getValue();
-                short hostEndpointId = transaction.getHostEndpointId();
-                int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
-                if (numCompletedStartCalls == 0
-                        && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
-                    continue;
-                }
-
-                long nextRetryTime = transaction.getNextRetryTime();
-                long timeoutTime = transaction.getTimeoutTime();
-                boolean transactionTimedOut = timeoutTime <= now;
-                boolean transactionHitMaxRetries = nextRetryTime <= now
-                        && numCompletedStartCalls > RELIABLE_MESSAGE_MAX_NUM_RETRY;
-                if (transactionTimedOut || transactionHitMaxRetries) {
-                    completeMessageTransaction(transaction,
-                            ContextHubTransaction.RESULT_FAILED_TIMEOUT, iter);
-                    continueProcessing = true;
-                } else {
-                    if (nextRetryTime <= now || numCompletedStartCalls <= 0) {
-                        startMessageTransaction(transaction, now);
+            long now = SystemClock.elapsedRealtimeNanos();
+            long nextExecutionTime = Long.MAX_VALUE;
+            boolean continueProcessing;
+            do {
+                continueProcessing = false;
+                Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+                        mReliableMessageTransactionMap.entrySet().iterator();
+                while (iter.hasNext()) {
+                    ContextHubServiceTransaction transaction = iter.next().getValue();
+                    short hostEndpointId = transaction.getHostEndpointId();
+                    int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+                    if (numCompletedStartCalls == 0
+                            && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+                        continue;
                     }
 
-                    nextExecutionTime = Math.min(nextExecutionTime,
-                            transaction.getNextRetryTime());
-                    nextExecutionTime = Math.min(nextExecutionTime,
-                            transaction.getTimeoutTime());
-                }
-            }
-        } while (continueProcessing);
+                    long nextRetryTime = transaction.getNextRetryTime();
+                    long timeoutTime = transaction.getTimeoutTime();
+                    boolean transactionTimedOut = timeoutTime <= now;
+                    boolean transactionHitMaxRetries =
+                            nextRetryTime <= now
+                                    && numCompletedStartCalls > RELIABLE_MESSAGE_MAX_NUM_RETRY;
+                    if (transactionTimedOut || transactionHitMaxRetries) {
+                        removeAndCompleteMessageTransaction(
+                                transaction, ContextHubTransaction.RESULT_FAILED_TIMEOUT, iter);
+                        continueProcessing = true;
+                    } else {
+                        if (nextRetryTime <= now || numCompletedStartCalls <= 0) {
+                            startMessageTransaction(transaction, now);
+                        }
 
-        if (nextExecutionTime < Long.MAX_VALUE) {
-            mReliableMessageTransactionFuture = mExecutor.schedule(
-                    () -> processMessageTransactions(),
-                    Math.max(nextExecutionTime - SystemClock.elapsedRealtimeNanos(),
-                            RELIABLE_MESSAGE_MIN_WAIT_TIME.toNanos()),
-                    TimeUnit.NANOSECONDS);
+                        nextExecutionTime =
+                                Math.min(nextExecutionTime, transaction.getNextRetryTime());
+                        nextExecutionTime =
+                                Math.min(nextExecutionTime, transaction.getTimeoutTime());
+                    }
+                }
+            } while (continueProcessing);
+
+            if (nextExecutionTime < Long.MAX_VALUE) {
+                mReliableMessageTransactionFuture =
+                        mExecutor.schedule(
+                                () -> processMessageTransactions(),
+                                Math.max(
+                                        nextExecutionTime - SystemClock.elapsedRealtimeNanos(),
+                                        RELIABLE_MESSAGE_MIN_WAIT_TIME.toNanos()),
+                                TimeUnit.NANOSECONDS);
+            }
         }
     }
 
     /**
-     * Completes a message transaction and removes it from the reliable message map.
+     * Completes a message transaction.
      *
      * @param transaction The transaction to complete.
      * @param result The result code.
      */
-    private void completeMessageTransaction(ContextHubServiceTransaction transaction,
-            @ContextHubTransaction.Result int result) {
-        completeMessageTransaction(transaction, result, /* iter= */ null);
+    private void completeMessageTransaction(
+            ContextHubServiceTransaction transaction, @ContextHubTransaction.Result int result) {
+        synchronized (transaction) {
+            transaction.onTransactionComplete(result);
+            transaction.setComplete();
+        }
+
+        Log.d(
+                TAG,
+                "Successfully completed reliable message transaction with "
+                        + "message sequence number = "
+                        + transaction.getMessageSequenceNumber()
+                        + " and result = "
+                        + result);
     }
 
     /**
@@ -705,25 +830,41 @@
      * @param result The result code.
      * @param iter The iterator for the reliable message map - used to remove the message directly.
      */
-    private void completeMessageTransaction(ContextHubServiceTransaction transaction,
+    @GuardedBy("mReliableMessageLock")
+    private void removeAndCompleteMessageTransaction(
+            ContextHubServiceTransaction transaction,
             @ContextHubTransaction.Result int result,
             Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter) {
-        transaction.onTransactionComplete(result);
+        removeMessageTransaction(transaction, iter);
+        completeMessageTransaction(transaction, result);
+    }
 
+    /**
+     * Removes a message transaction from the reliable message map.
+     *
+     * @param transaction The transaction to remove.
+     */
+    @GuardedBy("mReliableMessageLock")
+    private void removeMessageTransaction(ContextHubServiceTransaction transaction) {
+        removeMessageTransaction(transaction, /* iter= */ null);
+    }
+
+    /**
+     * Removes a message transaction from the reliable message map.
+     *
+     * @param transaction The transaction to remove.
+     * @param iter The iterator for the reliable message map - used to remove the message directly.
+     */
+    @GuardedBy("mReliableMessageLock")
+    private void removeMessageTransaction(
+            ContextHubServiceTransaction transaction,
+            Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter) {
         if (iter == null) {
             mReliableMessageTransactionMap.remove(transaction.getMessageSequenceNumber());
         } else {
             iter.remove();
         }
         mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
-
-    Log.d(
-        TAG,
-        "Successfully completed reliable message transaction with "
-            + "message sequence number = "
-            + transaction.getMessageSequenceNumber()
-            + " and result = "
-            + result);
     }
 
     /**
@@ -732,24 +873,25 @@
      * @param transaction The transaction to start.
      * @param now The now time.
      */
+    @GuardedBy("mReliableMessageLock")
     private void startMessageTransaction(ContextHubServiceTransaction transaction, long now) {
         int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
         @ContextHubTransaction.Result int result = transaction.onTransact();
         if (result == ContextHubTransaction.RESULT_SUCCESS) {
-      Log.d(
-          TAG,
-          "Successfully "
-              + (numCompletedStartCalls == 0 ? "started" : "retried")
-              + " reliable message transaction with message sequence number = "
-              + transaction.getMessageSequenceNumber());
+            Log.d(
+                    TAG,
+                    "Successfully "
+                            + (numCompletedStartCalls == 0 ? "started" : "retried")
+                            + " reliable message transaction with message sequence number = "
+                            + transaction.getMessageSequenceNumber());
         } else {
-      Log.w(
-          TAG,
-          "Could not start reliable message transaction with "
-              + "message sequence number = "
-              + transaction.getMessageSequenceNumber()
-              + ", result = "
-              + result);
+            Log.w(
+                    TAG,
+                    "Could not start reliable message transaction with "
+                            + "message sequence number = "
+                            + transaction.getMessageSequenceNumber()
+                            + ", result = "
+                            + result);
         }
 
         transaction.setNextRetryTime(now + RELIABLE_MESSAGE_RETRY_WAIT_TIME.toNanos());
@@ -788,17 +930,19 @@
     public String toString() {
         StringBuilder sb = new StringBuilder();
         int i = 0;
-        synchronized (this) {
-            for (ContextHubServiceTransaction transaction: mTransactionQueue) {
+        synchronized (mTransactionLock) {
+            for (ContextHubServiceTransaction transaction : mTransactionQueue) {
                 sb.append(i);
                 sb.append(": ");
                 sb.append(transaction.toString());
                 sb.append("\n");
                 ++i;
             }
+        }
 
-            if (Flags.reliableMessageRetrySupportService()) {
-                for (ContextHubServiceTransaction transaction:
+        if (Flags.reliableMessageRetrySupportService()) {
+            synchronized (mReliableMessageLock) {
+                for (ContextHubServiceTransaction transaction :
                         mReliableMessageTransactionMap.values()) {
                     sb.append(i);
                     sb.append(": ");
@@ -807,7 +951,9 @@
                     ++i;
                 }
             }
+        }
 
+        synchronized (mTransactionRecordLock) {
             sb.append("Transaction History:\n");
             Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
             while (iterator.hasNext()) {
@@ -815,6 +961,7 @@
                 sb.append("\n");
             }
         }
+
         return sb.toString();
     }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
new file mode 100644
index 0000000..a67fa30
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManagerOld.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import android.chre.flags.Flags;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.time.Duration;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages transactions at the Context Hub Service.
+ *
+ * <p>This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ * and executes them through the Context Hub. At any point in time, either the transaction queue is
+ * empty, or there is a pending transaction that is waiting for an asynchronous response from the
+ * hub. This class also handles synchronous errors and timeouts of each transaction.
+ *
+ * <p>This is the old version of ContextHubTransactionManager that uses global synchronization
+ * instead of individual locks. This will be deleted when the
+ * reduce_locking_context_hub_transaction_manager flag is cleaned up.
+ *
+ * @hide
+ */
+/* package */ class ContextHubTransactionManagerOld extends ContextHubTransactionManager {
+    /* package */ ContextHubTransactionManagerOld(
+            IContextHubWrapper contextHubProxy,
+            ContextHubClientManager clientManager,
+            NanoAppStateManager nanoAppStateManager) {
+        super(contextHubProxy, clientManager, nanoAppStateManager);
+    }
+
+    /**
+     * Adds a new transaction to the queue.
+     *
+     * <p>If there was no pending transaction at the time, the transaction that was added will be
+     * started in this method. If there were too many transactions in the queue, an exception will
+     * be thrown.
+     *
+     * @param transaction the transaction to add
+     * @throws IllegalStateException if the queue is full
+     */
+    /* package */
+    @Override
+    synchronized void addTransaction(ContextHubServiceTransaction transaction)
+            throws IllegalStateException {
+        if (transaction == null) {
+            return;
+        }
+
+        if (mTransactionQueue.size() >= MAX_PENDING_REQUESTS
+                || mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
+            throw new IllegalStateException(
+                    "Transaction queue is full (capacity = " + MAX_PENDING_REQUESTS + ")");
+        }
+
+        mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
+        if (Flags.reliableMessageRetrySupportService()
+                && transaction.getTransactionType()
+                        == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+            mReliableMessageTransactionMap.put(transaction.getMessageSequenceNumber(), transaction);
+            mExecutor.execute(() -> processMessageTransactions());
+        } else {
+            mTransactionQueue.add(transaction);
+            if (mTransactionQueue.size() == 1) {
+                startNextTransaction();
+            }
+        }
+    }
+
+    /**
+     * Handles a transaction response from a Context Hub.
+     *
+     * @param transactionId the transaction ID of the response
+     * @param success true if the transaction succeeded
+     */
+    /* package */
+    @Override
+    synchronized void onTransactionResponse(int transactionId, boolean success) {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+            return;
+        }
+        if (transaction.getTransactionId() != transactionId) {
+            Log.w(
+                    TAG,
+                    "Received unexpected transaction response (expected ID = "
+                            + transaction.getTransactionId()
+                            + ", received ID = "
+                            + transactionId
+                            + ")");
+            return;
+        }
+
+        transaction.onTransactionComplete(
+                success
+                        ? ContextHubTransaction.RESULT_SUCCESS
+                        : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+        removeTransactionAndStartNext();
+    }
+
+    /* package */
+    @Override
+    synchronized void onMessageDeliveryResponse(int messageSequenceNumber, boolean success) {
+        if (!Flags.reliableMessageRetrySupportService()) {
+            ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+            if (transaction == null) {
+                Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+                return;
+            }
+
+            int transactionMessageSequenceNumber = transaction.getMessageSequenceNumber();
+            if (transaction.getTransactionType() != ContextHubTransaction.TYPE_RELIABLE_MESSAGE
+                    || transactionMessageSequenceNumber != messageSequenceNumber) {
+                Log.w(
+                        TAG,
+                        "Received unexpected message transaction response (expected message "
+                                + "sequence number = "
+                                + transaction.getMessageSequenceNumber()
+                                + ", received messageSequenceNumber = "
+                                + messageSequenceNumber
+                                + ")");
+                return;
+            }
+
+            transaction.onTransactionComplete(
+                    success
+                            ? ContextHubTransaction.RESULT_SUCCESS
+                            : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+            removeTransactionAndStartNext();
+            return;
+        }
+
+        ContextHubServiceTransaction transaction =
+                mReliableMessageTransactionMap.get(messageSequenceNumber);
+        if (transaction == null) {
+            Log.w(
+                    TAG,
+                    "Could not find reliable message transaction with "
+                            + "message sequence number = "
+                            + messageSequenceNumber);
+            return;
+        }
+
+        completeMessageTransaction(
+                transaction,
+                success
+                        ? ContextHubTransaction.RESULT_SUCCESS
+                        : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+        mExecutor.execute(() -> processMessageTransactions());
+    }
+
+    /**
+     * Handles a query response from a Context Hub.
+     *
+     * @param nanoAppStateList the list of nanoapps included in the response
+     */
+    /* package */
+    @Override
+    synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            Log.w(TAG, "Received unexpected query response (no transaction pending)");
+            return;
+        }
+        if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+            Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
+            return;
+        }
+
+        transaction.onQueryResponse(ContextHubTransaction.RESULT_SUCCESS, nanoAppStateList);
+        removeTransactionAndStartNext();
+    }
+
+    /** Handles a hub reset event by stopping a pending transaction and starting the next. */
+    /* package */
+    @Override
+    synchronized void onHubReset() {
+        if (Flags.reliableMessageRetrySupportService()) {
+            Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+                    mReliableMessageTransactionMap.entrySet().iterator();
+            while (iter.hasNext()) {
+                completeMessageTransaction(
+                        iter.next().getValue(), ContextHubTransaction.RESULT_FAILED_AT_HUB, iter);
+            }
+        }
+
+        ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+        if (transaction == null) {
+            return;
+        }
+
+        removeTransactionAndStartNext();
+    }
+
+    /**
+     * Pops the front transaction from the queue and starts the next pending transaction request.
+     *
+     * <p>Removing elements from the transaction queue must only be done through this method. When a
+     * pending transaction is removed, the timeout timer is cancelled and the transaction is marked
+     * complete.
+     *
+     * <p>It is assumed that the transaction queue is non-empty when this method is invoked, and
+     * that the caller has obtained a lock on this ContextHubTransactionManager object.
+     */
+    private void removeTransactionAndStartNext() {
+        if (mTimeoutFuture != null) {
+            mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
+            mTimeoutFuture = null;
+        }
+
+        ContextHubServiceTransaction transaction = mTransactionQueue.remove();
+        transaction.setComplete();
+
+        if (!mTransactionQueue.isEmpty()) {
+            startNextTransaction();
+        }
+    }
+
+    /**
+     * Starts the next pending transaction request.
+     *
+     * <p>Starting new transactions must only be done through this method. This method continues to
+     * process the transaction queue as long as there are pending requests, and no transaction is
+     * pending.
+     *
+     * <p>It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
+     * object.
+     */
+    private void startNextTransaction() {
+        int result = ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+        while (result != ContextHubTransaction.RESULT_SUCCESS && !mTransactionQueue.isEmpty()) {
+            ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+            result = transaction.onTransact();
+
+            if (result == ContextHubTransaction.RESULT_SUCCESS) {
+                Runnable onTimeoutFunc =
+                        () -> {
+                            synchronized (this) {
+                                if (!transaction.isComplete()) {
+                                    Log.d(TAG, transaction + " timed out");
+                                    transaction.onTransactionComplete(
+                                            ContextHubTransaction.RESULT_FAILED_TIMEOUT);
+
+                                    removeTransactionAndStartNext();
+                                }
+                            }
+                        };
+
+                long timeoutMs = transaction.getTimeout(TimeUnit.MILLISECONDS);
+                try {
+                    mTimeoutFuture =
+                            mExecutor.schedule(onTimeoutFunc, timeoutMs, TimeUnit.MILLISECONDS);
+                } catch (Exception e) {
+                    Log.e(TAG, "Error when schedule a timer", e);
+                }
+            } else {
+                transaction.onTransactionComplete(
+                        ContextHubServiceUtil.toTransactionResult(result));
+                mTransactionQueue.remove();
+            }
+        }
+    }
+
+    /**
+     * Processes message transactions, starting and completing them as needed.
+     *
+     * <p>This function is called when adding a message transaction or when a timer expires for an
+     * existing message transaction's retry or timeout. The internal processing loop will iterate at
+     * most twice as if one iteration completes a transaction, the next iteration can only start new
+     * transactions. If the first iteration does not complete any transaction, the loop will only
+     * iterate once.
+     *
+     * <p>
+     */
+    private synchronized void processMessageTransactions() {
+        if (!Flags.reliableMessageRetrySupportService()) {
+            return;
+        }
+
+        if (mReliableMessageTransactionFuture != null) {
+            mReliableMessageTransactionFuture.cancel(/* mayInterruptIfRunning= */ false);
+            mReliableMessageTransactionFuture = null;
+        }
+
+        long now = SystemClock.elapsedRealtimeNanos();
+        long nextExecutionTime = Long.MAX_VALUE;
+        boolean continueProcessing;
+        do {
+            continueProcessing = false;
+            Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+                    mReliableMessageTransactionMap.entrySet().iterator();
+            while (iter.hasNext()) {
+                ContextHubServiceTransaction transaction = iter.next().getValue();
+                short hostEndpointId = transaction.getHostEndpointId();
+                int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+                if (numCompletedStartCalls == 0
+                        && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+                    continue;
+                }
+
+                long nextRetryTime = transaction.getNextRetryTime();
+                long timeoutTime = transaction.getTimeoutTime();
+                boolean transactionTimedOut = timeoutTime <= now;
+                boolean transactionHitMaxRetries =
+                        nextRetryTime <= now
+                                && numCompletedStartCalls > RELIABLE_MESSAGE_MAX_NUM_RETRY;
+                if (transactionTimedOut || transactionHitMaxRetries) {
+                    completeMessageTransaction(
+                            transaction, ContextHubTransaction.RESULT_FAILED_TIMEOUT, iter);
+                    continueProcessing = true;
+                } else {
+                    if (nextRetryTime <= now || numCompletedStartCalls <= 0) {
+                        startMessageTransaction(transaction, now);
+                    }
+
+                    nextExecutionTime = Math.min(nextExecutionTime, transaction.getNextRetryTime());
+                    nextExecutionTime = Math.min(nextExecutionTime, transaction.getTimeoutTime());
+                }
+            }
+        } while (continueProcessing);
+
+        if (nextExecutionTime < Long.MAX_VALUE) {
+            mReliableMessageTransactionFuture =
+                    mExecutor.schedule(
+                            () -> processMessageTransactions(),
+                            Math.max(
+                                    nextExecutionTime - SystemClock.elapsedRealtimeNanos(),
+                                    RELIABLE_MESSAGE_MIN_WAIT_TIME.toNanos()),
+                            TimeUnit.NANOSECONDS);
+        }
+    }
+
+    /**
+     * Completes a message transaction and removes it from the reliable message map.
+     *
+     * @param transaction The transaction to complete.
+     * @param result The result code.
+     */
+    private void completeMessageTransaction(
+            ContextHubServiceTransaction transaction, @ContextHubTransaction.Result int result) {
+        completeMessageTransaction(transaction, result, /* iter= */ null);
+    }
+
+    /**
+     * Completes a message transaction and removes it from the reliable message map using iter.
+     *
+     * @param transaction The transaction to complete.
+     * @param result The result code.
+     * @param iter The iterator for the reliable message map - used to remove the message directly.
+     */
+    private void completeMessageTransaction(
+            ContextHubServiceTransaction transaction,
+            @ContextHubTransaction.Result int result,
+            Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter) {
+        transaction.onTransactionComplete(result);
+
+        if (iter == null) {
+            mReliableMessageTransactionMap.remove(transaction.getMessageSequenceNumber());
+        } else {
+            iter.remove();
+        }
+        mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
+
+        Log.d(
+                TAG,
+                "Successfully completed reliable message transaction with "
+                        + "message sequence number = "
+                        + transaction.getMessageSequenceNumber()
+                        + " and result = "
+                        + result);
+    }
+
+    /**
+     * Starts a message transaction.
+     *
+     * @param transaction The transaction to start.
+     * @param now The now time.
+     */
+    private void startMessageTransaction(ContextHubServiceTransaction transaction, long now) {
+        int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+        @ContextHubTransaction.Result int result = transaction.onTransact();
+        if (result == ContextHubTransaction.RESULT_SUCCESS) {
+            Log.d(
+                    TAG,
+                    "Successfully "
+                            + (numCompletedStartCalls == 0 ? "started" : "retried")
+                            + " reliable message transaction with message sequence number = "
+                            + transaction.getMessageSequenceNumber());
+        } else {
+            Log.w(
+                    TAG,
+                    "Could not start reliable message transaction with "
+                            + "message sequence number = "
+                            + transaction.getMessageSequenceNumber()
+                            + ", result = "
+                            + result);
+        }
+
+        transaction.setNextRetryTime(now + RELIABLE_MESSAGE_RETRY_WAIT_TIME.toNanos());
+        if (transaction.getTimeoutTime() == Long.MAX_VALUE) { // first time starting transaction
+            transaction.setTimeoutTime(now + RELIABLE_MESSAGE_TIMEOUT.toNanos());
+        }
+        transaction.setNumCompletedStartCalls(numCompletedStartCalls + 1);
+        mReliableMessageHostEndpointIdActiveSet.add(transaction.getHostEndpointId());
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        int i = 0;
+        synchronized (this) {
+            for (ContextHubServiceTransaction transaction : mTransactionQueue) {
+                sb.append(i);
+                sb.append(": ");
+                sb.append(transaction.toString());
+                sb.append("\n");
+                ++i;
+            }
+
+            if (Flags.reliableMessageRetrySupportService()) {
+                for (ContextHubServiceTransaction transaction :
+                        mReliableMessageTransactionMap.values()) {
+                    sb.append(i);
+                    sb.append(": ");
+                    sb.append(transaction.toString());
+                    sb.append("\n");
+                    ++i;
+                }
+            }
+
+            sb.append("Transaction History:\n");
+            Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
+            while (iterator.hasNext()) {
+                sb.append(iterator.next());
+                sb.append("\n");
+            }
+        }
+        return sb.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index c02b103..404c841 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.os.Environment;
 import android.security.keystore.recovery.KeyChainSnapshot;
-import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,9 +28,11 @@
 import com.android.server.locksettings.recoverablekeystore.serialization
         .KeyChainSnapshotParserException;
 import com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSerializer;
+import com.android.server.utils.Slogf;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.security.cert.CertificateEncodingException;
@@ -81,12 +82,14 @@
     public synchronized void put(int uid, KeyChainSnapshot snapshot) {
         mSnapshotByUid.put(uid, snapshot);
 
-        try {
-            writeToDisk(uid, snapshot);
+        File snapshotFile = getSnapshotFile(uid);
+        try (FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)) {
+            KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream);
         } catch (IOException | CertificateEncodingException e) {
-            Log.e(TAG,
-                    String.format(Locale.US, "Error persisting snapshot for %d to disk", uid),
-                    e);
+            // If we fail to write the latest snapshot, we should delete any older snapshot that
+            // happens to be around. Otherwise snapshot syncs might end up going 'back in time'.
+            snapshotFile.delete();
+            Slogf.e(TAG, e, "Error persisting snapshot for %d to disk", uid);
         }
     }
 
@@ -100,10 +103,17 @@
             return snapshot;
         }
 
-        try {
-            return readFromDisk(uid);
+        File snapshotFile = getSnapshotFile(uid);
+        try (FileInputStream fileInputStream = new FileInputStream(snapshotFile)) {
+            return KeyChainSnapshotDeserializer.deserialize(fileInputStream);
+        } catch (FileNotFoundException e) {
+            Slogf.i(TAG, "Snapshot for uid %d not found", uid);
+            return null;
         } catch (IOException | KeyChainSnapshotParserException e) {
-            Log.e(TAG, String.format(Locale.US, "Error reading snapshot for %d from disk", uid), e);
+            // If we fail to read the latest snapshot, we should delete it in case it is in some way
+            // corrupted. We can regenerate snapshots anyway.
+            snapshotFile.delete();
+            Slogf.e(TAG, e, "Error reading snapshot for %d from disk", uid);
             return null;
         }
     }
@@ -116,50 +126,6 @@
         getSnapshotFile(uid).delete();
     }
 
-    /**
-     * Writes the snapshot for recovery agent {@code uid} to disk.
-     *
-     * @throws IOException if an IO error occurs writing to disk.
-     */
-    private void writeToDisk(int uid, KeyChainSnapshot snapshot)
-            throws IOException, CertificateEncodingException {
-        File snapshotFile = getSnapshotFile(uid);
-
-        try (
-            FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)
-        ) {
-            KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream);
-        } catch (IOException | CertificateEncodingException e) {
-            // If we fail to write the latest snapshot, we should delete any older snapshot that
-            // happens to be around. Otherwise snapshot syncs might end up going 'back in time'.
-            snapshotFile.delete();
-            throw e;
-        }
-    }
-
-    /**
-     * Reads the last snapshot for recovery agent {@code uid} from disk.
-     *
-     * @return The snapshot, or null if none existed.
-     * @throws IOException if an IO error occurs reading from disk.
-     */
-    @Nullable
-    private KeyChainSnapshot readFromDisk(int uid)
-            throws IOException, KeyChainSnapshotParserException {
-        File snapshotFile = getSnapshotFile(uid);
-
-        try (
-            FileInputStream fileInputStream = new FileInputStream(snapshotFile)
-        ) {
-            return KeyChainSnapshotDeserializer.deserialize(fileInputStream);
-        } catch (IOException | KeyChainSnapshotParserException e) {
-            // If we fail to read the latest snapshot, we should delete it in case it is in some way
-            // corrupted. We can regenerate snapshots anyway.
-            snapshotFile.delete();
-            throw e;
-        }
-    }
-
     private File getSnapshotFile(int uid) {
         File folder = getStorageFolder();
         String fileName = getSnapshotFileName(uid);
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index c79f41d..25e1c94 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -420,7 +420,7 @@
         // to derive a name ourselves from the type instead.
         String deviceName = audioDeviceInfo.getPort().name();
 
-        if (!TextUtils.isEmpty(address)) {
+        if (mBluetoothRouteController.containsBondedDeviceWithAddress(address)) {
             routeId = mBluetoothRouteController.getRouteIdForBluetoothAddress(address);
             deviceName = mBluetoothRouteController.getNameForBluetoothAddress(address);
         }
diff --git a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
index 8b65ea3..c79d6e5 100644
--- a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
+++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
@@ -141,6 +141,11 @@
         mContext.unregisterReceiver(mDeviceStateChangedReceiver);
     }
 
+    /** Returns true if the given address corresponds to a currently-bonded Bluetooth device. */
+    public synchronized boolean containsBondedDeviceWithAddress(@Nullable String address) {
+        return mAddressToBondedDevice.containsKey(address);
+    }
+
     @Nullable
     public synchronized String getRouteIdForBluetoothAddress(@Nullable String address) {
         BluetoothDevice bluetoothDevice = mAddressToBondedDevice.get(address);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index d1d5d48..27bc1cf 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -677,24 +677,15 @@
             @NonNull IMediaRouter2Manager manager,
             int requestId,
             @NonNull RoutingSessionInfo oldSession,
-            @NonNull MediaRoute2Info route,
-            @NonNull UserHandle transferInitiatorUserHandle,
-            @NonNull String transferInitiatorPackageName) {
+            @NonNull MediaRoute2Info route) {
         Objects.requireNonNull(manager, "manager must not be null");
         Objects.requireNonNull(oldSession, "oldSession must not be null");
         Objects.requireNonNull(route, "route must not be null");
-        Objects.requireNonNull(transferInitiatorUserHandle);
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestCreateSessionWithManagerLocked(
-                        requestId,
-                        manager,
-                        oldSession,
-                        route,
-                        transferInitiatorUserHandle,
-                        transferInitiatorPackageName);
+                requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -1738,9 +1729,7 @@
             int requestId,
             @NonNull IMediaRouter2Manager manager,
             @NonNull RoutingSessionInfo oldSession,
-            @NonNull MediaRoute2Info route,
-            @NonNull UserHandle transferInitiatorUserHandle,
-            @NonNull String transferInitiatorPackageName) {
+            @NonNull MediaRoute2Info route) {
         ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
         if (managerRecord == null) {
             return;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 363b8e4..68e195d 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -607,16 +607,8 @@
             IMediaRouter2Manager manager,
             int requestId,
             RoutingSessionInfo oldSession,
-            MediaRoute2Info route,
-            UserHandle transferInitiatorUserHandle,
-            String transferInitiatorPackageName) {
-        mService2.requestCreateSessionWithManager(
-                manager,
-                requestId,
-                oldSession,
-                route,
-                transferInitiatorUserHandle,
-                transferInitiatorPackageName);
+            MediaRoute2Info route) {
+        mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index e7e519e..e0913cc 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -28,6 +28,7 @@
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK;
 import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN;
+import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -73,6 +74,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.ContentRecordingSession;
@@ -195,6 +197,15 @@
             if (mProjectionGrant == null || mProjectionGrant.packageName == null) {
                 return false;
             }
+            boolean disableScreenShareProtections = Settings.Global.getInt(
+                    getContext().getContentResolver(),
+                    DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0;
+            if (disableScreenShareProtections) {
+                Slog.v(TAG,
+                        "Allowing keyguard capture as screenshare protections are disabled.");
+                return true;
+            }
+
             if (mPackageManager.checkPermission(RECORD_SENSITIVE_CONTENT,
                     mProjectionGrant.packageName)
                     == PackageManager.PERMISSION_GRANTED) {
@@ -226,7 +237,8 @@
     void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
         if (!isKeyguardLocked) return;
         synchronized (mLock) {
-            if (mProjectionGrant != null && !canCaptureKeyguard()) {
+            if (mProjectionGrant != null && !canCaptureKeyguard()
+                    && mProjectionGrant.mVirtualDisplayId != INVALID_DISPLAY) {
                 Slog.d(TAG, "Content Recording: Stopped MediaProjection"
                         + " due to keyguard lock");
                 mProjectionGrant.stop();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a6f4c0e..2a3be1e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3100,11 +3100,16 @@
         }
 
         synchronized (mUidRulesFirstLock) {
-            final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
-            policy |= oldPolicy;
-            if (oldPolicy != policy) {
-                setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
-                mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+                policy |= oldPolicy;
+                if (oldPolicy != policy) {
+                    setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+                    mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
@@ -3119,11 +3124,16 @@
         }
 
         synchronized (mUidRulesFirstLock) {
-            final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
-            policy = oldPolicy & ~policy;
-            if (oldPolicy != policy) {
-                setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
-                mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+                policy = oldPolicy & ~policy;
+                if (oldPolicy != policy) {
+                    setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+                    mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index bbc7c01..4596a44 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -2,7 +2,5 @@
 file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
 per-file NetworkPolicyManagerService.java=jackyu@google.com, sarahchin@google.com
 
-jsharkey@android.com
 sudheersai@google.com
-yamasani@google.com
 suprabh@google.com
diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java
index cfcf6eb..f186f42 100644
--- a/services/core/java/com/android/server/notification/CalendarTracker.java
+++ b/services/core/java/com/android/server/notification/CalendarTracker.java
@@ -32,6 +32,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.Date;
 import java.util.Objects;
@@ -296,4 +298,8 @@
         void onChanged();
     }
 
+    @VisibleForTesting // (otherwise = NONE)
+    public int getUserId() {
+        return mUserContext.getUserId();
+    }
 }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 66e61c0..0b40d64 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -169,16 +169,15 @@
         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
             mSystemConditionProviders.valueAt(i).onBootComplete();
         }
-        if (mCallback != null) {
-            mCallback.onBootComplete();
-        }
     }
 
     @Override
     public void onUserSwitched(int user) {
         super.onUserSwitched(user);
-        if (mCallback != null) {
-            mCallback.onUserSwitched();
+        if (android.app.Flags.modesHsum()) {
+            for (int i = 0; i < mSystemConditionProviders.size(); i++) {
+                mSystemConditionProviders.valueAt(i).onUserSwitched(UserHandle.of(user));
+            }
         }
     }
 
@@ -515,10 +514,8 @@
     }
 
     public interface Callback {
-        void onBootComplete();
         void onServiceAdded(ComponentName component);
         void onConditionChanged(Uri id, Condition condition);
-        void onUserSwitched();
     }
 
 }
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index c3ace15..bb8ccd2 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.text.format.DateUtils;
@@ -65,6 +66,11 @@
     }
 
     @Override
+    public void onUserSwitched(UserHandle user) {
+        // Nothing to do because countdown conditions are not tied to any user data.
+    }
+
+    @Override
     public void dump(PrintWriter pw, DumpFilter filter) {
         pw.println("    CountdownConditionProvider:");
         pw.print("      mConnected="); pw.println(mConnected);
diff --git a/services/core/java/com/android/server/notification/CustomManualConditionProvider.java b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java
index 9531c5e..52dc116 100644
--- a/services/core/java/com/android/server/notification/CustomManualConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import android.net.Uri;
+import android.os.UserHandle;
 import android.service.notification.ZenModeConfig;
 
 import java.io.PrintWriter;
@@ -40,6 +41,11 @@
     }
 
     @Override
+    public void onUserSwitched(UserHandle user) {
+        // Nothing to do because we won't ever call notifyConditions.
+    }
+
+    @Override
     public void onConnected() {
         // No need to keep subscriptions because we won't ever call notifyConditions
     }
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 00dd547..ecc4cf7 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -23,6 +24,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -37,6 +39,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.notification.CalendarTracker.CheckEventResult;
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 import com.android.server.pm.PackageManagerService;
@@ -59,12 +62,13 @@
     private static final String EXTRA_TIME = "time";
     private static final long CHANGE_DELAY = 2 * 1000;  // coalesce chatty calendar changes
 
-    private final Context mContext = this;
+    @VisibleForTesting Context mContext = this;
     private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
     private final SparseArray<CalendarTracker> mTrackers = new SparseArray<>();
     private final Handler mWorker;
     private final HandlerThread mThread;
 
+    @Nullable private UserHandle mCurrentUser;
     private boolean mConnected;
     private boolean mRegistered;
     private boolean mBootComplete;  // don't hammer the calendar provider until boot completes.
@@ -114,10 +118,28 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                reloadTrackers();
+                if (android.app.Flags.modesHsum()) {
+                    if (mCurrentUser != null) {
+                        // Possibly the intent signals a profile added on a different user, but it
+                        // doesn't matter (except for a bit of wasted work here). We will reload
+                        // trackers for that user when we switch.
+                        reloadTrackers(mCurrentUser);
+                    }
+                } else {
+                    reloadTrackers();
+                }
             }
         }, filter);
-        reloadTrackers();
+        if (!android.app.Flags.modesHsum()) {
+            reloadTrackers();
+        }
+    }
+
+    @Override
+    public void onUserSwitched(UserHandle user) {
+        if (DEBUG) Slog.d(TAG, "onUserSwitched: " + user);
+        mCurrentUser = user;
+        reloadTrackers(user);
     }
 
     @Override
@@ -157,8 +179,41 @@
         }
     }
 
+    private void reloadTrackers(UserHandle user) {
+        if (DEBUG) Slog.d(TAG, "reloadTrackers user=" + user);
+        for (int i = 0; i < mTrackers.size(); i++) {
+            mTrackers.valueAt(i).setCallback(null);
+        }
+        mTrackers.clear();
+
+        // Ensure that user is the main user and not a profile.
+        UserManager userManager = UserManager.get(mContext);
+        UserHandle possibleParent = userManager.getProfileParent(user);
+        if (possibleParent != null) {
+            Slog.wtf(TAG, "reloadTrackers should not be called with profile " + user
+                    + "; continuing with parent " + possibleParent);
+            user = possibleParent;
+        }
+
+        for (UserInfo profile : userManager.getProfiles(user.getIdentifier())) {
+            final Context profileContext = getContextForUser(mContext, profile.getUserHandle());
+            if (profileContext == null) {
+                Slog.w(TAG, "Unable to create context for profile " + profile.id + " of user "
+                        + user.getIdentifier());
+                continue;
+            }
+            mTrackers.put(profile.id, new CalendarTracker(mContext, profileContext));
+        }
+        evaluateSubscriptions();
+    }
+
+    @Deprecated // Remove when inlining MODES_HSUM
     private void reloadTrackers() {
         if (DEBUG) Slog.d(TAG, "reloadTrackers");
+        if (android.app.Flags.modesHsum()) {
+            Slog.wtf(TAG, "Shouldn't call reloadTrackers() without user in MODES_HSUM",
+                    new Exception());
+        }
         for (int i = 0; i < mTrackers.size(); i++) {
             mTrackers.valueAt(i).setCallback(null);
         }
@@ -325,4 +380,9 @@
             evaluateSubscriptionsW();
         }
     };
+
+    @VisibleForTesting // (otherwise = NONE)
+    public SparseArray<CalendarTracker> getTrackers() {
+        return mTrackers;
+    }
 }
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 9b9be4c..6681e36 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -29,6 +29,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -88,6 +89,7 @@
     private final int mAutogroupSparseGroupsAtCount;
     private final Context mContext;
     private final PackageManager mPackageManager;
+    private boolean mIsTestHarnessExempted;
 
     // Only contains notifications that are not explicitly grouped by the app (aka no group or
     // sort key).
@@ -174,6 +176,11 @@
         NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections();
     }
 
+    void setTestHarnessExempted(boolean isExempted) {
+        // Allow E2E tests to post ungrouped notifications
+        mIsTestHarnessExempted = ActivityManager.isRunningInUserTestHarness() && isExempted;
+    }
+
     private String generatePackageKey(int userId, String pkg) {
         return userId + "|" + pkg;
     }
@@ -696,6 +703,10 @@
             return;
         }
 
+        if (mIsTestHarnessExempted) {
+            return;
+        }
+
         final NotificationSectioner sectioner = getSection(record);
         if (sectioner == null) {
             if (DEBUG) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index cd0a2a7..122836e 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -21,6 +21,9 @@
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_INSTANT;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
@@ -713,13 +716,21 @@
                         }
                     }
                     readExtraAttributes(tag, parser, resolvedUserId);
-                    if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
-                            getPackageName(approved), resolvedUserId, getRequiredPermission())
-                            || approved.isEmpty()) {
-                        if (mUm.getUserInfo(resolvedUserId) != null) {
-                            addApprovedList(approved, resolvedUserId, isPrimary, userSetComponent);
-                        }
+                    if (isUserChanged != null && approved.isEmpty()) {
+                        // NAS
+                        denyPregrantedAppUserSet(resolvedUserId, isPrimary);
                         mUseXml = true;
+                    } else {
+                        if (allowedManagedServicePackages == null
+                                || allowedManagedServicePackages.test(
+                                getPackageName(approved), resolvedUserId, getRequiredPermission())
+                                || approved.isEmpty()) {
+                            if (mUm.getUserInfo(resolvedUserId) != null) {
+                                addApprovedList(approved, resolvedUserId, isPrimary,
+                                        userSetComponent);
+                            }
+                            mUseXml = true;
+                        }
                     }
                 } else {
                     readExtraTag(tag, parser);
@@ -827,6 +838,17 @@
         }
     }
 
+    protected void denyPregrantedAppUserSet(int userId, boolean isPrimary) {
+        synchronized (mApproved) {
+            ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
+            if (approvedByType == null) {
+                approvedByType = new ArrayMap<>();
+                mApproved.put(userId, approvedByType);
+            }
+            approvedByType.put(isPrimary, new ArraySet<>());
+        }
+    }
+
     protected boolean isComponentEnabledForPackage(String pkg) {
         synchronized (mMutex) {
             return mEnabledServicesPackageNames.contains(pkg);
@@ -859,10 +881,18 @@
             if (approvedItem != null) {
                 final ComponentName component = ComponentName.unflattenFromString(approvedItem);
                 if (enabled) {
-                    if (component != null && !isValidService(component, userId)) {
-                        Log.e(TAG, "Skip allowing " + mConfig.caption + " " + pkgOrComponent
-                                + " (userSet: " + userSet + ") for invalid service");
-                        return;
+                    if (Flags.notificationNlsRebind()) {
+                        if (component != null && !isValidService(component, userId)) {
+                            // Only fail if package is available
+                            // If not, it will be validated again in onPackagesChanged
+                            final PackageManager pm = mContext.getPackageManager();
+                            if (pm.isPackageAvailable(component.getPackageName())) {
+                                Slog.w(TAG, "Skip allowing " + mConfig.caption
+                                        + " " + pkgOrComponent + " (userSet: " + userSet
+                                        + ") for invalid service");
+                                return;
+                            }
+                        }
                     }
                     approved.add(approvedItem);
                 } else {
@@ -1208,12 +1238,21 @@
         if (!TextUtils.isEmpty(packageName)) {
             queryIntent.setPackage(packageName);
         }
+
+        if (Flags.notificationNlsRebind()) {
+            // Expand the package query
+            extraFlags |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+            extraFlags |= MATCH_INSTANT;
+        }
+
         List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
                 queryIntent,
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA | extraFlags,
                 userId);
-        if (DEBUG)
-            Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
+        if (DEBUG) {
+            Slog.v(TAG, mConfig.serviceInterface + " pkg: " + packageName + " services: "
+                    + installedServices);
+        }
         if (installedServices != null) {
             for (int i = 0, count = installedServices.size(); i < count; i++) {
                 ResolveInfo resolveInfo = installedServices.get(i);
@@ -1338,8 +1377,12 @@
     }
 
     protected boolean isValidService(ComponentName component, int userId) {
-        return componentHasBindPermission(component, userId) && queryPackageForServices(
-                component.getPackageName(), userId).contains(component);
+        if (Flags.notificationNlsRebind()) {
+            return componentHasBindPermission(component, userId) && queryPackageForServices(
+                    component.getPackageName(), userId).contains(component);
+        } else {
+            return componentHasBindPermission(component, userId);
+        }
     }
 
     protected boolean isValidEntry(String packageOrComponent, int userId) {
@@ -1516,8 +1559,10 @@
     void unbindServicesImpl(int user, boolean allExceptUser) {
         final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
         synchronized (mMutex) {
-            // Remove enqueued rebinds to avoid rebinding services for a switched user
-            mHandler.removeMessages(ON_BINDING_DIED_REBIND_MSG);
+            if (Flags.notificationNlsRebind()) {
+                // Remove enqueued rebinds to avoid rebinding services for a switched user
+                mHandler.removeMessages(ON_BINDING_DIED_REBIND_MSG);
+            }
             final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
             for (ManagedServiceInfo info : removableBoundServices) {
                 if ((allExceptUser && (info.userid != user))
diff --git a/core/java/com/android/server/backup/NotificationBackupHelper.java b/services/core/java/com/android/server/notification/NotificationBackupHelper.java
similarity index 67%
rename from core/java/com/android/server/backup/NotificationBackupHelper.java
rename to services/core/java/com/android/server/notification/NotificationBackupHelper.java
index faa0509..ee9ec15 100644
--- a/core/java/com/android/server/backup/NotificationBackupHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationBackupHelper.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.backup;
+package com.android.server.notification;
 
 import android.app.INotificationManager;
 import android.app.backup.BlobBackupHelper;
@@ -22,6 +22,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.server.LocalServices;
+
 public class NotificationBackupHelper extends BlobBackupHelper {
     static final String TAG = "NotifBackupHelper";   // must be < 23 chars
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -34,9 +36,13 @@
 
     private final int mUserId;
 
+    private final NotificationManagerInternal mNm;
+
     public NotificationBackupHelper(int userId) {
         super(BLOB_VERSION, KEY_NOTIFICATIONS);
         mUserId = userId;
+
+        mNm = LocalServices.getService(NotificationManagerInternal.class);
     }
 
     @Override
@@ -44,9 +50,13 @@
         byte[] newPayload = null;
         if (KEY_NOTIFICATIONS.equals(key)) {
             try {
-                INotificationManager nm = INotificationManager.Stub.asInterface(
-                        ServiceManager.getService("notification"));
-                newPayload = nm.getBackupPayload(mUserId);
+                if (android.app.Flags.backupRestoreLogging()) {
+                    newPayload = mNm.getBackupPayload(mUserId, getLogger());
+                } else {
+                    INotificationManager nm = INotificationManager.Stub.asInterface(
+                            ServiceManager.getService("notification"));
+                    newPayload = nm.getBackupPayload(mUserId);
+                }
             } catch (Exception e) {
                 // Treat as no data
                 Slog.e(TAG, "Couldn't communicate with notification manager", e);
@@ -64,9 +74,13 @@
 
         if (KEY_NOTIFICATIONS.equals(key)) {
             try {
-                INotificationManager nm = INotificationManager.Stub.asInterface(
-                        ServiceManager.getService("notification"));
-                nm.applyRestore(payload, mUserId);
+                if (android.app.Flags.backupRestoreLogging()) {
+                    mNm.applyRestore(payload, mUserId, getLogger());
+                } else {
+                    INotificationManager nm = INotificationManager.Stub.asInterface(
+                            ServiceManager.getService("notification"));
+                    nm.applyRestore(payload, mUserId);
+                }
             } catch (Exception e) {
                 Slog.e(TAG, "Couldn't communicate with notification manager", e);
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 4b8de4e..d5d4070 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -19,6 +19,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
+import android.app.backup.BackupRestoreEventLogger;
 import android.service.notification.DeviceEffectsApplier;
 
 import java.util.Set;
@@ -43,7 +44,7 @@
 
     void onConversationRemoved(String pkg, int uid, Set<String> shortcuts);
 
-    /** Get the number of notification channels for a given package */
+    /** Get the number of app created notification channels for a given package */
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
 
     /** Does the specified package/uid have permission to post notifications? */
@@ -73,4 +74,9 @@
      * Otherwise an {@link IllegalStateException} will be thrown.
      */
     void setDeviceEffectsApplier(DeviceEffectsApplier applier);
+
+    // Backup/restore interface
+    byte[] getBackupPayload(int user, BackupRestoreEventLogger logger);
+
+    void applyRestore(byte[] payload, int user, BackupRestoreEventLogger logger);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6c2d4f7..e48c8ee 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -216,6 +216,7 @@
 import android.app.UriGrantsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.backup.BackupManager;
+import android.app.backup.BackupRestoreEventLogger;
 import android.app.compat.CompatChanges;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
@@ -462,7 +463,7 @@
     static final int INVALID_UID = -1;
     static final String ROOT_PKG = "root";
 
-    static final String[] ALLOWED_ADJUSTMENTS = new String[] {
+    static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
             Adjustment.KEY_PEOPLE,
             Adjustment.KEY_SNOOZE_CRITERIA,
             Adjustment.KEY_USER_SENTIMENT,
@@ -1102,7 +1103,8 @@
         return false;
     }
 
-    void readPolicyXml(InputStream stream, boolean forRestore, int userId)
+    void readPolicyXml(InputStream stream, boolean forRestore, int userId,
+            BackupRestoreEventLogger logger)
             throws XmlPullParserException, NumberFormatException, IOException {
         final TypedXmlPullParser parser;
         if (forRestore) {
@@ -1189,7 +1191,7 @@
             InputStream infile = null;
             try {
                 infile = mPolicyFile.openRead();
-                readPolicyXml(infile, false /*forRestore*/, USER_ALL);
+                readPolicyXml(infile, false /*forRestore*/, USER_ALL, null);
 
                 // We re-load the default dnd packages to allow the newly added and denined.
                 final boolean isWatch = mPackageManagerClient.hasSystemFeature(
@@ -1239,7 +1241,7 @@
                 }
 
                 try {
-                    writePolicyXml(stream, false /*forBackup*/, USER_ALL);
+                    writePolicyXml(stream, false /*forBackup*/, USER_ALL, null);
                     mPolicyFile.finishWrite(stream);
                 } catch (IOException e) {
                     Slog.w(TAG, "Failed to save policy file, restoring backup", e);
@@ -1250,8 +1252,8 @@
         }
     }
 
-    private void writePolicyXml(OutputStream stream, boolean forBackup, int userId)
-            throws IOException {
+    private void writePolicyXml(OutputStream stream, boolean forBackup, int userId,
+            BackupRestoreEventLogger logger)  throws IOException {
         final TypedXmlSerializer out;
         if (forBackup) {
             out = Xml.newFastSerializer();
@@ -2982,6 +2984,11 @@
         });
     }
 
+    //Enables tests running in TH mode to be exempted from forced grouping of notifications
+    void setTestHarnessExempted(boolean isExempted) {
+        mGroupHelper.setTestHarnessExempted(isExempted);
+    }
+
     private void sendRegisteredOnlyBroadcast(String action) {
         sendRegisteredOnlyBroadcast(new Intent(action));
     }
@@ -4119,6 +4126,24 @@
 
         @Override
         @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+        public void allowAssistantAdjustment(String adjustmentType) {
+            checkCallerIsSystemOrSystemUiOrShell();
+            mAssistants.allowAdjustmentType(adjustmentType);
+
+            handleSavePolicyFile();
+        }
+
+        @Override
+        @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+        public void disallowAssistantAdjustment(String adjustmentType) {
+            checkCallerIsSystemOrSystemUiOrShell();
+            mAssistants.disallowAdjustmentType(adjustmentType);
+
+            handleSavePolicyFile();
+        }
+
+        @Override
+        @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
         public void setAdjustmentTypeSupportedState(INotificationListener token,
                 @Adjustment.Keys String key, boolean supported) {
             final long identity = Binder.clearCallingIdentity();
@@ -4133,6 +4158,7 @@
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
+            handleSavePolicyFile();
         }
 
         @Override
@@ -4371,8 +4397,9 @@
             List<NotificationChannel> channels = channelsList.getList();
             final int channelsSize = channels.size();
             ParceledListSlice<NotificationChannel> oldChannels =
-                    mPreferencesHelper.getNotificationChannels(pkg, uid, true);
-            final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty();
+                    mPreferencesHelper.getNotificationChannels(pkg, uid, true, false);
+            final boolean hadNonBundleChannel =
+                    oldChannels != null && !oldChannels.getList().isEmpty();
             boolean needsPolicyFileChange = false;
             boolean hasRequestedNotificationPermission = false;
             for (int i = 0; i < channelsSize; i++) {
@@ -4389,13 +4416,18 @@
                             mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(),
                                     false),
                             NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
-                    boolean hasChannel = hadChannel || hasRequestedNotificationPermission;
-                    if (!hasChannel) {
+                    boolean hasNonBundleChannel =
+                            hadNonBundleChannel || hasRequestedNotificationPermission;
+                    if (!hasNonBundleChannel) {
                         ParceledListSlice<NotificationChannel> currChannels =
-                                mPreferencesHelper.getNotificationChannels(pkg, uid, true);
-                        hasChannel = currChannels != null && !currChannels.getList().isEmpty();
+                                mPreferencesHelper.getNotificationChannels(pkg, uid, true, false);
+                        hasNonBundleChannel =
+                                currChannels != null && !currChannels.getList().isEmpty();
                     }
-                    if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
+                    // show perm prompt if new non-bundle channel added and the user has not
+                    // seen the prompt
+                    if (!hadNonBundleChannel && hasNonBundleChannel
+                            && !hasRequestedNotificationPermission
                             && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
                         hasRequestedNotificationPermission = true;
                         if (mPermissionPolicyInternal == null) {
@@ -4630,7 +4662,7 @@
         public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
                 int uid, boolean includeDeleted) {
             enforceSystemOrSystemUI("getNotificationChannelsForPackage");
-            return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted);
+            return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted, true);
         }
 
         @Override
@@ -4762,7 +4794,7 @@
                     /* ignore */
                 }
                 return mPreferencesHelper.getNotificationChannels(
-                        targetPkg, targetUid, false /* includeDeleted */);
+                        targetPkg, targetUid, false /* includeDeleted */, true);
             }
             throw new SecurityException("Pkg " + callingPkg
                     + " cannot read channels for " + targetPkg + " in " + userId);
@@ -4891,7 +4923,7 @@
                     throw new SecurityException("Not currently an assistant");
             }
 
-            return mAssistants.getAllowedAssistantAdjustments();
+            return new ArrayList<>(mAssistants.getAllowedAssistantAdjustments());
         }
 
         /**
@@ -6171,7 +6203,7 @@
             if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
             try {
-                writePolicyXml(baos, true /*forBackup*/, user);
+                writePolicyXml(baos, true /*forBackup*/, user, null);
                 return baos.toByteArray();
             } catch (IOException e) {
                 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
@@ -6190,7 +6222,7 @@
             }
             final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
             try {
-                readPolicyXml(bais, true /*forRestore*/, user);
+                readPolicyXml(bais, true /*forRestore*/, user, null);
                 handleSavePolicyFile();
             } catch (NumberFormatException | XmlPullParserException | IOException e) {
                 Slog.w(TAG, "applyRestore: error reading payload", e);
@@ -6631,7 +6663,7 @@
             verifyPrivilegedListener(token, user, true);
 
             return mPreferencesHelper.getNotificationChannels(pkg,
-                    getUidForPackageAndUser(pkg, user), false /* includeDeleted */);
+                    getUidForPackageAndUser(pkg, user), false /* includeDeleted */, true);
         }
 
         @Override
@@ -7392,6 +7424,37 @@
      */
     private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
 
+        public byte[] getBackupPayload(int user, BackupRestoreEventLogger logger) {
+            checkCallerIsSystem();
+            if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
+            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            try {
+                writePolicyXml(baos, true /*forBackup*/, user, logger);
+                return baos.toByteArray();
+            } catch (IOException e) {
+                Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
+            }
+            return null;
+        }
+
+        @Override
+        public void applyRestore(byte[] payload, int user, BackupRestoreEventLogger logger) {
+            checkCallerIsSystem();
+            if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
+                    + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
+            if (payload == null) {
+                Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
+                return;
+            }
+            final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+            try {
+                readPolicyXml(bais, true /*forRestore*/, user, logger);
+                handleSavePolicyFile();
+            } catch (NumberFormatException | XmlPullParserException | IOException e) {
+                Slog.w(TAG, "applyRestore: error reading payload", e);
+            }
+        }
+
         @Override
         public NotificationChannel getNotificationChannel(String pkg, int uid, String
                 channelId) {
@@ -7641,8 +7704,9 @@
     }
 
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
-        return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList()
-                .size();
+        // don't show perm prompt if the only channels are bundle channels
+        return mPreferencesHelper.getNotificationChannels(
+                pkg, uid, includeDeleted, false).getList().size();
     }
 
     void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid,
@@ -7953,11 +8017,16 @@
     }
 
     /**
-     * Returns a channel, if exists, and restores deleted conversation channels.
+     * Returns a channel, if exists and is not a bundle channel, and restores deleted
+     * conversation channels.
      */
     @Nullable
     private NotificationChannel getNotificationChannelRestoreDeleted(String pkg,
             int callingUid, int notificationUid, String channelId, String conversationId) {
+        if (SYSTEM_RESERVED_IDS.contains(channelId)) {
+            // apps cannot post to these channels directly, in case they post incorrect content
+            return null;
+        }
         // Restore a deleted conversation channel, if exists. Otherwise use the parent channel.
         NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
                 pkg, notificationUid, channelId, conversationId,
@@ -11395,6 +11464,7 @@
         static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
 
         private static final String ATT_TYPES = "types";
+        private static final String ATT_DENIED = "denied_adjustments";
         private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments";
 
         private final Object mLock = new Object();
@@ -11403,6 +11473,9 @@
         private Set<String> mAllowedAdjustments = new ArraySet<>();
 
         @GuardedBy("mLock")
+        private Set<String> mDeniedAdjustments = new ArraySet<>();
+
+        @GuardedBy("mLock")
         private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>();
 
         protected ComponentName mDefaultFromConfig = null;
@@ -11474,9 +11547,11 @@
                 IPackageManager pm) {
             super(context, lock, up, pm);
 
-            // Add all default allowed adjustment types.
-            for (int i = 0; i < ALLOWED_ADJUSTMENTS.length; i++) {
-                mAllowedAdjustments.add(ALLOWED_ADJUSTMENTS[i]);
+            if (!notificationClassification()) {
+                // Add all default allowed adjustment types.
+                for (int i = 0; i < DEFAULT_ALLOWED_ADJUSTMENTS.length; i++) {
+                    mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]);
+                }
             }
         }
 
@@ -11539,17 +11614,28 @@
             return android.Manifest.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE;
         }
 
-        protected List<String> getAllowedAssistantAdjustments() {
+        protected Set<String> getAllowedAssistantAdjustments() {
             synchronized (mLock) {
-                List<String> types = new ArrayList<>();
-                types.addAll(mAllowedAdjustments);
-                return types;
+                if (notificationClassification()) {
+                    Set<String> types = new HashSet<>(Set.of(DEFAULT_ALLOWED_ADJUSTMENTS));
+                    types.removeAll(mDeniedAdjustments);
+                    return types;
+                } else {
+                    Set<String> types = new HashSet<>();
+                    types.addAll(mAllowedAdjustments);
+                    return types;
+                }
             }
         }
 
         protected boolean isAdjustmentAllowed(String type) {
             synchronized (mLock) {
-                return mAllowedAdjustments.contains(type);
+                if (notificationClassification()) {
+                    return List.of(DEFAULT_ALLOWED_ADJUSTMENTS).contains(type)
+                            && !mDeniedAdjustments.contains(type);
+                } else {
+                    return mAllowedAdjustments.contains(type);
+                }
             }
         }
 
@@ -11911,6 +11997,30 @@
 
         @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
         @GuardedBy("mNotificationLock")
+        public void allowAdjustmentType(@Adjustment.Keys String key) {
+            if (!android.service.notification.Flags.notificationClassification()) {
+                return;
+            }
+            mDeniedAdjustments.remove(key);
+            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+                mHandler.post(() -> notifyCapabilitiesChanged(info));
+            }
+        }
+
+        @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+        @GuardedBy("mNotificationLock")
+        public void disallowAdjustmentType(@Adjustment.Keys String key) {
+            if (!android.service.notification.Flags.notificationClassification()) {
+                return;
+            }
+            mDeniedAdjustments.add(key);
+            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+                mHandler.post(() -> notifyCapabilitiesChanged(info));
+            }
+        }
+
+        @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+        @GuardedBy("mNotificationLock")
         public void setAdjustmentTypeSupportedState(ManagedServiceInfo info,
                 @Adjustment.Keys String key, boolean supported) {
             if (!android.service.notification.Flags.notificationClassification()) {
@@ -11965,6 +12075,43 @@
                 }
             }
         }
+
+        @Override
+        protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
+            if (!android.service.notification.Flags.notificationClassification()) {
+                return;
+            }
+            synchronized (mLock) {
+                out.startTag(null, ATT_DENIED);
+                out.attribute(null, ATT_TYPES, TextUtils.join(",", mDeniedAdjustments));
+                out.endTag(null, ATT_DENIED);
+            }
+        }
+
+        @Override
+        protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
+            if (!android.service.notification.Flags.notificationClassification()) {
+                return;
+            }
+            if (ATT_DENIED.equals(tag)) {
+                final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
+                synchronized (mLock) {
+                    mDeniedAdjustments.clear();
+                    if (!TextUtils.isEmpty(types)) {
+                        mDeniedAdjustments.addAll(Arrays.asList(types.split(",")));
+                    }
+                }
+            }
+        }
+
+        private void notifyCapabilitiesChanged(final ManagedServiceInfo info) {
+            final INotificationListener assistant = (INotificationListener) info.service;
+            try {
+                assistant.onAllowedAdjustmentsChanged();
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "unable to notify assistant (capabilities): " + info, ex);
+            }
+        }
     }
 
     /**
@@ -12577,18 +12724,31 @@
 
                         // Checks if this is a request to notify system UI about a notification that
                         // has been lifetime extended.
-                        // (We only need to check old for the flag, because in both cancellation and
-                        // update cases, old should have the flag, whereas in update cases the
-                        // new will NOT have the flag.)
-                        // If it is such a request, and this is system UI, we send the post request
-                        // only to System UI, and break as we don't need to continue checking other
-                        // Managed Services.
-                        if (info.isSystemUi() && old != null && old.getNotification() != null
+                        // We check both old and new for the flag, to avoid catching updates
+                        // (where new will not have the flag).
+                        // If it is such a request, and this is the system UI listener, we send
+                        // the post request. If it's any other listener, we skip it.
+                        if (old != null && old.getNotification() != null
                                 && (old.getNotification().flags
+                                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0
+                                && sbn != null && sbn.getNotification() != null
+                                && (sbn.getNotification().flags
                                 & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) {
-                            final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
-                            listenerCalls.add(() -> notifyPosted(info, sbnToPost, update));
-                            break;
+                            if (info.isSystemUi()) {
+                                final NotificationRankingUpdate update =
+                                        makeRankingUpdateLocked(info);
+                                listenerCalls.add(() -> notifyPosted(info, sbnToPost, update));
+                                break;
+                            } else {
+                                // Skipping because this is the direct-reply "update" and we only
+                                // need to send it to sysui, so we immediately continue, before it
+                                // can get sent to other listeners below.
+                                if (DBG) {
+                                    Slog.d(TAG, "prepareNotifyPostedLocked: direct reply update, "
+                                            + "skipping post to " + info.toString());
+                                }
+                                continue;
+                            }
                         }
                     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 3ba9384..93f512b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -334,7 +334,7 @@
             return helper.createWaveformVibration(vibrationPattern, insistent);
         }
 
-        if (com.android.server.notification.Flags.notificationVibrationInSoundUri()) {
+        if (com.android.server.notification.Flags.notificationVibrationInSoundUriForChannel()) {
             final VibrationEffect vibrationEffectFromSoundUri =
                     helper.createVibrationEffectFromSoundUri(channel.getSound());
             if (vibrationEffectFromSoundUri != null) {
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index 10169d5..c305d66 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -80,6 +80,7 @@
             + "  get <notification-key>\n"
             + "  snooze --for <msec> <notification-key>\n"
             + "  unsnooze <notification-key>\n"
+            + "  set_exempt_th_force_grouping [true|false]\n"
             ;
 
     private static final String NOTIFY_USAGE =
@@ -428,6 +429,13 @@
                     }
                     break;
                 }
+                case "set_exempt_th_force_grouping": {
+                    String arg = getNextArgRequired();
+                    final boolean exemptTestHarnessFromForceGrouping =
+                            "true".equals(arg) || "1".equals(arg);
+                    mDirectService.setTestHarnessExempted(exemptTestHarnessFromForceGrouping);
+                    break;
+                }
                 default:
                     return handleDefaultCommands(cmd);
             }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 9e70f81..c9edc410 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1870,7 +1870,7 @@
 
     @Override
     public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
-            boolean includeDeleted) {
+            boolean includeDeleted, boolean includeBundles) {
         Objects.requireNonNull(pkg);
         List<NotificationChannel> channels = new ArrayList<>();
         synchronized (mLock) {
@@ -1882,7 +1882,9 @@
             for (int i = 0; i < N; i++) {
                 final NotificationChannel nc = r.channels.valueAt(i);
                 if (includeDeleted || !nc.isDeleted()) {
-                    channels.add(nc);
+                    if (includeBundles || !SYSTEM_RESERVED_IDS.contains(nc.getId())) {
+                        channels.add(nc);
+                    }
                 }
             }
             return new ParceledListSlice<>(channels);
@@ -1986,6 +1988,7 @@
      * bypassing DND. It should be called whenever a channel is created, updated, or deleted, or
      * when the current user (or its profiles) change.
      */
+    // TODO: b/368247671 - remove fromSystemOrSystemUi argument when modes_ui is inlined.
     private void updateCurrentUserHasChannelsBypassingDnd(int callingUid,
             boolean fromSystemOrSystemUi) {
         ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
@@ -2016,7 +2019,12 @@
         boolean haveBypassingApps = candidatePkgs.size() > 0;
         if (mCurrentUserHasChannelsBypassingDnd != haveBypassingApps) {
             mCurrentUserHasChannelsBypassingDnd = haveBypassingApps;
-            updateZenPolicy(mCurrentUserHasChannelsBypassingDnd, callingUid, fromSystemOrSystemUi);
+            if (android.app.Flags.modesUi()) {
+                mZenModeHelper.updateHasPriorityChannels(mCurrentUserHasChannelsBypassingDnd);
+            } else {
+                updateZenPolicy(mCurrentUserHasChannelsBypassingDnd, callingUid,
+                        fromSystemOrSystemUi);
+            }
         }
     }
 
@@ -2034,6 +2042,9 @@
         return true;
     }
 
+    // TODO: b/368247671 - delete this method when modes_ui is inlined, as
+    //                     updateCurrentUserHasChannelsBypassingDnd was the only caller and
+    //                     PreferencesHelper should otherwise not need to modify actual policy
     public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid,
             boolean fromSystemOrSystemUi) {
         NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 8df24c9..001e91c 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -55,5 +55,5 @@
     void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
     void permanentlyDeleteNotificationChannels(String pkg, int uid);
     ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
-            boolean includeDeleted);
+            boolean includeDeleted, boolean includeBundles);
 }
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 6efe88f..24b090c 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -25,6 +25,7 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.Condition;
 import android.service.notification.ScheduleCalendar;
@@ -115,6 +116,11 @@
     }
 
     @Override
+    public void onUserSwitched(UserHandle user) {
+        // Nothing to do because time-based schedules are not tied to any user data.
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         if (DEBUG) Slog.d(TAG, "onDestroy");
diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java
index 97073b7..656f9df 100644
--- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java
+++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.service.notification.ConditionProviderService;
 import android.service.notification.IConditionProvider;
 import android.util.TimeUtils;
@@ -30,9 +31,10 @@
 
 public abstract class SystemConditionProviderService extends ConditionProviderService {
 
-    abstract public void dump(PrintWriter pw, DumpFilter filter);
-    abstract public boolean isValidConditionId(Uri id);
-    abstract public void onBootComplete();
+    public abstract void dump(PrintWriter pw, DumpFilter filter);
+    public abstract boolean isValidConditionId(Uri id);
+    public abstract void onBootComplete();
+    public abstract void onUserSwitched(UserHandle user);
 
     final ComponentName getComponent() {
         return new ComponentName("android", this.getClass().getName());
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 50bfbc3..b1f010c 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -102,16 +102,6 @@
     }
 
     @Override
-    public void onBootComplete() {
-        // noop
-    }
-
-    @Override
-    public void onUserSwitched() {
-        // noop
-    }
-
-    @Override
     public void onServiceAdded(ComponentName component) {
         if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
         final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ea211a9..5547bd3 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1567,6 +1567,28 @@
         return azr;
     }
 
+    // Update only the hasPriorityChannels state (aka areChannelsBypassingDnd) without modifying
+    // any of the rest of the existing policy. This allows components that only want to modify
+    // this bit (PreferencesHelper) to not have to adjust the rest of the policy.
+    protected void updateHasPriorityChannels(boolean hasPriorityChannels) {
+        if (!Flags.modesUi()) {
+            Log.wtf(TAG, "updateHasPriorityChannels called without modes_ui");
+        }
+        synchronized (mConfigLock) {
+            // If it already matches, do nothing
+            if (mConfig.areChannelsBypassingDnd == hasPriorityChannels) {
+                return;
+            }
+
+            ZenModeConfig newConfig = mConfig.copy();
+            newConfig.areChannelsBypassingDnd = hasPriorityChannels;
+            // The updated calculation of whether there are priority channels is always done by
+            // the system, even if the event causing the calculation had a different origin.
+            setConfigLocked(newConfig, null, ORIGIN_SYSTEM, "updateHasPriorityChannels",
+                    Process.SYSTEM_UID);
+        }
+    }
+
     @SuppressLint("MissingPermission")
     void scheduleActivationBroadcast(String pkg, @UserIdInt int userId, String ruleId,
             boolean activated) {
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 0b34177..c479acf 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -165,6 +165,13 @@
 }
 
 flag {
+  name: "notification_lock_screen_settings"
+  namespace: "systemui"
+  description: "This flag enables the new settings page for the notifications on lock screen."
+  bug: "367455695"
+}
+
+flag {
   name: "notification_vibration_in_sound_uri"
   namespace: "systemui"
   description: "This flag enables sound uri with vibration source"
@@ -180,3 +187,20 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "notification_vibration_in_sound_uri_for_channel"
+  namespace: "systemui"
+  description: "Enables sound uri with vibration source in notification channel"
+  bug: "351975435"
+}
+
+flag {
+  name: "notification_nls_rebind"
+  namespace: "systemui"
+  description: "Check for NLS service intent filter when rebinding services"
+  bug: "347674739"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 3bcaf58..f23d782 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -96,12 +96,15 @@
         mHandler = thread.getThreadHandler();
 
         mWatcher = new TombstoneWatcher();
-        mWatcher.startWatching();
     }
 
     void onSystemReady() {
         registerForUserRemoval();
         registerForPackageRemoval();
+        // TombstoneWatcher depends on DropboxManagerService.
+        // DropboxManagerService started before systemReady.
+        // So it is good to call startWatching here.
+        mWatcher.startWatching();
 
         BootReceiver.initDropboxRateLimiter();
 
diff --git a/services/core/java/com/android/server/pinner/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java
index d7ac520..2c75926 100644
--- a/services/core/java/com/android/server/pinner/PinnerService.java
+++ b/services/core/java/com/android/server/pinner/PinnerService.java
@@ -121,9 +121,6 @@
     private static boolean PROP_PIN_PINLIST =
             SystemProperties.getBoolean("pinner.use_pinlist", true);
 
-    private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
-    private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
-
     public static final String ANON_REGION_STAT_NAME = "[anon]";
 
     private static final String SYSTEM_GROUP_NAME = "system";
@@ -179,8 +176,10 @@
 
     // Resource-configured pinner flags;
     private final boolean mConfiguredToPinCamera;
+    private final int mConfiguredCameraPinBytes;
     private final int mConfiguredHomePinBytes;
     private final boolean mConfiguredToPinAssistant;
+    private final int mConfiguredAssistantPinBytes;
     private final int mConfiguredWebviewPinBytes;
 
     // This is the percentage of total device memory that will be used to set the global quota.
@@ -250,6 +249,10 @@
         mDeviceConfigInterface = mInjector.getDeviceConfigInterface();
         mConfiguredToPinCamera = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerCameraApp);
+        mConfiguredCameraPinBytes = context.getResources().getInteger(
+                com.android.internal.R.integer.config_pinnerCameraPinBytes);
+        mConfiguredAssistantPinBytes = context.getResources().getInteger(
+                com.android.internal.R.integer.config_pinnerAssistantPinBytes);
         mConfiguredHomePinBytes = context.getResources().getInteger(
                 com.android.internal.R.integer.config_pinnerHomePinBytes);
         mConfiguredToPinAssistant = context.getResources().getBoolean(
@@ -812,11 +815,11 @@
     private int getSizeLimitForKey(@AppKey int key) {
         switch (key) {
             case KEY_CAMERA:
-                return MAX_CAMERA_PIN_SIZE;
+                return mConfiguredCameraPinBytes;
             case KEY_HOME:
                 return mConfiguredHomePinBytes;
             case KEY_ASSISTANT:
-                return MAX_ASSISTANT_PIN_SIZE;
+                return mConfiguredAssistantPinBytes;
             default:
                 return 0;
         }
@@ -1303,6 +1306,10 @@
             pw.format("   Maximum Pinner quota: %d bytes (%.2f MB)\n", mConfiguredMaxPinnedMemory,
                     mConfiguredMaxPinnedMemory / bytesPerMB);
             pw.format("   Max Home App Pin Bytes (without deps): %d\n", mConfiguredHomePinBytes);
+            pw.format("   Max Assistant App Pin Bytes (without deps): %d\n",
+                    mConfiguredAssistantPinBytes);
+            pw.format(
+                    "   Max Camera App Pin Bytes (without deps): %d\n", mConfiguredCameraPinBytes);
             pw.format("\nPinned Files:\n");
             synchronized (PinnerService.this) {
                 long totalSize = 0;
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index 49a6ffd..a221152 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -55,6 +55,8 @@
     private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER";
     private static final String ACTION_DISMISS_NOTIFICATION =
             "com.android.server.ACTION_DISMISS_NOTIFICATION";
+    private static final String EXTRA_NOTIFICATION_CLIENT_UID =
+            "com.android.server.EXTRA_CLIENT_UID";
     /**
      * The clientUid from the AudioFocusInfo of the background user,
      * for which an active notification is currently displayed.
@@ -101,7 +103,7 @@
         ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class);
 
         registerReceiver(am);
-        mBgUserListener = new BackgroundUserListener(mSystemUserContext);
+        mBgUserListener = new BackgroundUserListener();
         AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext);
         focusControlPolicyBuilder.setLooper(Looper.getMainLooper());
 
@@ -119,26 +121,16 @@
 
     final class BackgroundUserListener extends AudioPolicy.AudioPolicyFocusListener {
 
-        Context mSystemContext;
-
-        BackgroundUserListener(Context systemContext) {
-            mSystemContext = systemContext;
-        }
-
-        @SuppressLint("MissingPermission")
         public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
             try {
-                BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi,
-                        mSystemContext.createContextAsUser(
-                                UserHandle.of(ActivityManager.getCurrentUser()), 0));
+                BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
         }
 
-        @SuppressLint("MissingPermission")
         public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
-            BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary();
+            BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi.getClientUid());
         }
     }
 
@@ -160,7 +152,7 @@
                 if (mNotificationClientUid == -1) {
                     return;
                 }
-                dismissNotification();
+                dismissNotification(mNotificationClientUid);
 
                 if (DEBUG) {
                     final int actionIndex = intent.getAction().lastIndexOf(".") + 1;
@@ -171,7 +163,7 @@
                 }
 
                 if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
-                    muteAlarmSounds(mSystemUserContext);
+                    muteAlarmSounds(mNotificationClientUid);
                 } else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
                     activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid));
                 }
@@ -193,17 +185,17 @@
      */
     @SuppressLint("MissingPermission")
     @VisibleForTesting
-    void muteAlarmSounds(Context context) {
-        AudioManager audioManager = context.getSystemService(AudioManager.class);
+    void muteAlarmSounds(int notificationClientUid) {
+        AudioManager audioManager = mSystemUserContext.getSystemService(AudioManager.class);
         if (audioManager != null) {
             for (AudioPlaybackConfiguration apc : audioManager.getActivePlaybackConfigurations()) {
-                if (apc.getClientUid() == mNotificationClientUid && apc.getPlayerProxy() != null) {
+                if (apc.getClientUid() == notificationClientUid && apc.getPlayerProxy() != null) {
                     apc.getPlayerProxy().stop();
                 }
             }
         }
 
-        AudioFocusInfo currentAfi = getAudioFocusInfoForNotification();
+        AudioFocusInfo currentAfi = getAudioFocusInfoForNotification(notificationClientUid);
         if (currentAfi != null) {
             mFocusControlAudioPolicy.sendFocusLossAndUpdate(currentAfi);
         }
@@ -212,9 +204,14 @@
     /**
      * Check if sound is coming from background user and show notification is required.
      */
+    @SuppressLint("MissingPermission")
     @VisibleForTesting
-    void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context foregroundContext)
-            throws RemoteException {
+    void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi) throws RemoteException {
+        if (afi == null) {
+            return;
+        }
+        Context foregroundContext = mSystemUserContext.createContextAsUser(
+                UserHandle.of(ActivityManager.getCurrentUser()), 0);
         final int userId = UserHandle.getUserId(afi.getClientUid());
         final int usage = afi.getAttributes().getUsage();
         UserInfo userInfo = mUserManager.getUserInfo(userId);
@@ -232,8 +229,8 @@
 
                 mNotificationClientUid = afi.getClientUid();
 
-                mNotificationManager.notifyAsUser(LOG_TAG, mNotificationClientUid,
-                        createNotification(userInfo.name, foregroundContext),
+                mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(),
+                        createNotification(userInfo.name, foregroundContext, afi.getClientUid()),
                         foregroundContext.getUser());
             }
         }
@@ -245,14 +242,15 @@
      * focus ownership.
      */
     @VisibleForTesting
-    void dismissNotificationIfNecessary() {
-        if (getAudioFocusInfoForNotification() == null && mNotificationClientUid >= 0) {
+    void dismissNotificationIfNecessary(int notificationClientUid) {
+        if (getAudioFocusInfoForNotification(notificationClientUid) == null
+                && mNotificationClientUid >= 0) {
             if (DEBUG) {
                 Log.d(LOG_TAG, "Alarm ringing on background user "
-                        + UserHandle.getUserHandleForUid(mNotificationClientUid).getIdentifier()
+                        + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier()
                         + " left focus stack, dismissing notification");
             }
-            dismissNotification();
+            dismissNotification(notificationClientUid);
             mNotificationClientUid = -1;
         }
     }
@@ -262,8 +260,8 @@
      * shown.
      */
     @SuppressLint("MissingPermission")
-    private void dismissNotification() {
-        mNotificationManager.cancelAsUser(LOG_TAG, mNotificationClientUid, UserHandle.ALL);
+    private void dismissNotification(int notificationClientUid) {
+        mNotificationManager.cancelAsUser(LOG_TAG, notificationClientUid, UserHandle.ALL);
     }
 
     /**
@@ -272,11 +270,11 @@
     @SuppressLint("MissingPermission")
     @VisibleForTesting
     @Nullable
-    AudioFocusInfo getAudioFocusInfoForNotification() {
-        if (mNotificationClientUid >= 0) {
+    AudioFocusInfo getAudioFocusInfoForNotification(int notificationClientUid) {
+        if (notificationClientUid >= 0) {
             List<AudioFocusInfo> stack = mFocusControlAudioPolicy.getFocusStack();
             for (int i = stack.size() - 1; i >= 0; i--) {
-                if (stack.get(i).getClientUid() == mNotificationClientUid) {
+                if (stack.get(i).getClientUid() == notificationClientUid) {
                     return stack.get(i);
                 }
             }
@@ -284,22 +282,24 @@
         return null;
     }
 
-    private PendingIntent createPendingIntent(String intentAction) {
+    private PendingIntent createPendingIntent(String intentAction, int notificationClientUid) {
         final Intent intent = new Intent(intentAction);
-        PendingIntent resultPI =  PendingIntent.getBroadcast(mSystemUserContext, 0, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-        return resultPI;
+        intent.putExtra(EXTRA_NOTIFICATION_CLIENT_UID, notificationClientUid);
+        return PendingIntent.getBroadcast(mSystemUserContext, notificationClientUid, intent,
+                PendingIntent.FLAG_IMMUTABLE);
     }
 
+    @SuppressLint("MissingPermission")
     @VisibleForTesting
-    Notification createNotification(String userName, Context fgContext) {
+    Notification createNotification(String userName, Context fgContext, int notificationClientUid) {
         final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm,
                 userName);
         final int icon = R.drawable.ic_audio_alarm;
 
-        PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND);
-        PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER);
-        PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION);
+        PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND, notificationClientUid);
+        PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER, notificationClientUid);
+        PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION,
+                notificationClientUid);
 
         final Notification.Action mute = new Notification.Action.Builder(null,
                 fgContext.getString(R.string.bg_user_sound_notification_button_mute),
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 369029ad..84a5f2b 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -80,11 +80,6 @@
  */
 public final class BroadcastHelper {
     private static final boolean DEBUG_BROADCASTS = false;
-    /**
-     * Permissions required in order to receive instant application lifecycle broadcasts.
-     */
-    private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
-            new String[]{android.Manifest.permission.ACCESS_INSTANT_APPS};
 
     private final UserManagerInternal mUmInternal;
     private final ActivityManagerInternal mAmInternal;
@@ -115,7 +110,7 @@
         SparseArray<int[]> broadcastAllowList = new SparseArray<>();
         broadcastAllowList.put(userId, visibilityAllowList);
         broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
-                filterExtrasForReceiver, bOptions);
+                filterExtrasForReceiver, bOptions, null /* requiredPermissions */);
     }
 
     void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
@@ -123,7 +118,7 @@
             final int[] userIds, int[] instantUserIds,
             @Nullable SparseArray<int[]> broadcastAllowList,
             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
-            @Nullable Bundle bOptions) {
+            @Nullable Bundle bOptions, @Nullable String[] requiredPermissions) {
         try {
             final IActivityManager am = ActivityManager.getService();
             if (am == null) return;
@@ -137,12 +132,12 @@
             if (ArrayUtils.isEmpty(instantUserIds)) {
                 doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
                         resolvedUserIds, false /* isInstantApp */, broadcastAllowList,
-                        filterExtrasForReceiver, bOptions);
+                        filterExtrasForReceiver, bOptions, requiredPermissions);
             } else {
                 // send restricted broadcasts for instant apps
                 doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
-                        instantUserIds, true /* isInstantApp */, null,
-                        null /* filterExtrasForReceiver */, bOptions);
+                        instantUserIds, true /* isInstantApp */, null /* broadcastAllowList */,
+                        null /* filterExtrasForReceiver */, bOptions, requiredPermissions);
             }
         } catch (RemoteException ex) {
         }
@@ -166,7 +161,8 @@
             boolean isInstantApp,
             @Nullable SparseArray<int[]> broadcastAllowList,
             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
-            @Nullable Bundle bOptions) {
+            @Nullable Bundle bOptions,
+            @Nullable String[] requiredPermissions) {
         for (int userId : userIds) {
             final Intent intent = new Intent(action,
                     pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
@@ -189,17 +185,18 @@
             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
             broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
-                    filterExtrasForReceiver, bOptions);
+                    filterExtrasForReceiver, bOptions, requiredPermissions);
         }
     }
 
-
     private void broadcastIntent(Intent intent, IIntentReceiver finishedReceiver,
             boolean isInstantApp, int userId, @Nullable SparseArray<int[]> broadcastAllowList,
             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
-            @Nullable Bundle bOptions) {
-        final String[] requiredPermissions =
-                isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
+            @Nullable Bundle bOptions, @Nullable String[] requiredPermissions) {
+        if (isInstantApp) {
+            requiredPermissions = ArrayUtils.appendElement(String.class, requiredPermissions,
+                    android.Manifest.permission.ACCESS_INSTANT_APPS);
+        }
         if (DEBUG_BROADCASTS) {
             RuntimeException here = new RuntimeException("here");
             here.fillInStackTrace();
@@ -234,7 +231,7 @@
                 null /* instantUserIds */, null /* broadcastAllowList */,
                 (callingUid, intentExtras) -> filterExtrasChangedPackageList(
                         snapshot, callingUid, intentExtras),
-                null /* bOptions */);
+                null /* bOptions */, null /* requiredPermissions */);
     }
 
     /**
@@ -294,14 +291,29 @@
         return bOptions;
     }
 
-    private void sendPackageChangedBroadcast(@NonNull String packageName,
-                                             boolean dontKillApp,
-                                             @NonNull ArrayList<String> componentNames,
-                                             int packageUid,
-                                             @Nullable String reason,
-                                             @Nullable int[] userIds,
-                                             @Nullable int[] instantUserIds,
-                                             @Nullable SparseArray<int[]> broadcastAllowList) {
+    private void sendPackageChangedBroadcastInternal(@NonNull String packageName,
+            boolean dontKillApp,
+            @NonNull ArrayList<String> componentNames,
+            int packageUid,
+            @Nullable String reason,
+            @Nullable int[] userIds,
+            @Nullable int[] instantUserIds,
+            @Nullable SparseArray<int[]> broadcastAllowList) {
+        sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
+                packageUid, reason, userIds, instantUserIds, broadcastAllowList,
+                null /* targetPackageName */, null /* requiredPermissions */);
+    }
+
+    private void sendPackageChangedBroadcastWithPermissions(@NonNull String packageName,
+            boolean dontKillApp,
+            @NonNull ArrayList<String> componentNames,
+            int packageUid,
+            @Nullable String reason,
+            @Nullable int[] userIds,
+            @Nullable int[] instantUserIds,
+            @Nullable SparseArray<int[]> broadcastAllowList,
+            @Nullable String targetPackageName,
+            @Nullable String[] requiredPermissions) {
         if (DEBUG_INSTALL) {
             Log.v(TAG, "Sending package changed: package=" + packageName + " components="
                     + componentNames);
@@ -321,9 +333,10 @@
         // little component state change.
         final int flags = !componentNames.contains(packageName)
                 ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
-        sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
-                userIds, instantUserIds, broadcastAllowList, null /* filterExtrasForReceiver */,
-                null /* bOptions */);
+        sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags,
+                targetPackageName, null /* finishedReceiver */, userIds, instantUserIds,
+                broadcastAllowList, null /* filterExtrasForReceiver */, null /* bOptions */,
+                requiredPermissions);
     }
 
     static void sendDeviceCustomizationReadyBroadcast() {
@@ -680,7 +693,8 @@
 
         sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                 packageName, extras, 0, null, null, userIds, instantUserIds,
-                broadcastAllowlist, null /* filterExtrasForReceiver */, null);
+                broadcastAllowlist, null /* filterExtrasForReceiver */, null /* bOptions */,
+                null /* requiredPermissions */);
         // Send to PermissionController for all new users, even if it may not be running for some
         // users
         if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
@@ -688,7 +702,8 @@
                     packageName, extras, 0,
                     mContext.getPackageManager().getPermissionControllerPackageName(),
                     null, userIds, instantUserIds,
-                    broadcastAllowlist, null /* filterExtrasForReceiver */, null);
+                    broadcastAllowlist, null /* filterExtrasForReceiver */, null /* bOptions */,
+                    null /* requiredPermissions */);
         }
     }
 
@@ -719,7 +734,8 @@
             int[] userIds, int[] instantUserIds) {
         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
                 installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */,
-                null /* filterExtrasForReceiver */, null);
+                null /* filterExtrasForReceiver */, null /* bOptions */,
+                null /* requiredPermissions */);
     }
 
     /**
@@ -824,7 +840,7 @@
         final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
         final SparseArray<int[]> broadcastAllowList =
                 isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
-        mHandler.post(() -> sendPackageChangedBroadcast(
+        mHandler.post(() -> sendPackageChangedBroadcastInternal(
                 packageName, dontKillApp, componentNames, packageUid, reason, userIds,
                 instantUserIds, broadcastAllowList));
         mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
@@ -843,7 +859,7 @@
                                                @Nullable Bundle bOptions) {
         mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags,
                 targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
-                null /* filterExtrasForReceiver */, bOptions));
+                null /* filterExtrasForReceiver */, bOptions, null /* requiredPermissions */));
         if (targetPkg == null) {
             // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
             // many times to different targets, e.g. installer app, permission controller, other
@@ -1014,7 +1030,7 @@
                 extras, flags, null /* targetPkg */, null /* finishedReceiver */,
                 new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
                 filterExtrasForReceiver,
-                options));
+                options, null /* requiredPermissions */));
         notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
                 null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver);
     }
@@ -1046,9 +1062,12 @@
                 } else {
                     intentExtras = null;
                 }
-                doSendBroadcast(action, null, intentExtras,
-                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
-                        targetUserIds, false, null, null, null);
+                doSendBroadcast(action, null /* pkg */, intentExtras,
+                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName,
+                        null /* finishedReceiver */,
+                        targetUserIds, false /* isInstantApp */, null /* broadcastAllowList */,
+                        null /* filterExtrasForReceiver */, null /* bOptions */,
+                        null /* requiredPermissions */);
             }
         });
     }
@@ -1077,7 +1096,7 @@
                 null /* broadcastAllowList */,
                 (callingUid, intentExtras) -> filterExtrasChangedPackageList(
                         snapshot, callingUid, intentExtras),
-                null /* bOptions */));
+                null /* bOptions */, null /* requiredPermissions */));
     }
 
     void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot,
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 4665a72..19ac1ec 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2768,7 +2768,8 @@
             enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
                     !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId),
                     "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission");
-        } else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0
+        } else if (!Flags.removeCrossUserPermissionHack()
+                && (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0
                 && isCallerSystemUser
                 && mUserManager.hasProfile(UserHandle.USER_SYSTEM)) {
             // If the caller wants all packages and has a profile associated with it,
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 02afdd6..0b58c75 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -89,6 +89,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -790,16 +791,6 @@
             }
             try {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
-                // This mirrors logic from commitReconciledScanResultLocked, where the library
-                // files needed for dexopt are assigned.
-                PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
-                // Unfortunately, the updated system app flag is only tracked on this
-                // PackageSetting
-                boolean isUpdatedSystemApp =
-                        installRequest.getScannedPackageSetting().isUpdatedSystemApp();
-                realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
-
                 DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
                         installRequest, dexoptOptions);
                 installRequest.onDexoptFinished(dexOptResult);
@@ -893,7 +884,8 @@
 
         @Override
         public void onApexStaged(@NonNull ApexStagedEvent event) {
-            mArtManager.onApexStaged(event.stagedApexModuleNames);
+            mArtManager.onApexStaged(Arrays.stream(event.stagedApexInfos)
+                    .map(info -> info.moduleName).toArray(String[]::new));
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 1f79ac0..089bbb7 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.Flags.improveInstallFreeze;
 import static android.content.pm.PackageInstaller.SessionParams.USER_ACTION_UNSPECIFIED;
 import static android.content.pm.PackageManager.INSTALL_REASON_UNKNOWN;
 import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
@@ -1050,13 +1049,13 @@
     }
 
     public void onFreezeStarted() {
-        if (mPackageMetrics != null && improveInstallFreeze()) {
+        if (mPackageMetrics != null) {
             mPackageMetrics.onStepStarted(PackageMetrics.STEP_FREEZE_INSTALL);
         }
     }
 
     public void onFreezeCompleted() {
-        if (mPackageMetrics != null && improveInstallFreeze()) {
+        if (mPackageMetrics != null) {
             mPackageMetrics.onStepFinished(PackageMetrics.STEP_FREEZE_INSTALL);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 34d939b..f6a808b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -25,12 +25,14 @@
 import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
+import static com.android.server.pm.PackageInstallerSession.isValidVerificationPolicy;
 import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -150,6 +152,7 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.IntPredicate;
 import java.util.function.Supplier;
 
@@ -275,6 +278,13 @@
         }
     };
 
+    /**
+     * Default verification policy for incoming installation sessions.
+     * TODO(b/360129657): update the default policy.
+     */
+    private final AtomicInteger mVerificationPolicy = new AtomicInteger(
+            VERIFICATION_POLICY_BLOCK_FAIL_WARN);
+
     private static final class Lifecycle extends SystemService {
         private final PackageInstallerService mPackageInstallerService;
 
@@ -1042,7 +1052,7 @@
                 userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
                 null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
                 false, false, false, PackageManager.INSTALL_UNKNOWN, "", null,
-                mVerifierController);
+                mVerifierController, mVerificationPolicy.get());
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
@@ -1866,6 +1876,34 @@
         }
     }
 
+    @Override
+    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need the "
+                    + "com.android.permission.VERIFICATION_AGENT permission "
+                    + "to get the verification policy");
+        }
+        return mVerificationPolicy.get();
+    }
+
+    @Override
+    public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need the "
+                    + "com.android.permission.VERIFICATION_AGENT permission "
+                    + "to set the verification policy");
+        }
+        if (!isValidVerificationPolicy(policy)) {
+            return false;
+        }
+        if (policy != mVerificationPolicy.get()) {
+            mVerificationPolicy.set(policy);
+        }
+        return true;
+    }
+
     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
             int installerUid) {
         int count = 0;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 897ee431..9a9e434 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -21,9 +21,17 @@
 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
 import static android.content.pm.DataLoaderType.INCREMENTAL;
 import static android.content.pm.DataLoaderType.STREAMING;
+import static android.content.pm.PackageInstaller.EXTRA_VERIFICATION_FAILURE_REASON;
 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_UNKNOWN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_NONE;
 import static android.content.pm.PackageItemInfo.MAX_SAFE_LABEL_LENGTH;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
@@ -38,7 +46,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
+import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE;
 import static android.os.Process.INVALID_UID;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 import static android.system.OsConstants.O_CREAT;
@@ -109,6 +117,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PackageInfoFlags;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningInfo;
 import android.content.pm.dex.DexMetadataHelper;
@@ -225,7 +234,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Predicate;
-import java.util.function.Supplier;
 
 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     private static final String TAG = "PackageInstallerSession";
@@ -312,6 +320,7 @@
     private static final String ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT =
             "applicationEnabledSettingPersistent";
     private static final String ATTR_DOMAIN = "domain";
+    private static final String ATTR_VERIFICATION_POLICY = "verificationPolicy";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
     private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
@@ -409,6 +418,11 @@
     private final PackageSessionProvider mSessionProvider;
     private final SilentUpdatePolicy mSilentUpdatePolicy;
     /**
+     * The verification policy applied to this session, which might be different from the default
+     * verification policy used by the system.
+     */
+    private final AtomicInteger mVerificationPolicy;
+    /**
      * Note all calls must be done outside {@link #mLock} to prevent lock inversion.
      */
     private final StagingManager mStagingManager;
@@ -790,7 +804,8 @@
             if (errorMsg != null) {
                 Slog.e(TAG, "verifySession error: " + errorMsg);
                 setSessionFailed(INSTALL_FAILED_INTERNAL_ERROR, errorMsg);
-                onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg);
+                onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg,
+                        /* extras= */ null);
                 return false;
             }
             return true;
@@ -1102,17 +1117,6 @@
         final boolean isUpdateOwnershipEnforcementEnabled =
                 mPm.isUpdateOwnershipEnforcementAvailable()
                         && existingUpdateOwnerPackageName != null;
-
-        if (Build.IS_USERDEBUG) {
-            Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement"
-                    + " isUpdateOwnershipEnforcementEnabled= " + isUpdateOwnershipEnforcementEnabled
-                    + ", mPm.isUpdateOwnershipEnforcementAvailable= "
-                    + mPm.isUpdateOwnershipEnforcementAvailable()
-                    + ", existingUpdateOwnerPackageName=" + existingUpdateOwnerPackageName
-                    + ", isUpdateOwner= " + isUpdateOwner + ", getInstallerPackageName()= "
-                    + getInstallerPackageName() + ", isInstallerShell= " + isInstallerShell
-                    + ", mInstallerUid=" + mInstallerUid + ", packageName = " + packageName);
-        }
         // For an installation that un-archives an app, if the installer doesn't have the
         // INSTALL_PACKAGES permission, the user should have already been prompted to confirm the
         // un-archive request. There's no need for another confirmation during the installation.
@@ -1126,10 +1130,6 @@
                 || isInstallUnarchive;
 
         if (noUserActionNecessary) {
-            if (Build.IS_USERDEBUG) {
-                Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement"
-                                + " noUserActionNecessary userActionNotTypicallyNeededResponse");
-            }
             return userActionNotTypicallyNeededResponse;
         }
 
@@ -1139,27 +1139,15 @@
                 && !isInstallerShell
                 // We don't enforce the update ownership for the managed user and profile.
                 && !isFromManagedUserOrProfile) {
-            if (Build.IS_USERDEBUG) {
-                Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement"
-                        + "USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER");
-            }
             return USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER;
         }
 
         if (isPermissionGranted) {
-            if (Build.IS_USERDEBUG) {
-                Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement"
-                        + " permission userActionNotTypicallyNeededResponse");
-            }
             return userActionNotTypicallyNeededResponse;
         }
 
         if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid,
                 userId)) {
-            if (Build.IS_USERDEBUG) {
-                Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement"
-                        + " disable USER_ACTION_REQUIRED");
-            }
             // show the installer to account for device policy or unknown sources use cases
             return USER_ACTION_REQUIRED;
         }
@@ -1168,17 +1156,9 @@
                 && isUpdateWithoutUserActionPermissionGranted
                 && ((isUpdateOwnershipEnforcementEnabled ? isUpdateOwner
                 : isInstallerOfRecord) || isSelfUpdate)) {
-            if (Build.IS_USERDEBUG) {
-                Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement"
-                        + " USER_ACTION_PENDING_APK_PARSING");
-            }
             return USER_ACTION_PENDING_APK_PARSING;
         }
 
-        if (Build.IS_USERDEBUG) {
-            Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement"
-                    + " USER_ACTION_REQUIRED");
-        }
         return USER_ACTION_REQUIRED;
     }
 
@@ -1201,7 +1181,8 @@
             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
             boolean isFailed, boolean isApplied, int sessionErrorCode,
             String sessionErrorMessage, DomainSet preVerifiedDomains,
-            @NonNull VerifierController verifierController) {
+            @NonNull VerifierController verifierController,
+            @PackageInstaller.VerificationPolicy int verificationPolicy) {
         mCallback = callback;
         mContext = context;
         mPm = pm;
@@ -1211,6 +1192,7 @@
         mHandler = new Handler(looper, mHandlerCallback);
         mStagingManager = stagingManager;
         mVerifierController = verifierController;
+        mVerificationPolicy = new AtomicInteger(verificationPolicy);
 
         this.sessionId = sessionId;
         this.userId = userId;
@@ -1296,9 +1278,9 @@
             }
         }
 
-        if (Flags.verificationService()) {
+        if (shouldUseVerificationService()) {
             // Start binding to the verification service, if not bound already.
-            mVerifierController.bindToVerifierServiceIfNeeded(() -> pm.snapshotComputer(), userId);
+            mVerifierController.bindToVerifierServiceIfNeeded(mPm::snapshotComputer, userId);
             if (!TextUtils.isEmpty(params.appPackageName)) {
                 mVerifierController.notifyPackageNameAvailable(params.appPackageName);
             }
@@ -2614,10 +2596,10 @@
         dispatchSessionFinished(error, detailMessage, null);
     }
 
-    private void onSessionVerificationFailure(int error, String msg) {
+    private void onSessionVerificationFailure(int error, String msg, Bundle extras) {
         Slog.e(TAG, "Failed to verify session " + sessionId);
         // Dispatch message to remove session from PackageInstallerService.
-        dispatchSessionFinished(error, msg, null);
+        dispatchSessionFinished(error, msg, extras);
         maybeFinishChildSessions(error, msg);
     }
 
@@ -2749,11 +2731,6 @@
         @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED;
         // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
         userActionRequirement = session.computeUserActionRequirement();
-        if (Build.IS_USERDEBUG) {
-            Log.d("updateowner", "PackageInstallerSession checkUserActionRequirement"
-                    + " userActionRequirement= " + userActionRequirement
-                    + ", session.packageName= " + session.getPackageName());
-        }
         session.updateUserActionRequirement(userActionRequirement);
         if (userActionRequirement == USER_ACTION_REQUIRED
                 || userActionRequirement == USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER) {
@@ -2880,34 +2857,75 @@
             // since installation is in progress.
             activate();
         }
-        if (Flags.verificationService()) {
-            final Supplier<Computer> snapshotSupplier = mPm::snapshotComputer;
-            if (mVerifierController.isVerifierInstalled(snapshotSupplier, userId)) {
-                // TODO: extract shared library declarations
-                final SigningInfo signingInfo;
-                synchronized (mLock) {
-                    signingInfo = new SigningInfo(mSigningDetails);
-                }
-                // Send the request to the verifier and wait for its response before the rest of
-                // the installation can proceed.
-                if (!mVerifierController.startVerificationSession(snapshotSupplier, userId,
-                        sessionId, params.appPackageName, Uri.fromFile(stageDir), signingInfo,
-                        /* declaredLibraries= */null, /* extensionParams= */ null,
-                        new VerifierCallback(), /* retry= */ false)) {
-                    // A verifier is installed but cannot be connected. Installation disallowed.
-                    onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                            "A verifier agent is available on device but cannot be connected.");
+        try {
+            List<PackageInstallerSession> children = getChildSessions();
+            if (isMultiPackage()) {
+                for (PackageInstallerSession child : children) {
+                    child.prepareInheritedFiles();
+                    child.parseApk();
                 }
             } else {
-                // Verifier is not installed. Let the installation pass for now.
-                resumeVerify();
+                prepareInheritedFiles();
+                parseApk();
+            }
+        }  catch (PackageManagerException e) {
+            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+            final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
+            setSessionFailed(e.error, errorMsg);
+            onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null);
+        }
+        if (shouldUseVerificationService()) {
+            final SigningInfo signingInfo;
+            final List<SharedLibraryInfo> declaredLibraries;
+            synchronized (mLock) {
+                signingInfo = new SigningInfo(mSigningDetails);
+                declaredLibraries =
+                        mPackageLite == null ? null : mPackageLite.getDeclaredLibraries();
+            }
+            // Send the request to the verifier and wait for its response before the rest of
+            // the installation can proceed.
+            if (!mVerifierController.startVerificationSession(mPm::snapshotComputer, userId,
+                    sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo,
+                    declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null,
+                    new VerifierCallback(), /* retry= */ false)) {
+                // A verifier is installed but cannot be connected. Installation disallowed.
+                onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
+                        "A verifier agent is available on device but cannot be connected.",
+                        /* extras= */ null);
             }
         } else {
-            // New verification feature is not enabled. Proceed to the rest of the verification.
+            // No need to check with verifier. Proceed with the rest of the verification.
             resumeVerify();
         }
     }
 
+    private boolean shouldUseVerificationService() {
+        if (!Flags.verificationService()) {
+            // Feature is not enabled.
+            return false;
+        }
+        if ((params.installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
+            // adb installs are exempted from verification unless explicitly requested
+            if (!params.forceVerification) {
+                return false;
+            }
+        }
+        final String verifierPackageName = mVerifierController.getVerifierPackageName(
+                mPm::snapshotComputer, userId);
+        if (verifierPackageName == null) {
+            // Feature is enabled but no verifier installed.
+            return false;
+        }
+        synchronized (mLock) {
+            if (verifierPackageName.equals(mPackageName)) {
+                // The verifier itself is being updated. Skip.
+                Slog.w(TAG, "Skipping verification service because the verifier is being updated");
+                return false;
+            }
+        }
+        return true;
+    }
+
     private void resumeVerify() {
         if (mVerificationInProgress) {
             Slog.w(TAG, "Verification is already in progress for session " + sessionId);
@@ -2927,19 +2945,17 @@
             List<PackageInstallerSession> children = getChildSessions();
             if (isMultiPackage()) {
                 for (PackageInstallerSession child : children) {
-                    child.prepareInheritedFiles();
-                    child.parseApkAndExtractNativeLibraries();
+                    child.extractNativeLibraries();
                 }
             } else {
-                prepareInheritedFiles();
-                parseApkAndExtractNativeLibraries();
+                extractNativeLibraries();
             }
             verifyNonStaged();
         } catch (PackageManagerException e) {
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
             final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
             setSessionFailed(e.error, errorMsg);
-            onSessionVerificationFailure(e.error, errorMsg);
+            onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null);
         }
     }
 
@@ -2948,24 +2964,57 @@
      */
     public class VerifierCallback {
         /**
+         * Called by the VerifierController when the verifier requests to get the current
+         * verification policy for this session.
+         */
+        public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+            return mVerificationPolicy.get();
+        }
+        /**
+         * Called by the VerifierController when the verifier requests to change the verification
+         * policy for this session.
+         */
+        public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+            if (!isValidVerificationPolicy(policy)) {
+                return false;
+            }
+            mVerificationPolicy.set(policy);
+            return true;
+        }
+        /**
          * Called by the VerifierController when the connection has failed.
          */
         public void onConnectionFailed() {
-            mHandler.post(() -> {
-                onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
-                            "A verifier agent is available on device but cannot be connected.");
-            });
+            // TODO(b/360129657): prompt user on fail warning
+            handleNonPackageBlockedFailure(
+                    /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+                    /* onFailClosed= */ () -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+                                VERIFICATION_FAILED_REASON_UNKNOWN);
+                        onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                                "A verifier agent is available on device but cannot be connected.",
+                                bundle);
+
+                    });
         }
         /**
          * Called by the VerifierController when the verification request has timed out.
          */
         public void onTimeout() {
-            mHandler.post(() -> {
-                mVerifierController.notifyVerificationTimeout(sessionId);
-                onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
-                        "Verification timed out; missing a response from the verifier within the"
-                                + " time limit");
-            });
+            // Always notify the verifier, regardless of the policy.
+            mVerifierController.notifyVerificationTimeout(sessionId);
+            // TODO(b/360129657): prompt user on fail warning
+            handleNonPackageBlockedFailure(
+                    /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+                    /* onFailClosed= */ () -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+                                VERIFICATION_FAILED_REASON_UNKNOWN);
+                        onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                                "Verification timed out; missing a response from the verifier"
+                                        + " within the time limit", bundle);
+                    });
         }
         /**
          * Called by the VerifierController when the verification request has received a complete
@@ -2975,17 +3024,22 @@
                 @Nullable PersistableBundle extensionResponse) {
             // TODO: handle extension response
             mHandler.post(() -> {
-                if (statusReceived.isVerified()) {
+                if (statusReceived.isVerified()
+                        || mVerificationPolicy.get() == VERIFICATION_POLICY_NONE) {
                     // Continue with the rest of the verification and installation.
                     resumeVerify();
-                } else {
-                    StringBuilder sb = new StringBuilder("Verifier rejected the installation");
-                    if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) {
-                        sb.append(" with message: ").append(statusReceived.getFailureMessage());
-                    }
-                    onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
-                            sb.toString());
+                    return;
                 }
+                // Package is blocked.
+                StringBuilder sb = new StringBuilder("Verifier rejected the installation");
+                if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) {
+                    sb.append(" with message: ").append(statusReceived.getFailureMessage());
+                }
+                Bundle bundle = new Bundle();
+                bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+                        VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED);
+                onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                        sb.toString(), bundle);
             });
         }
         /**
@@ -2993,14 +3047,49 @@
          * response.
          */
         public void onVerificationIncompleteReceived(int incompleteReason) {
-            mHandler.post(() -> {
-                if (incompleteReason == VERIFICATION_INCOMPLETE_UNKNOWN) {
-                    // TODO: change this to a user confirmation and handle other incomplete reasons
-                    onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Verification cannot be completed for unknown reasons.");
-                }
-            });
+            // TODO(b/360129657): prompt user on fail warning
+            handleNonPackageBlockedFailure(
+                    /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+                    /* onFailClosed= */ () -> {
+                        final int failureReason;
+                        StringBuilder sb = new StringBuilder(
+                                "Verification cannot be completed because of ");
+                        if (incompleteReason == VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE) {
+                            failureReason = VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE;
+                            sb.append("unavailable network.");
+                        } else {
+                            failureReason = VERIFICATION_FAILED_REASON_UNKNOWN;
+                            sb.append("unknown reasons.");
+                        }
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON, failureReason);
+                        onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                                sb.toString(), bundle);
+                    });
         }
+
+        private void handleNonPackageBlockedFailure(Runnable onFailWarning, Runnable onFailClosed) {
+            final Runnable r = switch (mVerificationPolicy.get()) {
+                case VERIFICATION_POLICY_NONE, VERIFICATION_POLICY_BLOCK_FAIL_OPEN ->
+                        PackageInstallerSession.this::resumeVerify;
+                case VERIFICATION_POLICY_BLOCK_FAIL_WARN -> onFailWarning;
+                case VERIFICATION_POLICY_BLOCK_FAIL_CLOSED -> onFailClosed;
+                default -> {
+                    Log.wtf(TAG, "Unknown verification policy: " + mVerificationPolicy.get());
+                    yield onFailClosed;
+                }
+            };
+            mHandler.post(r);
+        }
+    }
+
+    /**
+     * Returns whether a policy is a valid verification policy.
+     */
+    public static boolean isValidVerificationPolicy(
+            @PackageInstaller.VerificationPolicy int policy) {
+        return policy >= VERIFICATION_POLICY_NONE
+                && policy <= VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
     }
 
     private IntentSender getRemoteStatusReceiver() {
@@ -3109,7 +3198,7 @@
         mStageDirInUse = true;
     }
 
-    private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
+    private void parseApk() throws PackageManagerException {
         synchronized (mLock) {
             if (mStageDirInUse) {
                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -3142,12 +3231,16 @@
                 // stage dir here.
                 // Besides, PackageLite may be null for staged sessions that don't complete
                 // pre-reboot verification.
-                result = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
+                mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
             } else {
-                result = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
+                mPackageLite = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
             }
-            if (result != null) {
-                mPackageLite = result;
+        }
+    }
+
+    private void extractNativeLibraries() throws PackageManagerException {
+        synchronized (mLock) {
+            if (mPackageLite != null) {
                 if (!isApexSession()) {
                     synchronized (mProgressLock) {
                         mInternalProgress = 0.5f;
@@ -3174,7 +3267,7 @@
                 if (error == INSTALL_SUCCEEDED) {
                     onVerificationComplete();
                 } else {
-                    onSessionVerificationFailure(error, msg);
+                    onSessionVerificationFailure(error, msg, /* extras= */ null);
                 }
             });
         });
@@ -5346,6 +5439,14 @@
         }
     }
 
+    /**
+     * @return the current policy for the verification request associated with this session.
+     */
+    @VisibleForTesting
+    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+        assertCallerIsOwnerOrRoot();
+        return mVerificationPolicy.get();
+    }
 
     void setSessionReady() {
         synchronized (mLock) {
@@ -5516,7 +5617,7 @@
             }
         } catch (InstallerException ignored) {
         }
-        if (Flags.verificationService()
+        if (shouldUseVerificationService()
                 && !TextUtils.isEmpty(params.appPackageName)
                 && !isCommitted()) {
             // Only notify for the cancellation if the verification request has not
@@ -5649,6 +5750,10 @@
             if (!ArrayUtils.isEmpty(warnings)) {
                 fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
             }
+            if (extras.containsKey(EXTRA_VERIFICATION_FAILURE_REASON)) {
+                fillIn.putExtra(EXTRA_VERIFICATION_FAILURE_REASON,
+                        extras.getInt(EXTRA_VERIFICATION_FAILURE_REASON));
+            }
         }
         try {
             final BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -5804,6 +5909,7 @@
             out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason);
             writeBooleanAttribute(out, ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT,
                     params.applicationEnabledSettingPersistent);
+            out.attributeInt(null, ATTR_VERIFICATION_POLICY, mVerificationPolicy.get());
 
             final boolean isDataLoader = params.dataLoaderParams != null;
             writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader);
@@ -5954,6 +6060,8 @@
         final boolean sealed = in.getAttributeBoolean(null, ATTR_SEALED, false);
         final int parentSessionId = in.getAttributeInt(null, ATTR_PARENT_SESSION_ID,
                 SessionInfo.INVALID_ID);
+        final int verificationPolicy = in.getAttributeInt(null, ATTR_VERIFICATION_POLICY,
+                VERIFICATION_POLICY_NONE);
 
         final SessionParams params = new SessionParams(
                 SessionParams.MODE_INVALID);
@@ -6128,6 +6236,7 @@
                 installerUid, installSource, params, createdMillis, committedMillis, stageDir,
                 stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
                 childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
-                sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController);
+                sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController,
+                verificationPolicy);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java
index 66ecd6e..7d8573e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerNative.java
+++ b/services/core/java/com/android/server/pm/PackageManagerNative.java
@@ -20,7 +20,6 @@
 
 import static com.android.server.pm.PackageManagerService.TAG;
 
-import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManagerNative;
 import android.content.pm.IStagedApexObserver;
@@ -184,14 +183,8 @@
     }
 
     @Override
-    public String[] getStagedApexModuleNames() {
-        return mPm.mInstallerService.getStagingManager()
-                .getStagedApexModuleNames().toArray(new String[0]);
-    }
-
-    @Override
-    @Nullable
-    public StagedApexInfo getStagedApexInfo(String moduleName) {
-        return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
+    public StagedApexInfo[] getStagedApexInfos() {
+        return mPm.mInstallerService.getStagingManager().getStagedApexInfos().toArray(
+                new StagedApexInfo[0]);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c8cf938..d78f122 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -204,7 +204,6 @@
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
-import com.android.server.PackageWatchdog;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.ThreadPriorityBooster;
@@ -214,6 +213,7 @@
 import com.android.server.art.model.DeleteResult;
 import com.android.server.compat.CompatChange;
 import com.android.server.compat.PlatformCompat;
+import com.android.server.crashrecovery.CrashRecoveryAdaptor;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.Settings.VersionInfo;
 import com.android.server.pm.dex.ArtManagerService;
@@ -340,7 +340,7 @@
     static final boolean DEBUG_UPGRADE = false;
     static final boolean DEBUG_DOMAIN_VERIFICATION = false;
     static final boolean DEBUG_BACKUP = false;
-    public static final boolean DEBUG_INSTALL = Build.IS_USERDEBUG;
+    public static final boolean DEBUG_INSTALL = false;
     public static final boolean DEBUG_REMOVE = false;
     static final boolean DEBUG_PACKAGE_INFO = false;
     static final boolean DEBUG_INTENT_MATCHING = false;
@@ -3048,7 +3048,7 @@
         mDexManager.writePackageDexUsageNow();
         mDynamicCodeLogger.writeNow();
         if (!refactorCrashrecovery()) {
-            PackageWatchdog.getInstance(mContext).writeNow();
+            CrashRecoveryAdaptor.packageWatchdogWriteNow(mContext);
         }
 
         synchronized (mLock) {
@@ -4709,10 +4709,11 @@
                 extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
                 mHandler.post(() -> {
                     mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED,
-                            packageName, extras,
-                            Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
-                            userIds, null, broadcastAllowList, null,
-                            null);
+                            packageName, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY,
+                            null /* targetPkg */, null /* finishedReceiver */, userIds,
+                            null /* instantUserIds */, broadcastAllowList,
+                            null /* filterExtrasForReceiver */, null /* bOptions */,
+                            null /* requiredPermissions */);
                 });
                 mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED,
                         packageName, extras, userIds, null /* instantUserIds */,
@@ -7169,17 +7170,17 @@
                 // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
                 mHandler.post(() -> {
                     mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
-                            packageName, extras,
-                            flags, null, null,
-                            userIds, null, broadcastAllowList, null,
-                            null);
+                            packageName, extras, flags, null /* targetPkg */,
+                            null /* finishedReceiver */, userIds, null /* instantUserIds */,
+                            broadcastAllowList, null /* filterExtrasForReceiver */,
+                            null /* bOptions */, null /* requiredPermissions */);
                 });
             } else {
                 mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
-                        packageName, extras,
-                        flags, null, null,
-                        userIds, null, broadcastAllowList, null,
-                        null);
+                        packageName, extras, flags, null /* targetPkg */,
+                        null /* finishedReceiver */, userIds, null /* instantUserIds */,
+                        broadcastAllowList, null /* filterExtrasForReceiver */, null /* bOptions */,
+                        null /* requiredPermissions */);
             }
             mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED,
                     packageName, extras, userIds, null /* instantUserIds */,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7a53fe7..4652c3a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3599,6 +3599,9 @@
                             .setCompilerFilter(sessionParams.dexoptCompilerFilter)
                             .build();
                     break;
+                case "--force-verification":
+                    sessionParams.setForceVerification();
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
@@ -4805,6 +4808,7 @@
         pw.println("          https://source.android.com/docs/core/runtime/configure"
                 + "#compiler_filters");
         pw.println("          or 'skip'");
+        pw.println("      --force-verification: if set, enable the verification for this install");
         pw.println("");
         pw.println("  install-existing [--user USER_ID|all|current]");
         pw.println("       [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 94bdfbd..38cf0b9 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,7 +17,6 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.apex.ApexInfo;
 import android.apex.ApexSessionInfo;
 import android.apex.ApexSessionParams;
@@ -38,7 +37,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Slog;
@@ -65,9 +63,9 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -807,14 +805,13 @@
     }
 
     /**
-     * Returns ApexInfo about APEX contained inside the session as a {@code Map<String, ApexInfo>},
-     * where the key of the map is the module name of the ApexInfo.
+     * Returns ApexInfo about APEX contained inside the session.
      *
-     * Returns an empty map if there is any error.
+     * Returns an empty list if there is any error.
      */
     @VisibleForTesting
     @NonNull
-    Map<String, ApexInfo> getStagedApexInfos(@NonNull StagedSession session) {
+    List<ApexInfo> getStagedApexInfos(@NonNull StagedSession session) {
         Preconditions.checkArgument(session != null, "Session is null");
         Preconditions.checkArgument(!session.hasParentSessionId(),
                 session.sessionId() + " session has parent session");
@@ -824,7 +821,7 @@
         // Even if caller calls this method on ready session, the session could be abandoned
         // right after this method is called.
         if (!session.isSessionReady() || session.isDestroyed()) {
-            return Collections.emptyMap();
+            return Collections.emptyList();
         }
 
         ApexSessionParams params = new ApexSessionParams();
@@ -838,20 +835,17 @@
             }
         }
         params.childSessionIds = childSessionIds.toArray();
-
-        ApexInfo[] infos = mApexManager.getStagedApexInfos(params);
-        Map<String, ApexInfo> result = new ArrayMap<>();
-        for (ApexInfo info : infos) {
-            result.put(info.moduleName, info);
-        }
-        return result;
+        return Arrays.asList(mApexManager.getStagedApexInfos(params));
     }
 
     /**
-     * Returns apex module names of all packages that are staged ready
+     * Returns ApexInfo list about APEXes contained inside all staged sessions.
+     *
+     * Returns an empty list if there is any error.
      */
-    List<String> getStagedApexModuleNames() {
-        List<String> result = new ArrayList<>();
+    @NonNull
+    List<StagedApexInfo> getStagedApexInfos() {
+        List<StagedApexInfo> result = new ArrayList<>();
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
                 final StagedSession session = mStagedSessions.valueAt(i);
@@ -859,26 +853,7 @@
                         || session.hasParentSessionId() || !session.containsApexSession()) {
                     continue;
                 }
-                result.addAll(getStagedApexInfos(session).keySet());
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Returns ApexInfo of the {@code moduleInfo} provided if it is staged, otherwise returns null.
-     */
-    @Nullable
-    StagedApexInfo getStagedApexInfo(String moduleName) {
-        synchronized (mStagedSessions) {
-            for (int i = 0; i < mStagedSessions.size(); i++) {
-                final StagedSession session = mStagedSessions.valueAt(i);
-                if (!session.isSessionReady() || session.isDestroyed()
-                        || session.hasParentSessionId() || !session.containsApexSession()) {
-                    continue;
-                }
-                ApexInfo ai = getStagedApexInfos(session).get(moduleName);
-                if (ai != null) {
+                getStagedApexInfos(session).stream().map(ai -> {
                     StagedApexInfo info = new StagedApexInfo();
                     info.moduleName = ai.moduleName;
                     info.diskImagePath = ai.modulePath;
@@ -886,17 +861,19 @@
                     info.versionName = ai.versionName;
                     info.hasClassPathJars = ai.hasClassPathJars;
                     return info;
-                }
+                }).forEach(result::add);
             }
         }
-        return null;
+        return result;
     }
 
     private void notifyStagedApexObservers() {
         synchronized (mStagedApexObservers) {
+            List<StagedApexInfo> stagedApexInfos = getStagedApexInfos();
+            ApexStagedEvent event = new ApexStagedEvent();
+            event.stagedApexInfos =
+                    stagedApexInfos.toArray(new StagedApexInfo[stagedApexInfos.size()]);
             for (IStagedApexObserver observer : mStagedApexObservers) {
-                ApexStagedEvent event = new ApexStagedEvent();
-                event.stagedApexModuleNames = getStagedApexModuleNames().toArray(new String[0]);
                 try {
                     observer.onApexStaged(event);
                 } catch (RemoteException re) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8bab9de..6ac8b22 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -195,6 +195,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -1101,23 +1102,14 @@
         if (android.multiuser.Flags.cachesNotInvalidatedAtStartReadOnly()) {
             UserManager.invalidateIsUserUnlockedCache();
             UserManager.invalidateQuietModeEnabledCache();
-            UserManager.invalidateUserSerialNumberCache();
+            if (android.multiuser.Flags.cacheUserPropertiesCorrectlyReadOnly()) {
+                UserManager.invalidateStaticUserProperties();
+                UserManager.invalidateUserPropertiesCache();
+            }
+            UserManager.invalidateCacheOnUserListChange();
         }
     }
 
-    private boolean doesDeviceHardwareSupportPrivateSpace() {
-        return !mPm.hasSystemFeature(FEATURE_EMBEDDED, 0)
-                && !mPm.hasSystemFeature(FEATURE_WATCH, 0)
-                && !mPm.hasSystemFeature(FEATURE_LEANBACK, 0)
-                && !mPm.hasSystemFeature(FEATURE_AUTOMOTIVE, 0);
-    }
-
-    private static boolean isAutoLockForPrivateSpaceEnabled() {
-        return android.os.Flags.allowPrivateProfile()
-                && Flags.supportAutolockForPrivateSpace()
-                && android.multiuser.Flags.enablePrivateSpaceFeatures();
-    }
-
     void systemReady() {
         mAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -1162,6 +1154,19 @@
         }
     }
 
+    private boolean doesDeviceHardwareSupportPrivateSpace() {
+        return !mPm.hasSystemFeature(FEATURE_EMBEDDED, 0)
+                && !mPm.hasSystemFeature(FEATURE_WATCH, 0)
+                && !mPm.hasSystemFeature(FEATURE_LEANBACK, 0)
+                && !mPm.hasSystemFeature(FEATURE_AUTOMOTIVE, 0);
+    }
+
+    private static boolean isAutoLockForPrivateSpaceEnabled() {
+        return android.os.Flags.allowPrivateProfile()
+                && Flags.supportAutolockForPrivateSpace()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures();
+    }
+
     private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() {
         return android.os.Flags.allowPrivateProfile()
                 && android.multiuser.Flags.enablePrivateSpaceAutolockOnRestarts()
@@ -4448,7 +4453,7 @@
 
                             if (userData != null) {
                                 synchronized (mUsersLock) {
-                                    mUsers.put(userData.info.id, userData);
+                                    addUserDataLU(userData);
                                     if (mNextSerialNumber < 0
                                             || mNextSerialNumber <= userData.info.id) {
                                         mNextSerialNumber = userData.info.id + 1;
@@ -5724,7 +5729,7 @@
                     userData.info = userInfo;
                     userData.userProperties = new UserProperties(
                             userTypeDetails.getDefaultUserPropertiesReference());
-                    mUsers.put(userId, userData);
+                    addUserDataLU(userData);
                 }
                 writeUserLP(userData);
                 writeUserListLP();
@@ -6138,7 +6143,7 @@
         final UserData userData = new UserData();
         userData.info = userInfo;
         synchronized (mUsersLock) {
-            mUsers.put(userInfo.id, userData);
+            addUserDataLU(userData);
         }
         updateUserIds();
         return userData;
@@ -6148,8 +6153,7 @@
     @VisibleForTesting
     void removeUserInfo(@UserIdInt int userId) {
         synchronized (mUsersLock) {
-            UserManager.invalidateUserSerialNumberCache();
-            mUsers.remove(userId);
+            removeUserDataLU(userId);
         }
     }
 
@@ -6258,14 +6262,16 @@
         Slog.i(LOG_TAG, "removeUser u" + userId);
         checkCreateUsersPermission("Only the system can remove users");
 
-        final String restriction = getUserRemovalRestriction(userId);
-        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
-            Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
+        final Optional<String> restrictionOptional = getUserRemovalRestrictionOptional(userId);
+        if (!restrictionOptional.isEmpty()
+                && getUserRestrictions(UserHandle.getCallingUserId())
+                        .getBoolean(restrictionOptional.get(), false)) {
+            Slog.w(LOG_TAG, "Cannot remove user. " + restrictionOptional.get() + " is enabled.");
             return false;
         }
         if (mCurrentBootPhase < SystemService.PHASE_ACTIVITY_MANAGER_READY) {
             Slog.w(LOG_TAG, "Cannot remove user, removeUser is called too early during boot. "
-                + "ActivityManager is not ready yet.");
+                            + "ActivityManager is not ready yet.");
             return false;
         }
         return removeUserWithProfilesUnchecked(userId);
@@ -6332,18 +6338,30 @@
     }
 
     /**
-     * Returns the string name of the restriction to check for user removal. The restriction name
-     * varies depending on whether the user is a managed profile.
+     * Returns an optional string name of the restriction to check for user removal. The restriction
+     * name varies depending on whether the user is a managed profile.
+     *
+     * <p>If the flag android.multiuser.ignore_restrictions_when_deleting_private_profile is enabled
+     * and the user is a private profile (i.e. has no removal restrictions) the method will return
+     * {@code Optional.empty()}.
      */
-    private String getUserRemovalRestriction(@UserIdInt int userId) {
+    private Optional<String> getUserRemovalRestrictionOptional(@UserIdInt int userId) {
+        final boolean isPrivateProfile;
         final boolean isManagedProfile;
         final UserInfo userInfo;
         synchronized (mUsersLock) {
             userInfo = getUserInfoLU(userId);
         }
+        isPrivateProfile = userInfo != null && userInfo.isPrivateProfile();
         isManagedProfile = userInfo != null && userInfo.isManagedProfile();
-        return isManagedProfile
-                ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+        if (android.multiuser.Flags.ignoreRestrictionsWhenDeletingPrivateProfile()
+                && isPrivateProfile) {
+            return Optional.empty();
+        }
+        return Optional.of(
+                isManagedProfile
+                        ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
+                        : UserManager.DISALLOW_REMOVE_USER);
     }
 
     private boolean removeUserUnchecked(@UserIdInt int userId) {
@@ -6452,9 +6470,13 @@
         checkCreateUsersPermission("Only the system can remove users");
 
         if (!overrideDevicePolicy) {
-            final String restriction = getUserRemovalRestriction(userId);
-            if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
-                Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
+            final Optional<String> restrictionOptional = getUserRemovalRestrictionOptional(userId);
+            if (!restrictionOptional.isEmpty()
+                    && getUserRestrictions(UserHandle.getCallingUserId())
+                            .getBoolean(restrictionOptional.get(), false)) {
+                Slog.w(
+                        LOG_TAG,
+                        "Cannot remove user. " + restrictionOptional.get() + " is enabled.");
                 return UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION;
             }
         }
@@ -6579,8 +6601,7 @@
 
         // Remove this user from the list
         synchronized (mUsersLock) {
-            UserManager.invalidateUserSerialNumberCache();
-            mUsers.remove(userId);
+            removeUserDataLU(userId);
             mIsUserManaged.delete(userId);
         }
         synchronized (mUserStates) {
@@ -6969,6 +6990,26 @@
     }
 
     /**
+     * Adding user data to mUsers list in one place to invalidate related caches.
+     */
+    @GuardedBy("mUsersLock")
+    private void addUserDataLU(UserData userData) {
+        if (android.multiuser.Flags.invalidateCacheOnUsersChangedReadOnly()) {
+            UserManager.invalidateCacheOnUserListChange();
+        }
+        mUsers.put(userData.info.id, userData);
+    }
+
+    /**
+     * Removing user data to mUsers list in one place to invalidate related caches.
+     */
+    @GuardedBy("mUsersLock")
+    private void removeUserDataLU(@UserIdInt int userId) {
+        UserManager.invalidateCacheOnUserListChange();
+        mUsers.remove(userId);
+    }
+
+    /**
      * Caches the list of user ids in an array, adjusting the array size when necessary.
      */
     private void updateUserIds() {
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java b/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java
index db747f9..67ac2a7 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerificationStatusTracker.java
@@ -30,9 +30,6 @@
     private final @CurrentTimeMillisLong long mMaxTimeoutTime;
     @NonNull
     private final VerifierController.Injector mInjector;
-    // Record the package name associated with the verification result
-    @NonNull
-    private final String mPackageName;
 
     /**
      * By default, the timeout time is the default timeout duration plus the current time (when
@@ -41,10 +38,8 @@
      * can be extended via {@link #extendTimeRemaining} to the maximum allowed.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
-    public VerificationStatusTracker(@NonNull String packageName,
-            long defaultTimeoutMillis, long maxExtendedTimeoutMillis,
+    public VerificationStatusTracker(long defaultTimeoutMillis, long maxExtendedTimeoutMillis,
             @NonNull VerifierController.Injector injector) {
-        mPackageName = packageName;
         mStartTime = injector.getCurrentTimeMillis();
         mTimeoutTime = mStartTime + defaultTimeoutMillis;
         mMaxTimeoutTime = mStartTime + maxExtendedTimeoutMillis;
@@ -93,9 +88,4 @@
     public boolean isTimeout() {
         return mInjector.getCurrentTimeMillis() >= mTimeoutTime;
     }
-
-    @NonNull
-    public String getPackageName() {
-        return mPackageName;
-    }
 }
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
index 7eac940..a35618b 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
@@ -30,11 +30,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningInfo;
-import android.content.pm.verify.pkg.IVerificationSessionCallback;
 import android.content.pm.verify.pkg.IVerificationSessionInterface;
 import android.content.pm.verify.pkg.IVerifierService;
 import android.content.pm.verify.pkg.VerificationSession;
@@ -44,7 +44,6 @@
 import android.os.Handler;
 import android.os.PersistableBundle;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.Pair;
@@ -94,9 +93,22 @@
     // Max duration allowed to wait for a verifier to respond to a verification request.
     private static final long DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS =
             TimeUnit.MINUTES.toMillis(10);
+    /**
+     * Configurable maximum amount of time in milliseconds for the system to wait from the moment
+     * when the installation session requires a verification, till when the request is delivered to
+     * the verifier, pending the connection to be established. If the request has not been delivered
+     * to the verifier within this amount of time, e.g., because the verifier has crashed or ANR'd,
+     * the controller then sends a failure status back to the installation session.
+     * Flag type: {@code long}
+     * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
+     */
+    private static final String PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS =
+            "verifier_connection_timeout_millis";
     // The maximum amount of time to wait from the moment when the session requires a verification,
     // till when the request is delivered to the verifier, pending the connection to be established.
-    private static final long CONNECTION_TIMEOUT_SECONDS = 10;
+    private static final long DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS =
+            TimeUnit.SECONDS.toMillis(10);
+
     // The maximum amount of time to wait before the system unbinds from the verifier.
     private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6);
 
@@ -127,15 +139,16 @@
     }
 
     /**
-     * Used by the installation session to check if a verifier is installed.
+     * Used by the installation session to get the package name of the installed verifier.
      */
-    public boolean isVerifierInstalled(Supplier<Computer> snapshotSupplier, int userId) {
+    @Nullable
+    public String getVerifierPackageName(Supplier<Computer> snapshotSupplier, int userId) {
         if (isVerifierConnected()) {
             // Verifier is connected or is being connected, so it must be installed.
-            return true;
+            return mRemoteServiceComponentName.getPackageName();
         }
         // Verifier has been disconnected, or it hasn't been connected. Check if it's installed.
-        return mInjector.isVerifierInstalled(snapshotSupplier.get(), userId);
+        return mInjector.getVerifierPackageName(snapshotSupplier.get(), userId);
     }
 
     /**
@@ -271,6 +284,7 @@
             int installationSessionId, String packageName,
             Uri stagedPackageUri, SigningInfo signingInfo,
             List<SharedLibraryInfo> declaredLibraries,
+            @PackageInstaller.VerificationPolicy int verificationPolicy,
             PersistableBundle extensionParams, PackageInstallerSession.VerifierCallback callback,
             boolean retry) {
         // Try connecting to the verifier if not already connected
@@ -292,8 +306,7 @@
                 /* id= */ verificationId,
                 /* installSessionId= */ installationSessionId,
                 packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
-                new VerificationSessionInterface(),
-                new VerificationSessionCallback(callback));
+                verificationPolicy, new VerificationSessionInterface(callback));
         AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
             if (!retry) {
                 if (DEBUG) {
@@ -306,7 +319,8 @@
                 }
                 service.onVerificationRetry(session);
             }
-        }).orTimeout(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS).whenComplete((res, err) -> {
+        }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
+                .whenComplete((res, err) -> {
             if (err != null) {
                 Slog.e(TAG, "Error notifying verification request for session " + verificationId,
                         err);
@@ -318,7 +332,7 @@
         final long defaultTimeoutMillis = mInjector.getVerificationRequestTimeoutMillis();
         final long maxExtendedTimeoutMillis = mInjector.getMaxVerificationExtendedTimeoutMillis();
         final VerificationStatusTracker tracker = new VerificationStatusTracker(
-                packageName, defaultTimeoutMillis, maxExtendedTimeoutMillis, mInjector);
+                defaultTimeoutMillis, maxExtendedTimeoutMillis, mInjector);
         synchronized (mVerificationStatus) {
             mVerificationStatus.put(verificationId, tracker);
         }
@@ -407,6 +421,12 @@
 
     // This class handles requests from the remote verifier
     private class VerificationSessionInterface extends IVerificationSessionInterface.Stub {
+        private final PackageInstallerSession.VerifierCallback mCallback;
+
+        VerificationSessionInterface(PackageInstallerSession.VerifierCallback callback) {
+            mCallback = callback;
+        }
+
         @Override
         public long getTimeoutTime(int verificationId) {
             checkCallerPermission();
@@ -432,17 +452,23 @@
                 return tracker.extendTimeRemaining(additionalMs);
             }
         }
-    }
 
-    private class VerificationSessionCallback extends IVerificationSessionCallback.Stub {
-        private final PackageInstallerSession.VerifierCallback mCallback;
-
-        VerificationSessionCallback(PackageInstallerSession.VerifierCallback callback) {
-            mCallback = callback;
+        @Override
+        public boolean setVerificationPolicy(int verificationId,
+                @PackageInstaller.VerificationPolicy int policy) {
+            checkCallerPermission();
+            synchronized (mVerificationStatus) {
+                final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
+                if (tracker == null) {
+                    throw new IllegalStateException("Verification session " + verificationId
+                            + " doesn't exist or has finished");
+                }
+            }
+            return mCallback.setVerificationPolicy(policy);
         }
 
         @Override
-        public void reportVerificationIncomplete(int id, int reason) throws RemoteException {
+        public void reportVerificationIncomplete(int id, int reason) {
             checkCallerPermission();
             final VerificationStatusTracker tracker;
             synchronized (mVerificationStatus) {
@@ -451,23 +477,21 @@
                     throw new IllegalStateException("Verification session " + id
                             + " doesn't exist or has finished");
                 }
-                mCallback.onVerificationIncompleteReceived(reason);
             }
+            mCallback.onVerificationIncompleteReceived(reason);
             // Remove status tracking and stop the timeout countdown
             removeStatusTracker(id);
         }
 
         @Override
-        public void reportVerificationComplete(int id, VerificationStatus verificationStatus)
-                throws RemoteException {
+        public void reportVerificationComplete(int id, VerificationStatus verificationStatus) {
             reportVerificationCompleteWithExtensionResponse(id, verificationStatus,
                     /* extensionResponse= */ null);
         }
 
         @Override
         public void reportVerificationCompleteWithExtensionResponse(int id,
-                VerificationStatus verificationStatus, PersistableBundle extensionResponse)
-                throws RemoteException {
+                VerificationStatus verificationStatus, PersistableBundle extensionResponse) {
             checkCallerPermission();
             final VerificationStatusTracker tracker;
             synchronized (mVerificationStatus) {
@@ -519,10 +543,15 @@
         }
 
         /**
-         * Check if a verifier is installed on this device.
+         * Return the package name of the verifier installed on this device.
          */
-        public boolean isVerifierInstalled(Computer snapshot, int userId) {
-            return resolveVerifierComponentName(snapshot, userId) != null;
+        @Nullable
+        public String getVerifierPackageName(Computer snapshot, int userId) {
+            final ComponentName componentName = resolveVerifierComponentName(snapshot, userId);
+            if (componentName == null) {
+                return null;
+            }
+            return componentName.getPackageName();
         }
 
         /**
@@ -630,6 +659,14 @@
             return getMaxVerificationExtendedTimeoutMillisFromDeviceConfig();
         }
 
+        /**
+         * This is added so that we can mock the maximum connection timeout duration without
+         * calling into DeviceConfig.
+         */
+        public long getVerifierConnectionTimeoutMillis() {
+            return getVerifierConnectionTimeoutMillisFromDeviceConfig();
+        }
+
         private static long getVerificationRequestTimeoutMillisFromDeviceConfig() {
             return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                     PROPERTY_VERIFICATION_REQUEST_TIMEOUT_MILLIS,
@@ -641,5 +678,11 @@
                     PROPERTY_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS,
                     DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS);
         }
+
+        private static long getVerifierConnectionTimeoutMillisFromDeviceConfig() {
+            return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                    PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS,
+                    DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e47b4c2..ad5c840 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -731,7 +731,10 @@
                     KeyEvent.KEYCODE_ASSIST,
                     KeyEvent.KEYCODE_VOICE_ASSIST,
                     KeyEvent.KEYCODE_MUTE,
-                    KeyEvent.KEYCODE_VOLUME_MUTE
+                    KeyEvent.KEYCODE_VOLUME_MUTE,
+                    KeyEvent.KEYCODE_RECENT_APPS,
+                    KeyEvent.KEYCODE_APP_SWITCH,
+                    KeyEvent.KEYCODE_NOTIFICATION
             ));
 
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
@@ -2082,12 +2085,21 @@
             }
             switch (mDoubleTapOnHomeBehavior) {
                 case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
+                    if (!isKeyEventForCurrentUser(
+                            event.getDisplayId(), event.getKeyCode(), "toggleRecentApps")) {
+                        break;
+                    }
                     notifyKeyGestureCompleted(event,
                             KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH);
                     mHomeConsumed = true;
                     toggleRecentApps();
                     break;
                 case DOUBLE_TAP_HOME_PIP_MENU:
+                    if (!isKeyEventForCurrentUser(
+                            event.getDisplayId(), event.getKeyCode(),
+                            "showPictureInPictureMenu")) {
+                        break;
+                    }
                     mHomeConsumed = true;
                     showPictureInPictureMenuInternal();
                     break;
@@ -2116,12 +2128,20 @@
                     }
                     break;
                 case LONG_PRESS_HOME_ASSIST:
+                    if (!isKeyEventForCurrentUser(
+                            event.getDisplayId(), event.getKeyCode(), "launchAssistAction")) {
+                        break;
+                    }
                     notifyKeyGestureCompleted(event,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
                     launchAssistAction(null, event.getDeviceId(), event.getEventTime(),
                             AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
                     break;
                 case LONG_PRESS_HOME_NOTIFICATION_PANEL:
+                    if (!isKeyEventForCurrentUser(
+                            event.getDisplayId(), event.getKeyCode(), "toggleNotificationPanel")) {
+                        break;
+                    }
                     notifyKeyGestureCompleted(event,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
                     toggleNotificationPanel();
@@ -3497,7 +3517,11 @@
 
         if (isUserSetupComplete() && !keyguardOn) {
             if (mModifierShortcutManager.interceptKey(event)) {
-                dismissKeyboardShortcutsMenu();
+                if (isKeyEventForCurrentUser(
+                        event.getDisplayId(), event.getKeyCode(),
+                        "dismissKeyboardShortcutsMenu")) {
+                    dismissKeyboardShortcutsMenu();
+                }
                 mPendingMetaAction = false;
                 mPendingCapsLockToggle = false;
                 return true;
@@ -4820,7 +4844,10 @@
         }
 
         // no keyguard stuff to worry about, just launch home!
-        if (mRecentsVisible) {
+        // If Recents is visible and the action is not from visible background users,
+        // hide Recents and notify it to launch Home.
+        if (mRecentsVisible
+                && (!mVisibleBackgroundUsersEnabled || displayId == DEFAULT_DISPLAY)) {
             try {
                 ActivityManager.getService().stopAppSwitches();
             } catch (RemoteException e) {}
@@ -5570,6 +5597,9 @@
      * Notify the StatusBar that a system key was pressed.
      */
     private void sendSystemKeyToStatusBar(KeyEvent key) {
+        if (!isKeyEventForCurrentUser(key.getDisplayId(), key.getKeyCode(), "handleSystemKey")) {
+            return;
+        }
         IStatusBarService statusBar = getStatusBarService();
         if (statusBar != null) {
             try {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index eb62b56..8ba56c5 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -771,6 +771,10 @@
     public void onGroupRemoved(int groupId) {
         mInteractivityByGroupId.remove(groupId);
         mWakefulnessSessionObserver.removePowerGroup(groupId);
+        if (mFlags.isPerDisplayWakeByTouchEnabled()) {
+            resetDisplayInteractivities();
+            mInputManagerInternal.setDisplayInteractivities(mDisplayInteractivities);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index fd60e06..db57d11 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -47,6 +47,9 @@
             Flags::perDisplayWakeByTouch
     );
 
+    private final FlagState mFrameworkWakelockInfo =
+            new FlagState(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO, Flags::frameworkWakelockInfo);
+
     /** Returns whether early-screen-timeout-detector is enabled on not. */
     public boolean isEarlyScreenTimeoutDetectorEnabled() {
         return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -67,6 +70,13 @@
     }
 
     /**
+     * @return Whether FrameworkWakelockInfo atom logging is enabled or not.
+     */
+    public boolean isFrameworkWakelockInfoEnabled() {
+        return mFrameworkWakelockInfo.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
@@ -75,6 +85,7 @@
         pw.println(" " + mEarlyScreenTimeoutDetectorFlagState);
         pw.println(" " + mImproveWakelockLatency);
         pw.println(" " + mPerDisplayWakeByTouch);
+        pw.println(" " + mFrameworkWakelockInfo);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index 9cf3bb6..8bb69ba 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -26,3 +26,10 @@
     bug: "343295183"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "framework_wakelock_info"
+    namespace: "power"
+    description: "Feature flag to enable statsd pulling of FrameworkWakelockInfo atoms"
+    bug: "352602149"
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index e0ffd88..c04158f 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -145,6 +145,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.LocalServices;
+import com.android.server.power.feature.PowerManagerFlags;
 import com.android.server.power.optimization.Flags;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
 import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
@@ -5160,6 +5161,10 @@
             mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
                     uidStats.mProcessState, true /* acquired */,
                     getPowerManagerWakeLockLevel(type));
+            if (mPowerManagerFlags.isFrameworkWakelockInfoEnabled()) {
+                mFrameworkEvents.noteStartWakeLock(
+                        mapIsolatedUid(uid), name, getPowerManagerWakeLockLevel(type), uptimeMs);
+            }
         }
     }
 
@@ -5205,6 +5210,10 @@
             mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name,
                     uidStats.mProcessState, false/* acquired */,
                     getPowerManagerWakeLockLevel(type));
+            if (mPowerManagerFlags.isFrameworkWakelockInfoEnabled()) {
+                mFrameworkEvents.noteStopWakeLock(
+                        mapIsolatedUid(uid), name, getPowerManagerWakeLockLevel(type), uptimeMs);
+            }
 
             if (mappedUid != uid) {
                 // Decrement the ref count for the isolated uid and delete the mapping if uneeded.
@@ -11361,6 +11370,9 @@
         return mTmpCpuTimeInFreq;
     }
 
+    WakelockStatsFrameworkEvents mFrameworkEvents = new WakelockStatsFrameworkEvents();
+    PowerManagerFlags mPowerManagerFlags = new PowerManagerFlags();
+
     public BatteryStatsImpl(@NonNull BatteryStatsConfig config, @NonNull Clock clock,
             @NonNull MonotonicClock monotonicClock, @Nullable File systemDir,
             @NonNull Handler handler, @Nullable PlatformIdleStateCallback platformIdleStateCallback,
@@ -15980,6 +15992,10 @@
                 // Already plugged in. Schedule the long plug in alarm.
                 scheduleNextResetWhilePluggedInCheck();
             }
+
+            if (mPowerManagerFlags.isFrameworkWakelockInfoEnabled()) {
+                mFrameworkEvents.initialize(context);
+            }
         }
     }
 
@@ -16807,7 +16823,8 @@
         }
         int NSORPMS = in.readInt();
         if (NSORPMS > 10000) {
-            throw new ParcelFormatException("File corrupt: too many screen-off rpm stats " + NSORPMS);
+            throw new ParcelFormatException(
+                    "File corrupt: too many screen-off rpm stats " + NSORPMS);
         }
         for (int irpm = 0; irpm < NSORPMS; irpm++) {
             if (in.readInt() != 0) {
diff --git a/services/core/java/com/android/server/power/stats/WakelockStatsFrameworkEvents.java b/services/core/java/com/android/server/power/stats/WakelockStatsFrameworkEvents.java
new file mode 100644
index 0000000..f387fec
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/WakelockStatsFrameworkEvents.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.StatsEvent;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/** A class to initialise and log metrics pulled by statsd. */
+public class WakelockStatsFrameworkEvents {
+    // statsd has a dimensional limit on the number of different keys it can handle.
+    // Beyond that limit, statsd will drop data.
+    //
+    // When we have seem SUMMARY_THRESHOLD distinct (uid, tag, wakeLockLevel) keys,
+    // we start summarizing new keys as (uid, OVERFLOW_TAG, OVERFLOW_LEVEL) to
+    // reduce the number of keys we pass to statsd.
+    //
+    // When we reach MAX_WAKELOCK_DIMENSIONS distinct keys, we summarize all new keys
+    // as (OVERFLOW_UID, HARD_CAP_TAG, OVERFLOW_LEVEL) to hard cap the number of
+    // distinct keys we pass to statsd.
+    @VisibleForTesting public static final int SUMMARY_THRESHOLD = 500;
+    @VisibleForTesting public static final int MAX_WAKELOCK_DIMENSIONS = 1000;
+
+    @VisibleForTesting public static final int HARD_CAP_UID = -1;
+    @VisibleForTesting public static final String OVERFLOW_TAG = "*overflow*";
+    @VisibleForTesting public static final String HARD_CAP_TAG = "*overflow hard cap*";
+    @VisibleForTesting public static final int OVERFLOW_LEVEL = 1;
+
+    private static class WakeLockKey {
+        private int uid;
+        private String tag;
+        private int powerManagerWakeLockLevel;
+        private int hashCode;
+
+        WakeLockKey(int uid, String tag, int powerManagerWakeLockLevel) {
+            this.uid = uid;
+            this.tag = new String(tag);
+            this.powerManagerWakeLockLevel = powerManagerWakeLockLevel;
+
+            this.hashCode = Objects.hash(uid, tag, powerManagerWakeLockLevel);
+        }
+
+        int getUid() {
+            return uid;
+        }
+
+        String getTag() {
+            return tag;
+        }
+
+        int getPowerManagerWakeLockLevel() {
+            return powerManagerWakeLockLevel;
+        }
+
+        void setOverflow() {
+            tag = OVERFLOW_TAG;
+            powerManagerWakeLockLevel = OVERFLOW_LEVEL;
+            this.hashCode = Objects.hash(uid, tag, powerManagerWakeLockLevel);
+        }
+
+        void setHardCap() {
+            uid = HARD_CAP_UID;
+            tag = HARD_CAP_TAG;
+            powerManagerWakeLockLevel = OVERFLOW_LEVEL;
+            this.hashCode = Objects.hash(uid, tag, powerManagerWakeLockLevel);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || !(o instanceof WakeLockKey)) return false;
+
+            WakeLockKey that = (WakeLockKey) o;
+            return uid == that.uid
+                    && tag.equals(that.tag)
+                    && powerManagerWakeLockLevel == that.powerManagerWakeLockLevel;
+        }
+
+        @Override
+        public int hashCode() {
+            return this.hashCode;
+        }
+    }
+
+    private static class WakeLockStats {
+        // accumulated uptime attributed to this WakeLock since boot, where overlap
+        // (including nesting) is ignored
+        public long uptimeMillis = 0;
+
+        // count of WakeLocks that have been acquired and then released
+        public long completedCount = 0;
+    }
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final Map<WakeLockKey, WakeLockStats> mWakeLockStats = new HashMap<>();
+
+    private static class WakeLockData {
+        // uptime millis when first acquired
+        public long acquireUptimeMillis = 0;
+        public int refCount = 0;
+
+        WakeLockData(long uptimeMillis) {
+            acquireUptimeMillis = uptimeMillis;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private final Map<WakeLockKey, WakeLockData> mOpenWakeLocks = new HashMap<>();
+
+    public void noteStartWakeLock(
+            int uid, String tag, int powerManagerWakeLockLevel, long eventUptimeMillis) {
+        final WakeLockKey key = new WakeLockKey(uid, tag, powerManagerWakeLockLevel);
+
+        synchronized (mLock) {
+            WakeLockData data =
+                    mOpenWakeLocks.computeIfAbsent(key, k -> new WakeLockData(eventUptimeMillis));
+            data.refCount++;
+            mOpenWakeLocks.put(key, data);
+        }
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    public boolean inOverflow() {
+        return mWakeLockStats.size() >= SUMMARY_THRESHOLD;
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    public boolean inHardCap() {
+        return mWakeLockStats.size() >= MAX_WAKELOCK_DIMENSIONS;
+    }
+
+    public void noteStopWakeLock(
+            int uid, String tag, int powerManagerWakeLockLevel, long eventUptimeMillis) {
+        WakeLockKey key = new WakeLockKey(uid, tag, powerManagerWakeLockLevel);
+
+        synchronized (mLock) {
+            WakeLockData data = mOpenWakeLocks.get(key);
+            if (data == null) {
+                Log.e(TAG, "WakeLock not found when stopping: " + uid + " " + tag);
+                return;
+            }
+
+            if (data.refCount == 1) {
+                mOpenWakeLocks.remove(key);
+                long wakeLockDur = eventUptimeMillis - data.acquireUptimeMillis;
+
+                // Rewrite key if in an overflow state.
+                if (inOverflow() && !mWakeLockStats.containsKey(key)) {
+                    key.setOverflow();
+                    if (inHardCap() && !mWakeLockStats.containsKey(key)) {
+                        key.setHardCap();
+                    }
+                }
+
+                WakeLockStats stats = mWakeLockStats.computeIfAbsent(key, k -> new WakeLockStats());
+                stats.uptimeMillis += wakeLockDur;
+                stats.completedCount++;
+                mWakeLockStats.put(key, stats);
+            } else {
+                data.refCount--;
+                mOpenWakeLocks.put(key, data);
+            }
+        }
+    }
+
+    // Shim interface for testing.
+    @VisibleForTesting
+    public interface EventLogger {
+        void logResult(
+                int uid, String tag, int wakeLockLevel, long uptimeMillis, long completedCount);
+    }
+
+    public List<StatsEvent> pullFrameworkWakelockInfoAtoms() {
+        List<StatsEvent> result = new ArrayList<>();
+        EventLogger logger =
+                new EventLogger() {
+                    public void logResult(
+                            int uid,
+                            String tag,
+                            int wakeLockLevel,
+                            long uptimeMillis,
+                            long completedCount) {
+                        StatsEvent event =
+                                StatsEvent.newBuilder()
+                                        .setAtomId(FrameworkStatsLog.FRAMEWORK_WAKELOCK_INFO)
+                                        .writeInt(uid)
+                                        .writeString(tag)
+                                        .writeInt(wakeLockLevel)
+                                        .writeLong(uptimeMillis)
+                                        .writeLong(completedCount)
+                                        .build();
+                        result.add(event);
+                    }
+                };
+        pullFrameworkWakelockInfoAtoms(SystemClock.uptimeMillis(), logger);
+        return result;
+    }
+
+    @VisibleForTesting
+    public void pullFrameworkWakelockInfoAtoms(long nowMillis, EventLogger logger) {
+        HashSet<WakeLockKey> keys = new HashSet<>();
+
+        // Used to collect open WakeLocks when in an overflow state.
+        HashMap<WakeLockKey, WakeLockStats> openOverflowStats = new HashMap<>();
+
+        synchronized (mLock) {
+            keys.addAll(mWakeLockStats.keySet());
+
+            // If we are in an overflow state, an open wakelock may have a new key
+            // that needs to be summarized.
+            if (inOverflow()) {
+                for (WakeLockKey key : mOpenWakeLocks.keySet()) {
+                    if (!mWakeLockStats.containsKey(key)) {
+                        WakeLockData data = mOpenWakeLocks.get(key);
+
+                        key.setOverflow();
+                        if (inHardCap() && !mWakeLockStats.containsKey(key)) {
+                            key.setHardCap();
+                        }
+                        keys.add(key);
+
+                        WakeLockStats stats =
+                                openOverflowStats.computeIfAbsent(key, k -> new WakeLockStats());
+                        stats.uptimeMillis += nowMillis - data.acquireUptimeMillis;
+                        openOverflowStats.put(key, stats);
+                    }
+                }
+            } else {
+                keys.addAll(mOpenWakeLocks.keySet());
+            }
+
+            for (WakeLockKey key : keys) {
+                long openWakeLockUptime = 0;
+                WakeLockData data = mOpenWakeLocks.get(key);
+                if (data != null) {
+                    openWakeLockUptime = nowMillis - data.acquireUptimeMillis;
+                }
+
+                WakeLockStats stats = mWakeLockStats.computeIfAbsent(key, k -> new WakeLockStats());
+                WakeLockStats extraTime =
+                        openOverflowStats.computeIfAbsent(key, k -> new WakeLockStats());
+
+                stats.uptimeMillis += openWakeLockUptime + extraTime.uptimeMillis;
+
+                logger.logResult(
+                        key.getUid(),
+                        key.getTag(),
+                        key.getPowerManagerWakeLockLevel(),
+                        stats.uptimeMillis,
+                        stats.completedCount);
+            }
+        }
+    }
+
+    private static final String TAG = "BatteryStatsPulledMetrics";
+
+    private final StatsPullCallbackHandler mStatsPullCallbackHandler =
+            new StatsPullCallbackHandler();
+
+    private boolean mIsInitialized = false;
+
+    public void initialize(Context context) {
+        if (mIsInitialized) {
+            return;
+        }
+
+        final StatsManager statsManager = context.getSystemService(StatsManager.class);
+        if (statsManager == null) {
+            Log.e(
+                    TAG,
+                    "Error retrieving StatsManager. Cannot initialize BatteryStatsPulledMetrics.");
+        } else {
+            Log.d(TAG, "Registering callback with StatsManager");
+
+            // DIRECT_EXECUTOR means that callback will run on binder thread.
+            statsManager.setPullAtomCallback(
+                    FrameworkStatsLog.FRAMEWORK_WAKELOCK_INFO,
+                    null /* metadata */,
+                    ConcurrentUtils.DIRECT_EXECUTOR,
+                    mStatsPullCallbackHandler);
+            mIsInitialized = true;
+        }
+    }
+
+    private class StatsPullCallbackHandler implements StatsManager.StatsPullAtomCallback {
+        @Override
+        public int onPullAtom(int atomTag, List<StatsEvent> data) {
+            // handle the tags appropriately.
+            List<StatsEvent> events = pullEvents(atomTag);
+            if (events == null) {
+                return StatsManager.PULL_SKIP;
+            }
+
+            data.addAll(events);
+            return StatsManager.PULL_SUCCESS;
+        }
+
+        private List<StatsEvent> pullEvents(int atomTag) {
+            switch (atomTag) {
+                case FrameworkStatsLog.FRAMEWORK_WAKELOCK_INFO:
+                    return pullFrameworkWakelockInfoAtoms();
+                default:
+                    return null;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 685ab3a..ab756f2 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -54,7 +54,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
-import com.android.server.RescueParty;
+import com.android.server.crashrecovery.CrashRecoveryAdaptor;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import java.io.File;
@@ -627,7 +627,7 @@
 
             if (!deprecateFlagsAndSettingsResets()) {
                 // Clear flags.
-                RescueParty.resetDeviceConfigForPackages(packageNames);
+                CrashRecoveryAdaptor.rescuePartyResetDeviceConfigForPackages(packageNames);
             }
 
             Consumer<Intent> onResult = result -> {
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 8f603fc..075a31f 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -191,8 +191,7 @@
                         SensorPrivacyManager.Sensors.CAMERA);
                 if (mIsServiceEnabled && isCameraAvailable) {
                     final RotationResolverManagerPerUserService service =
-                            getServiceForUserLocked(
-                                    UserHandle.getCallingUserId());
+                            getServiceForUserLocked(UserHandle.USER_CURRENT);
                     final RotationResolutionRequest request;
                     if (packageName == null) {
                         request = new RotationResolutionRequest(/* packageName */ "",
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index 9b39fa1..a49a9fd 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -46,6 +46,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.FileDescriptor;
@@ -89,6 +90,8 @@
     @GuardedBy("mSearchables")
     private final SparseArray<Searchables> mSearchables = new SparseArray<>();
 
+    private final UserManagerInternal mUserManagerInternal;
+
     /**
      * Initializes the Search Manager service in the provided system context.
      * Only one instance of this object should be created!
@@ -101,6 +104,7 @@
         mMyPackageMonitor.register(context, null, UserHandle.ALL, true);
         new GlobalSearchProviderObserver(context.getContentResolver());
         mHandler = BackgroundThread.getHandler();
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
     }
 
     private Searchables getSearchables(int userId) {
@@ -336,6 +340,14 @@
 
     @Override
     public void launchAssist(int userHandle, Bundle args) {
+        // Currently, visible background users are not allowed to launch assist.(b/332222893)
+        // TODO(b/368715893): Consider indirect calls from system service when checking the
+        // calling user.
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (mUserManagerInternal.isVisibleBackgroundFullUser(callingUserId)) {
+            throw new SecurityException("Visible background user(u" + callingUserId
+                    + ") is not permitted to launch assist.");
+        }
         StatusBarManagerInternal statusBarManager =
                 LocalServices.getService(StatusBarManagerInternal.class);
         if (statusBarManager != null) {
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
new file mode 100644
index 0000000..9a63c823
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.advancedprotection;
+
+import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE;
+
+import android.Manifest;
+import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PermissionEnforcer;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.provider.Settings;
+import android.security.advancedprotection.IAdvancedProtectionCallback;
+import android.security.advancedprotection.IAdvancedProtectionService;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+
+/** @hide */
+public class AdvancedProtectionService extends IAdvancedProtectionService.Stub  {
+    private static final int MODE_CHANGED = 0;
+    private static final int CALLBACK_ADDED = 1;
+
+    private final Handler mHandler;
+    private final AdvancedProtectionStore mStore;
+    private final ArrayMap<IBinder, IAdvancedProtectionCallback> mCallbacks = new ArrayMap<>();
+
+    private AdvancedProtectionService(@NonNull Context context) {
+        super(PermissionEnforcer.fromContext(context));
+        mHandler = new AdvancedProtectionHandler(FgThread.get().getLooper());
+        mStore = new AdvancedProtectionStore(context);
+    }
+
+    @VisibleForTesting
+    AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store,
+            @NonNull Looper looper, @NonNull PermissionEnforcer permissionEnforcer) {
+        super(permissionEnforcer);
+        mStore = store;
+        mHandler = new AdvancedProtectionHandler(looper);
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE)
+    public boolean isAdvancedProtectionEnabled() {
+        isAdvancedProtectionEnabled_enforcePermission();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return isAdvancedProtectionEnabledInternal();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    // Without permission check
+    private boolean isAdvancedProtectionEnabledInternal() {
+        return mStore.retrieve();
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE)
+    public void registerAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback)
+            throws RemoteException {
+        registerAdvancedProtectionCallback_enforcePermission();
+        IBinder b = callback.asBinder();
+        b.linkToDeath(new DeathRecipient(b), 0);
+        synchronized (mCallbacks) {
+            mCallbacks.put(b, callback);
+            sendCallbackAdded(isAdvancedProtectionEnabledInternal(), callback);
+        }
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE)
+    public void unregisterAdvancedProtectionCallback(@NonNull IAdvancedProtectionCallback callback)
+            throws RemoteException {
+        unregisterAdvancedProtectionCallback_enforcePermission();
+        synchronized (mCallbacks) {
+            mCallbacks.remove(callback.asBinder());
+        }
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE)
+    public void setAdvancedProtectionEnabled(boolean enabled) {
+        setAdvancedProtectionEnabled_enforcePermission();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mCallbacks) {
+                if (enabled != isAdvancedProtectionEnabledInternal()) {
+                    mStore.store(enabled);
+                    sendModeChanged(enabled);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out,
+            FileDescriptor err, @NonNull String[] args, ShellCallback callback,
+            @NonNull ResultReceiver resultReceiver) {
+        (new AdvancedProtectionShellCommand(this))
+                .exec(this, in, out, err, args, callback, resultReceiver);
+    }
+
+    void sendModeChanged(boolean enabled) {
+        Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1)
+                .sendToTarget();
+    }
+
+    void sendCallbackAdded(boolean enabled, IAdvancedProtectionCallback callback) {
+        Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused*/ -1,
+                        /*callback*/ callback)
+                .sendToTarget();
+    }
+
+    public static final class Lifecycle extends SystemService {
+        private final AdvancedProtectionService mService;
+
+        public Lifecycle(@NonNull Context context) {
+            super(context);
+            mService = new AdvancedProtectionService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.ADVANCED_PROTECTION_SERVICE, mService);
+        }
+    }
+
+    @VisibleForTesting
+    static class AdvancedProtectionStore {
+        private final Context mContext;
+        private static final int APM_ON = 1;
+        private static final int APM_OFF = 0;
+        private final UserManagerInternal mUserManager;
+
+        AdvancedProtectionStore(@NonNull Context context) {
+            mContext = context;
+            mUserManager = LocalServices.getService(UserManagerInternal.class);
+        }
+
+        void store(boolean enabled) {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    ADVANCED_PROTECTION_MODE, enabled ? APM_ON : APM_OFF,
+                    mUserManager.getMainUserId());
+        }
+
+        boolean retrieve() {
+            return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    ADVANCED_PROTECTION_MODE, APM_OFF, mUserManager.getMainUserId()) == APM_ON;
+        }
+    }
+
+    private class AdvancedProtectionHandler extends Handler {
+        private AdvancedProtectionHandler(@NonNull Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            switch (msg.what) {
+                //arg1 == enabled
+                case MODE_CHANGED:
+                    handleAllCallbacks(msg.arg1 == 1);
+                    break;
+                //arg1 == enabled
+                //obj == callback
+                case CALLBACK_ADDED:
+                    handleSingleCallback(msg.arg1 == 1, (IAdvancedProtectionCallback) msg.obj);
+                    break;
+            }
+        }
+
+        private void handleAllCallbacks(boolean enabled) {
+            ArrayList<IAdvancedProtectionCallback> deadObjects = new ArrayList<>();
+
+            synchronized (mCallbacks) {
+                for (int i = 0; i < mCallbacks.size(); i++) {
+                    IAdvancedProtectionCallback callback = mCallbacks.valueAt(i);
+                    try {
+                        callback.onAdvancedProtectionChanged(enabled);
+                    } catch (RemoteException e) {
+                        deadObjects.add(callback);
+                    }
+                }
+
+                for (int i = 0; i < deadObjects.size(); i++) {
+                    mCallbacks.remove(deadObjects.get(i).asBinder());
+                }
+            }
+        }
+
+        private void handleSingleCallback(boolean enabled, IAdvancedProtectionCallback callback) {
+            try {
+                callback.onAdvancedProtectionChanged(enabled);
+            } catch (RemoteException e) {
+                mCallbacks.remove(callback.asBinder());
+            }
+        }
+    }
+
+    private final class DeathRecipient implements IBinder.DeathRecipient {
+        private final IBinder mBinder;
+
+        DeathRecipient(IBinder  binder) {
+            mBinder = binder;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mCallbacks) {
+                mCallbacks.remove(mBinder);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java
new file mode 100644
index 0000000..42505ad
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.advancedprotection;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+class AdvancedProtectionShellCommand extends ShellCommand {
+    private AdvancedProtectionService mService;
+
+    AdvancedProtectionShellCommand(@NonNull AdvancedProtectionService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            switch (cmd) {
+                case "help":
+                    onHelp();
+                    return 0;
+                case "set-protection-enabled":
+                    return setProtectionEnabled();
+                case "is-protection-enabled":
+                    return isProtectionEnabled(pw);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        dumpHelp(pw);
+    }
+
+    private void dumpHelp(@NonNull PrintWriter pw) {
+        pw.println("Advanced Protection Mode commands:");
+        pw.println("  help");
+        pw.println("      Print this help text.");
+        pw.println("  set-protection-enabled [true|false]");
+        pw.println("  is-protection-enabled");
+    }
+
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    private int setProtectionEnabled() throws RemoteException {
+        String protectionMode = getNextArgRequired();
+        mService.setAdvancedProtectionEnabled(Boolean.parseBoolean(protectionMode));
+        return 0;
+    }
+
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    private int isProtectionEnabled(@NonNull PrintWriter pw) throws RemoteException {
+        boolean protectionMode = mService.isAdvancedProtectionEnabled();
+        pw.println(protectionMode);
+        return 0;
+    }
+}
diff --git a/services/core/java/com/android/server/security/advancedprotection/OWNERS b/services/core/java/com/android/server/security/advancedprotection/OWNERS
new file mode 100644
index 0000000..9bf5e58
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:main:/core/java/android/security/advancedprotection/OWNERS
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
new file mode 100644
index 0000000..07639d1
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.forensic;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.security.forensic.IForensicService;
+import android.security.forensic.IForensicServiceCommandCallback;
+import android.security.forensic.IForensicServiceStateCallback;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class ForensicService extends SystemService {
+    private static final String TAG = "ForensicService";
+
+    private static final int MSG_MONITOR_STATE = 0;
+    private static final int MSG_MAKE_VISIBLE = 1;
+    private static final int MSG_MAKE_INVISIBLE = 2;
+    private static final int MSG_ENABLE = 3;
+    private static final int MSG_DISABLE = 4;
+    private static final int MSG_BACKUP = 5;
+
+    private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
+    private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
+    private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+    private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
+
+    private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
+    private static final int ERROR_PERMISSION_DENIED =
+            IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
+    private static final int ERROR_INVALID_STATE_TRANSITION =
+            IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
+    private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
+            IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+    private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
+            IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final BinderService mBinderService;
+
+    private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
+    private volatile int mState = STATE_INVISIBLE;
+
+    public ForensicService(@NonNull Context context) {
+        this(new InjectorImpl(context));
+    }
+
+    @VisibleForTesting
+    ForensicService(@NonNull Injector injector) {
+        super(injector.getContext());
+        mContext = injector.getContext();
+        mHandler = new EventHandler(injector.getLooper(), this);
+        mBinderService = new BinderService(this);
+    }
+
+    @VisibleForTesting
+    protected void setState(int state) {
+        mState = state;
+    }
+
+    private static final class BinderService extends IForensicService.Stub {
+        final ForensicService mService;
+
+        BinderService(ForensicService service)  {
+            mService = service;
+        }
+
+        @Override
+        public void monitorState(IForensicServiceStateCallback callback) {
+            mService.mHandler.obtainMessage(MSG_MONITOR_STATE, callback).sendToTarget();
+        }
+
+        @Override
+        public void makeVisible(IForensicServiceCommandCallback callback) {
+            mService.mHandler.obtainMessage(MSG_MAKE_VISIBLE, callback).sendToTarget();
+        }
+
+        @Override
+        public void makeInvisible(IForensicServiceCommandCallback callback) {
+            mService.mHandler.obtainMessage(MSG_MAKE_INVISIBLE, callback).sendToTarget();
+        }
+
+        @Override
+        public void enable(IForensicServiceCommandCallback callback) {
+            mService.mHandler.obtainMessage(MSG_ENABLE, callback).sendToTarget();
+        }
+
+        @Override
+        public void disable(IForensicServiceCommandCallback callback) {
+            mService.mHandler.obtainMessage(MSG_DISABLE, callback).sendToTarget();
+        }
+    }
+
+    private static class EventHandler extends Handler {
+        private final ForensicService mService;
+
+        EventHandler(Looper looper, ForensicService service) {
+            super(looper);
+            mService = service;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_MONITOR_STATE:
+                    try {
+                        mService.monitorState(
+                                (IForensicServiceStateCallback) msg.obj);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "RemoteException", e);
+                    }
+                    break;
+                case MSG_MAKE_VISIBLE:
+                    try {
+                        mService.makeVisible((IForensicServiceCommandCallback) msg.obj);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "RemoteException", e);
+                    }
+                    break;
+                case MSG_MAKE_INVISIBLE:
+                    try {
+                        mService.makeInvisible((IForensicServiceCommandCallback) msg.obj);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "RemoteException", e);
+                    }
+                    break;
+                case MSG_ENABLE:
+                    try {
+                        mService.enable((IForensicServiceCommandCallback) msg.obj);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "RemoteException", e);
+                    }
+                    break;
+                case MSG_DISABLE:
+                    try {
+                        mService.disable((IForensicServiceCommandCallback) msg.obj);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "RemoteException", e);
+                    }
+                    break;
+                default:
+                    Slog.w(TAG, "Unknown message: " + msg.what);
+            }
+        }
+    }
+
+    private void monitorState(IForensicServiceStateCallback callback) throws RemoteException {
+        for (int i = 0; i < mStateMonitors.size(); i++) {
+            if (mStateMonitors.get(i).asBinder() == callback.asBinder()) {
+                return;
+            }
+        }
+        mStateMonitors.add(callback);
+        callback.onStateChange(mState);
+    }
+
+    private void notifyStateMonitors() throws RemoteException {
+        for (int i = 0; i < mStateMonitors.size(); i++) {
+            mStateMonitors.get(i).onStateChange(mState);
+        }
+    }
+
+    private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
+        switch (mState) {
+            case STATE_INVISIBLE:
+                mState = STATE_VISIBLE;
+                notifyStateMonitors();
+                callback.onSuccess();
+                break;
+            case STATE_VISIBLE:
+                callback.onSuccess();
+                break;
+            default:
+                callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+        }
+    }
+
+    private void makeInvisible(IForensicServiceCommandCallback callback) throws RemoteException {
+        switch (mState) {
+            case STATE_VISIBLE:
+            case STATE_ENABLED:
+                mState = STATE_INVISIBLE;
+                notifyStateMonitors();
+                callback.onSuccess();
+                break;
+            case STATE_INVISIBLE:
+                callback.onSuccess();
+                break;
+            default:
+                callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+        }
+    }
+
+    private void enable(IForensicServiceCommandCallback callback) throws RemoteException {
+        switch (mState) {
+            case STATE_VISIBLE:
+                mState = STATE_ENABLED;
+                notifyStateMonitors();
+                callback.onSuccess();
+                break;
+            case STATE_ENABLED:
+                callback.onSuccess();
+                break;
+            default:
+                callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+        }
+    }
+
+    private void disable(IForensicServiceCommandCallback callback) throws RemoteException {
+        switch (mState) {
+            case STATE_ENABLED:
+                mState = STATE_VISIBLE;
+                notifyStateMonitors();
+                callback.onSuccess();
+                break;
+            case STATE_VISIBLE:
+                callback.onSuccess();
+                break;
+            default:
+                callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+        }
+    }
+
+    @Override
+    public void onStart() {
+        try {
+            publishBinderService(Context.FORENSIC_SERVICE, mBinderService);
+        } catch (Throwable t) {
+            Slog.e(TAG, "Could not start the ForensicService.", t);
+        }
+    }
+
+    @VisibleForTesting
+    IForensicService getBinderService() {
+        return mBinderService;
+    }
+
+    interface Injector {
+        Context getContext();
+
+        Looper getLooper();
+    }
+
+    private static final class InjectorImpl implements Injector {
+        private final Context mContext;
+
+        InjectorImpl(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+
+        @Override
+        public Looper getLooper() {
+            ServiceThread serviceThread =
+                    new ServiceThread(
+                            TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
+            serviceThread.start();
+            return serviceThread.getLooper();
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java
index 0d420a5..4c9cbc4 100644
--- a/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java
+++ b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java
@@ -42,32 +42,55 @@
             return;
         }
         StatsEvent.Builder builder = StatsEvent.newBuilder().setAtomId(atom.atomId);
-        for (StatsBootstrapAtomValue value : atom.values) {
+        for (StatsBootstrapAtomValue atomValue : atom.values) {
+            StatsBootstrapAtomValue.Primitive value = atomValue.value;
             switch (value.getTag()) {
-                case StatsBootstrapAtomValue.boolValue:
+                case StatsBootstrapAtomValue.Primitive.boolValue:
                     builder.writeBoolean(value.getBoolValue());
                     break;
-                case StatsBootstrapAtomValue.intValue:
+                case StatsBootstrapAtomValue.Primitive.intValue:
                     builder.writeInt(value.getIntValue());
                     break;
-                case StatsBootstrapAtomValue.longValue:
+                case StatsBootstrapAtomValue.Primitive.longValue:
                     builder.writeLong(value.getLongValue());
                     break;
-                case StatsBootstrapAtomValue.floatValue:
+                case StatsBootstrapAtomValue.Primitive.floatValue:
                     builder.writeFloat(value.getFloatValue());
                     break;
-                case StatsBootstrapAtomValue.stringValue:
+                case StatsBootstrapAtomValue.Primitive.stringValue:
                     builder.writeString(value.getStringValue());
                     break;
-                case StatsBootstrapAtomValue.bytesValue:
+                case StatsBootstrapAtomValue.Primitive.bytesValue:
                     builder.writeByteArray(value.getBytesValue());
                     break;
+                case StatsBootstrapAtomValue.Primitive.stringArrayValue:
+                    builder.writeStringArray(value.getStringArrayValue());
+                    break;
                 default:
                     Slog.e(TAG, "Unexpected value type " + value.getTag()
                             + " when logging atom " + atom.atomId);
                     return;
 
             }
+            StatsBootstrapAtomValue.Annotation[] annotations = atomValue.annotations;
+            for (StatsBootstrapAtomValue.Annotation annotation : atomValue.annotations) {
+                if (annotation.id != StatsBootstrapAtomValue.Annotation.Id.IS_UID) {
+                    Slog.e(TAG, "Unexpected annotation ID: " + annotation.id
+                            + ", for atom " + atom.atomId + ": only UIDs are supported!");
+                    return;
+                }
+
+                switch (annotation.value.getTag()) {
+                    case StatsBootstrapAtomValue.Annotation.Primitive.boolValue:
+                        builder.addBooleanAnnotation(
+                                annotation.id, annotation.value.getBoolValue());
+                        break;
+                    default:
+                        Slog.e(TAG, "Unexpected value type " + annotation.value.getTag()
+                                + " when logging UID for atom " + atom.atomId);
+                        return;
+                }
+            }
         }
         StatsLog.write(builder.usePooledBuffer().build());
     }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 9662a87..e7735d8 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1275,49 +1275,39 @@
             case FrameworkStatsLog.WIFI_BYTES_TRANSFER: {
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTransportLocked(
                         TRANSPORT_WIFI);
-                if (stats != null) {
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
-                            new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/false));
-                }
+                ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
+                        new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/false));
                 break;
             }
             case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTransportLocked(
                         TRANSPORT_WIFI);
-                if (stats != null) {
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
-                            new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/true));
-                }
+                ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
+                        new int[]{TRANSPORT_WIFI}, /*slicedByFgbg=*/true));
                 break;
             }
             case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: {
                 final NetworkStats stats =
                         getUidNetworkStatsSnapshotForTransportLocked(TRANSPORT_CELLULAR);
-                if (stats != null) {
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
-                            new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
-                }
+                ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats),
+                        new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
                 break;
             }
             case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
                 final NetworkStats stats =
                         getUidNetworkStatsSnapshotForTransportLocked(TRANSPORT_CELLULAR);
-                if (stats != null) {
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
-                            new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true));
-                }
+                ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
+                        new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true));
                 break;
             }
             case FrameworkStatsLog.PROXY_BYTES_TRANSFER_BY_FG_BG: {
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTemplateLocked(
                         new NetworkTemplate.Builder(MATCH_PROXY).build(),  /*includeTags=*/false);
-                if (stats != null) {
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
-                            new int[]{TRANSPORT_BLUETOOTH},
-                            /*slicedByFgbg=*/true, /*slicedByTag=*/false,
-                            /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                            /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/true));
-                }
+                ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
+                        new int[]{TRANSPORT_BLUETOOTH},
+                        /*slicedByFgbg=*/true, /*slicedByTag=*/false,
+                        /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                        /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/true));
                 break;
             }
             case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
@@ -1326,14 +1316,12 @@
                 final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplateLocked(
                         new NetworkTemplate.Builder(MATCH_MOBILE)
                                 .setMeteredness(METERED_YES).build(), /*includeTags=*/true);
-                if (wifiStats != null && cellularStats != null) {
-                    final NetworkStats stats = wifiStats.add(cellularStats);
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
-                            new int[]{TRANSPORT_WIFI, TRANSPORT_CELLULAR},
-                            /*slicedByFgbg=*/false, /*slicedByTag=*/true,
-                            /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                            /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/false));
-                }
+                final NetworkStats stats = wifiStats.add(cellularStats);
+                ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
+                        new int[]{TRANSPORT_WIFI, TRANSPORT_CELLULAR},
+                        /*slicedByFgbg=*/false, /*slicedByTag=*/true,
+                        /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                        /*subInfo=*/null, OEM_MANAGED_ALL, /*isTypeProxy=*/false));
                 break;
             }
             case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: {
@@ -1519,12 +1507,10 @@
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTemplateLocked(
                         template, false);
                 final Integer transport = ruleAndTransport.second;
-                if (stats != null) {
-                    ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
-                            new int[]{transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false,
-                            /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                            /*subInfo=*/null, oemManaged, /*isTypeProxy=*/false));
-                }
+                ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
+                        new int[]{transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false,
+                        /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                        /*subInfo=*/null, oemManaged, /*isTypeProxy=*/false));
             }
         }
 
@@ -1535,7 +1521,7 @@
      * Create a snapshot of NetworkStats for a given transport.
      */
     @GuardedBy("mDataBytesTransferLock")
-    @Nullable
+    @NonNull
     private NetworkStats getUidNetworkStatsSnapshotForTransportLocked(int transport) {
         NetworkTemplate template = null;
         switch (transport) {
@@ -1574,7 +1560,7 @@
      * some traffic before boot.
      */
     @GuardedBy("mDataBytesTransferLock")
-    @Nullable
+    @NonNull
     private NetworkStats getUidNetworkStatsSnapshotForTemplateLocked(
             @NonNull NetworkTemplate template, boolean includeTags) {
         final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
@@ -1613,7 +1599,7 @@
     }
 
     @GuardedBy("mDataBytesTransferLock")
-    @Nullable
+    @NonNull
     private NetworkStats getUidNetworkStatsSnapshotForTemplateLocked(
             @NonNull NetworkTemplate template, boolean includeTags, long startTime, long endTime) {
         final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
@@ -1660,12 +1646,10 @@
                             .setMeteredness(METERED_YES).build();
             final NetworkStats stats =
                     getUidNetworkStatsSnapshotForTemplateLocked(template, /*includeTags=*/false);
-            if (stats != null) {
-                ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
-                        new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
-                        /*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo,
-                        OEM_MANAGED_ALL, /*isTypeProxy=*/false));
-            }
+            ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
+                    new int[]{TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
+                    /*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo,
+                    OEM_MANAGED_ALL, /*isTypeProxy=*/false));
         }
         return ret;
     }
diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
index cc63968..e798bc4 100644
--- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
+++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsAccumulator.java
@@ -17,7 +17,6 @@
 package com.android.server.stats.pull.netstats;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 
@@ -55,7 +54,7 @@
      * This method method may call {@code queryFunction} more than once, which includes maintaining
      * an internal cumulative stats snapshot and querying stats after the snapshot.
      */
-    @Nullable
+    @NonNull
     public NetworkStats queryStats(long currentTimeMillis,
             @NonNull StatsQueryFunction queryFunction) {
         maybeExpandSnapshot(currentTimeMillis, queryFunction);
@@ -80,23 +79,21 @@
         if (newEndTimeMillis - mSnapshotEndTimeMillis > mBucketDurationMillis) {
             NetworkStats extraStats = queryFunction.queryNetworkStats(mTemplate, mWithTags,
                     mSnapshotEndTimeMillis, newEndTimeMillis);
-            if (extraStats != null) {
-                mSnapshot = mSnapshot.add(extraStats);
-                mSnapshotEndTimeMillis = newEndTimeMillis;
-            }
+            mSnapshot = mSnapshot.add(extraStats);
+            mSnapshotEndTimeMillis = newEndTimeMillis;
         }
     }
 
     /**
      * Adds up stats in the internal cumulative snapshot and the stats that follow after it.
      */
-    @Nullable
+    @NonNull
     private NetworkStats snapshotPlusFollowingStats(long currentTimeMillis,
             @NonNull StatsQueryFunction queryFunction) {
         // Set end time in the future to include all stats in the active bucket.
         NetworkStats extraStats = queryFunction.queryNetworkStats(mTemplate, mWithTags,
                 mSnapshotEndTimeMillis, currentTimeMillis + mBucketDurationMillis);
-        return extraStats != null ? mSnapshot.add(extraStats) : null;
+        return mSnapshot.add(extraStats);
     }
 
     @FunctionalInterface
@@ -104,7 +101,7 @@
         /**
          * Returns network stats during the given time period.
          */
-        @Nullable
+        @NonNull
         NetworkStats queryNetworkStats(@NonNull NetworkTemplate template, boolean includeTags,
                 long startTime, long endTime);
     }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 91a17a9..4589d26 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -381,10 +381,12 @@
                     // service to populate the hardware list.
                     serviceState = new ServiceState(component, userId);
                     userState.serviceStateMap.put(component, serviceState);
-                    updateServiceConnectionLocked(component, userId);
                 } else {
                     inputList.addAll(serviceState.hardwareInputMap.values());
                 }
+                if (serviceState.needInit) {
+                    updateServiceConnectionLocked(component, userId);
+                }
             } else {
                 try {
                     TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
@@ -489,6 +491,27 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void cleanUpHdmiDevices(int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "cleanUpHdmiDevices: user " + userId);
+        }
+        UserState userState = getOrCreateUserStateLocked(userId);
+        for (ServiceState serviceState : userState.serviceStateMap.values()) {
+            for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
+                try {
+                    if (serviceState.service != null) {
+                        serviceState.service.notifyHdmiDeviceRemoved(device);
+                    } else {
+                        serviceState.hdmiDeviceRemovedBuffer.add(device);
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
+                }
+            }
+        }
+    }
+
     private void startUser(int userId) {
         synchronized (mLock) {
             if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
@@ -500,9 +523,13 @@
             if (userInfo.isProfile()
                     && parentInfo != null
                     && parentInfo.id == mCurrentUserId) {
-                // only the children of the current user can be started in background
+                int prevUserId = mCurrentUserId;
                 mCurrentUserId = userId;
-                startProfileLocked(userId);
+                // only the children of the current user can be started in background
+                releaseSessionOfUserLocked(prevUserId);
+                cleanUpHdmiDevices(prevUserId);
+                unbindServiceOfUserLocked(prevUserId);
+                startProfileLocked(mCurrentUserId);
             }
         }
     }
@@ -515,6 +542,7 @@
             }
 
             releaseSessionOfUserLocked(userId);
+            cleanUpHdmiDevices(userId);
             unbindServiceOfUserLocked(userId);
             mRunningProfiles.remove(userId);
         }
@@ -543,15 +571,19 @@
                 unbindServiceOfUserLocked(runningId);
             }
             mRunningProfiles.clear();
-            releaseSessionOfUserLocked(mCurrentUserId);
-            unbindServiceOfUserLocked(mCurrentUserId);
 
+            int prevUserId = mCurrentUserId;
             mCurrentUserId = userId;
-            buildTvInputListLocked(userId, null);
-            buildTvContentRatingSystemListLocked(userId);
+
+            releaseSessionOfUserLocked(prevUserId);
+            cleanUpHdmiDevices(prevUserId);
+            unbindServiceOfUserLocked(prevUserId);
+
+            buildTvInputListLocked(mCurrentUserId, null);
+            buildTvContentRatingSystemListLocked(mCurrentUserId);
             mMessageHandler
                     .obtainMessage(MessageHandler.MSG_SWITCH_CONTENT_RESOLVER,
-                            getContentResolverForUser(userId))
+                            getContentResolverForUser(mCurrentUserId))
                     .sendToTarget();
         }
     }
@@ -590,6 +622,9 @@
 
     @GuardedBy("mLock")
     private void unbindServiceOfUserLocked(int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "unbindServiceOfUserLocked: user " + userId);
+        }
         UserState userState = getUserStateLocked(userId);
         if (userState == null) {
             return;
@@ -600,7 +635,12 @@
             ServiceState serviceState = userState.serviceStateMap.get(component);
             if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
                 unbindService(serviceState);
-                it.remove();
+                if (!serviceState.isHardware) {
+                    it.remove();
+                } else {
+                    serviceState.hardwareInputMap.clear();
+                    serviceState.needInit = true;
+                }
             }
         }
     }
@@ -774,7 +814,7 @@
         boolean shouldBind;
         if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
             shouldBind = !serviceState.sessionTokens.isEmpty()
-                    || (serviceState.isHardware && serviceState.neverConnected);
+                    || (serviceState.isHardware && serviceState.needInit);
         } else {
             // For a non-current user,
             // if sessionTokens is not empty, it contains recording sessions only
@@ -3404,13 +3444,13 @@
         private ServiceCallback callback;
         private boolean bound;
         private boolean reconnecting;
-        private boolean neverConnected;
+        private boolean needInit;
 
         private ServiceState(ComponentName component, int userId) {
             this.component = component;
             this.connection = new InputServiceConnection(component, userId);
             this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
-            this.neverConnected = true;
+            this.needInit = true;
         }
     }
 
@@ -3618,11 +3658,9 @@
         }
         ComponentName component = mTvInputHardwareManager.getInputMap().get(inputId).getComponent();
         ServiceState serviceState = getServiceStateLocked(component, userId);
-        boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
-        if (removed) {
-            buildTvInputListLocked(userId, null);
-            mTvInputHardwareManager.removeHardwareInput(inputId);
-        }
+        serviceState.hardwareInputMap.remove(inputId);
+        buildTvInputListLocked(userId, null);
+        mTvInputHardwareManager.removeHardwareInput(inputId);
     }
 
     private final class InputServiceConnection implements ServiceConnection {
@@ -3648,7 +3686,7 @@
                 }
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
                 serviceState.service = ITvInputService.Stub.asInterface(service);
-                serviceState.neverConnected = false;
+                serviceState.needInit = false;
 
                 // Register a callback, if we need to.
                 if (serviceState.isHardware && serviceState.callback == null) {
@@ -3841,9 +3879,12 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    Slog.d(TAG, "ServiceCallback: removeHardwareInput, inputId: " + inputId +
-                                " by " + mComponent + ", userId: " + mUserId);
-                    removeHardwareInputLocked(inputId, mUserId);
+                    if (mUserId == mCurrentUserId) {
+                        Slog.d(TAG,
+                                "ServiceCallback: removeHardwareInput, inputId: " + inputId + " by "
+                                        + mComponent + ", userId: " + mUserId);
+                        removeHardwareInputLocked(inputId, mUserId);
+                    }
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -4578,6 +4619,11 @@
     private final class HardwareListener implements TvInputHardwareManager.Listener {
         @Override
         public void onStateChanged(String inputId, int state) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onStateChanged: inputId " + (inputId != null ? inputId : "null")
+                                + ", state " + state);
+            }
             synchronized (mLock) {
                 setStateLocked(inputId, state, mCurrentUserId);
             }
@@ -4585,6 +4631,11 @@
 
         @Override
         public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHardwareDeviceAdded: TvInputHardwareInfo "
+                                + (info != null ? info.toString() : "null"));
+            }
             synchronized (mLock) {
                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
@@ -4607,6 +4658,11 @@
 
         @Override
         public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHardwareDeviceRemoved: TvInputHardwareInfo "
+                                + (info != null ? info.toString() : "null"));
+            }
             synchronized (mLock) {
                 String relatedInputId =
                         mTvInputHardwareManager.getHardwareInputIdMap().get(info.getDeviceId());
@@ -4634,6 +4690,11 @@
 
         @Override
         public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHdmiDeviceAdded: HdmiDeviceInfo "
+                                + (deviceInfo != null ? deviceInfo.toString() : "null"));
+            }
             synchronized (mLock) {
                 UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
@@ -4656,6 +4717,11 @@
 
         @Override
         public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHdmiDeviceRemoved: HdmiDeviceInfo "
+                                + (deviceInfo != null ? deviceInfo.toString() : "null"));
+            }
             synchronized (mLock) {
                 String relatedInputId =
                         mTvInputHardwareManager.getHdmiInputIdMap().get(deviceInfo.getId());
@@ -4683,6 +4749,12 @@
 
         @Override
         public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onHdmiDeviceUpdated: inputId " + (inputId != null ? inputId : "null")
+                                + ", deviceInfo: "
+                                + (deviceInfo != null ? deviceInfo.toString() : "null"));
+            }
             synchronized (mLock) {
                 Integer state;
                 switch (deviceInfo.getDevicePowerStatus()) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
index eb5361c..d161070 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
@@ -30,7 +30,7 @@
      * Handle of the current resource. Should not be changed and should be aligned with the driver
      * level implementation.
      */
-    final int mHandle;
+    final long mHandle;
 
     private final int mSystemId;
 
@@ -50,7 +50,7 @@
         this.mAvailableSessionNum = builder.mMaxSessionNum;
     }
 
-    public int getHandle() {
+    public long getHandle() {
         return mHandle;
     }
 
@@ -146,11 +146,11 @@
      */
     public static class Builder {
 
-        private final int mHandle;
+        private final long mHandle;
         private int mSystemId;
         protected int mMaxSessionNum;
 
-        Builder(int handle, int systemId) {
+        Builder(long handle, int systemId) {
             this.mHandle = handle;
             this.mSystemId = systemId;
         }
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
index 5cef729..b325570 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
@@ -42,7 +42,7 @@
      * Builder class for {@link CiCamResource}.
      */
     public static class Builder extends CasResource.Builder {
-        Builder(int handle, int systemId) {
+        Builder(long handle, int systemId) {
             super(handle, systemId);
         }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 8e37527..0962319 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -67,19 +67,19 @@
     /**
      * The handle of the primary frontend resource
      */
-    private int mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+    private long mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
 
     /**
      * List of the frontend handles that are used by the current client.
      */
-    private Set<Integer> mUsingFrontendHandles = new HashSet<>();
+    private Set<Long> mUsingFrontendHandles = new HashSet<>();
 
     /**
      * List of the client ids that share frontend with the current client.
      */
     private Set<Integer> mShareFeClientIds = new HashSet<>();
 
-    private Set<Integer> mUsingDemuxHandles = new HashSet<>();
+    private Set<Long> mUsingDemuxHandles = new HashSet<>();
 
     /**
      * Client id sharee that has shared frontend with the current client.
@@ -89,7 +89,7 @@
     /**
      * List of the Lnb handles that are used by the current client.
      */
-    private Set<Integer> mUsingLnbHandles = new HashSet<>();
+    private Set<Long> mUsingLnbHandles = new HashSet<>();
 
     /**
      * List of the Cas system ids that are used by the current client.
@@ -184,7 +184,7 @@
      *
      * @param frontendHandle being used.
      */
-    public void useFrontend(int frontendHandle) {
+    public void useFrontend(long frontendHandle) {
         mUsingFrontendHandles.add(frontendHandle);
     }
 
@@ -193,14 +193,14 @@
      *
      * @param frontendHandle being used.
      */
-    public void setPrimaryFrontend(int frontendHandle) {
+    public void setPrimaryFrontend(long frontendHandle) {
         mPrimaryUsingFrontendHandle = frontendHandle;
     }
 
     /**
      * Get the primary frontend used by the client
      */
-    public int getPrimaryFrontend() {
+    public long getPrimaryFrontend() {
         return mPrimaryUsingFrontendHandle;
     }
 
@@ -222,7 +222,7 @@
         mShareFeClientIds.remove(clientId);
     }
 
-    public Set<Integer> getInUseFrontendHandles() {
+    public Set<Long> getInUseFrontendHandles() {
         return mUsingFrontendHandles;
     }
 
@@ -253,14 +253,14 @@
      *
      * @param demuxHandle the demux being used.
      */
-    public void useDemux(int demuxHandle) {
+    public void useDemux(long demuxHandle) {
         mUsingDemuxHandles.add(demuxHandle);
     }
 
     /**
      * Get the set of demux handles in use.
      */
-    public Set<Integer> getInUseDemuxHandles() {
+    public Set<Long> getInUseDemuxHandles() {
         return mUsingDemuxHandles;
     }
 
@@ -269,7 +269,7 @@
      *
      * @param demuxHandle the demux handl being released.
      */
-    public void releaseDemux(int demuxHandle) {
+    public void releaseDemux(long demuxHandle) {
         mUsingDemuxHandles.remove(demuxHandle);
     }
 
@@ -278,11 +278,11 @@
      *
      * @param lnbHandle being used.
      */
-    public void useLnb(int lnbHandle) {
+    public void useLnb(long lnbHandle) {
         mUsingLnbHandles.add(lnbHandle);
     }
 
-    public Set<Integer> getInUseLnbHandles() {
+    public Set<Long> getInUseLnbHandles() {
         return mUsingLnbHandles;
     }
 
@@ -291,7 +291,7 @@
      *
      * @param lnbHandle being released.
      */
-    public void releaseLnb(int lnbHandle) {
+    public void releaseLnb(long lnbHandle) {
         mUsingLnbHandles.remove(lnbHandle);
     }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
index df73565..14bc216 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
@@ -69,7 +69,7 @@
     public static class Builder extends TunerResourceBasic.Builder {
         private int mFilterTypes;
 
-        Builder(int handle) {
+        Builder(long handle) {
             super(handle);
         }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
index 7ef75e3..953d974 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
@@ -42,7 +42,7 @@
     /**
      * An array to save all the FE handles under the same exclisive group.
      */
-    private Set<Integer> mExclusiveGroupMemberHandles = new HashSet<>();
+    private Set<Long> mExclusiveGroupMemberHandles = new HashSet<>();
 
     private FrontendResource(Builder builder) {
         super(builder);
@@ -58,7 +58,7 @@
         return mExclusiveGroupId;
     }
 
-    public Set<Integer> getExclusiveGroupMemberFeHandles() {
+    public Set<Long> getExclusiveGroupMemberFeHandles() {
         return mExclusiveGroupMemberHandles;
     }
 
@@ -67,7 +67,7 @@
      *
      * @param handle the handle to be added.
      */
-    public void addExclusiveGroupMemberFeHandle(int handle) {
+    public void addExclusiveGroupMemberFeHandle(long handle) {
         mExclusiveGroupMemberHandles.add(handle);
     }
 
@@ -76,7 +76,7 @@
      *
      * @param handles the handle collection to be added.
      */
-    public void addExclusiveGroupMemberFeHandles(Collection<Integer> handles) {
+    public void addExclusiveGroupMemberFeHandles(Collection<Long> handles) {
         mExclusiveGroupMemberHandles.addAll(handles);
     }
 
@@ -85,7 +85,7 @@
      *
      * @param id the id to be removed.
      */
-    public void removeExclusiveGroupMemberFeId(int handle) {
+    public void removeExclusiveGroupMemberFeId(long handle) {
         mExclusiveGroupMemberHandles.remove(handle);
     }
 
@@ -104,7 +104,7 @@
         @Type private int mType;
         private int mExclusiveGroupId;
 
-        Builder(int handle) {
+        Builder(long handle) {
             super(handle);
         }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
index 41cacea..ab28371 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
@@ -37,8 +37,7 @@
      * Builder class for {@link LnbResource}.
      */
     public static class Builder extends TunerResourceBasic.Builder {
-
-        Builder(int handle) {
+        Builder(long handle) {
             super(handle);
         }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
index 07853fc..d2ff8fa 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
@@ -28,7 +28,7 @@
      * Handle of the current resource. Should not be changed and should be aligned with the driver
      * level implementation.
      */
-    final int mHandle;
+    final long mHandle;
 
     /**
      * If the current resource is in use.
@@ -44,7 +44,7 @@
         this.mHandle = builder.mHandle;
     }
 
-    public int getHandle() {
+    public long getHandle() {
         return mHandle;
     }
 
@@ -78,9 +78,9 @@
      * Builder class for {@link TunerResourceBasic}.
      */
     public static class Builder {
-        private final int mHandle;
+        private final long mHandle;
 
-        Builder(int handle) {
+        Builder(long handle) {
             this.mHandle = handle;
         }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 9229f7f..c5b6bbf 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -78,12 +78,18 @@
 
     private static final int INVALID_FE_COUNT = -1;
 
+    private static final int RESOURCE_ID_SHIFT = 24;
+    private static final int RESOURCE_TYPE_SHIFT = 56;
+    private static final long RESOURCE_COUNT_MASK = 0xffffff;
+    private static final long RESOURCE_ID_MASK = 0xffffffff;
+    private static final long RESOURCE_TYPE_MASK = 0xff;
+
     // Map of the registered client profiles
     private Map<Integer, ClientProfile> mClientProfiles = new HashMap<>();
     private int mNextUnusedClientId = 0;
 
     // Map of the current available frontend resources
-    private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>();
+    private Map<Long, FrontendResource> mFrontendResources = new HashMap<>();
     // SparseIntArray of the max usable number for each frontend resource type
     private SparseIntArray mFrontendMaxUsableNums = new SparseIntArray();
     // SparseIntArray of the currently used number for each frontend resource type
@@ -93,15 +99,15 @@
 
     // Backups for the frontend resource maps for enabling testing with custom resource maps
     // such as TunerTest.testHasUnusedFrontend1()
-    private Map<Integer, FrontendResource> mFrontendResourcesBackup = new HashMap<>();
+    private Map<Long, FrontendResource> mFrontendResourcesBackup = new HashMap<>();
     private SparseIntArray mFrontendMaxUsableNumsBackup = new SparseIntArray();
     private SparseIntArray mFrontendUsedNumsBackup = new SparseIntArray();
     private SparseIntArray mFrontendExistingNumsBackup = new SparseIntArray();
 
     // Map of the current available demux resources
-    private Map<Integer, DemuxResource> mDemuxResources = new HashMap<>();
+    private Map<Long, DemuxResource> mDemuxResources = new HashMap<>();
     // Map of the current available lnb resources
-    private Map<Integer, LnbResource> mLnbResources = new HashMap<>();
+    private Map<Long, LnbResource> mLnbResources = new HashMap<>();
     // Map of the current available Cas resources
     private Map<Integer, CasResource> mCasResources = new HashMap<>();
     // Map of the current available CiCam resources
@@ -266,7 +272,7 @@
         }
 
         @Override
-        public void setLnbInfoList(int[] lnbHandles) throws RemoteException {
+        public void setLnbInfoList(long[] lnbHandles) throws RemoteException {
             enforceTrmAccessPermission("setLnbInfoList");
             if (lnbHandles == null) {
                 throw new RemoteException("Lnb handle list can't be null");
@@ -277,8 +283,8 @@
         }
 
         @Override
-        public boolean requestFrontend(@NonNull TunerFrontendRequest request,
-                @NonNull int[] frontendHandle) {
+        public boolean requestFrontend(
+                @NonNull TunerFrontendRequest request, @NonNull long[] frontendHandle) {
             enforceTunerAccessPermission("requestFrontend");
             enforceTrmAccessPermission("requestFrontend");
             if (frontendHandle == null) {
@@ -350,8 +356,8 @@
         }
 
         @Override
-        public boolean requestDemux(@NonNull TunerDemuxRequest request,
-                    @NonNull int[] demuxHandle) throws RemoteException {
+        public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull long[] demuxHandle)
+                throws RemoteException {
             enforceTunerAccessPermission("requestDemux");
             enforceTrmAccessPermission("requestDemux");
             if (demuxHandle == null) {
@@ -362,7 +368,7 @@
 
         @Override
         public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
-                    @NonNull int[] descramblerHandle) throws RemoteException {
+                @NonNull long[] descramblerHandle) throws RemoteException {
             enforceDescramblerAccessPermission("requestDescrambler");
             enforceTrmAccessPermission("requestDescrambler");
             if (descramblerHandle == null) {
@@ -379,7 +385,7 @@
 
         @Override
         public boolean requestCasSession(@NonNull CasSessionRequest request,
-                @NonNull int[] casSessionHandle) throws RemoteException {
+                @NonNull long[] casSessionHandle) throws RemoteException {
             enforceTrmAccessPermission("requestCasSession");
             if (casSessionHandle == null) {
                 throw new RemoteException("casSessionHandle can't be null");
@@ -388,8 +394,8 @@
         }
 
         @Override
-        public boolean requestCiCam(@NonNull TunerCiCamRequest request,
-                @NonNull int[] ciCamHandle) throws RemoteException {
+        public boolean requestCiCam(@NonNull TunerCiCamRequest request, @NonNull long[] ciCamHandle)
+                throws RemoteException {
             enforceTrmAccessPermission("requestCiCam");
             if (ciCamHandle == null) {
                 throw new RemoteException("ciCamHandle can't be null");
@@ -398,7 +404,7 @@
         }
 
         @Override
-        public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle)
+        public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull long[] lnbHandle)
                 throws RemoteException {
             enforceTunerAccessPermission("requestLnb");
             enforceTrmAccessPermission("requestLnb");
@@ -409,14 +415,14 @@
         }
 
         @Override
-        public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException {
+        public void releaseFrontend(long frontendHandle, int clientId) throws RemoteException {
             enforceTunerAccessPermission("releaseFrontend");
             enforceTrmAccessPermission("releaseFrontend");
             releaseFrontendInternal(frontendHandle, clientId);
         }
 
         @Override
-        public void releaseDemux(int demuxHandle, int clientId) throws RemoteException {
+        public void releaseDemux(long demuxHandle, int clientId) throws RemoteException {
             enforceTunerAccessPermission("releaseDemux");
             enforceTrmAccessPermission("releaseDemux");
             if (DEBUG) {
@@ -447,7 +453,7 @@
         }
 
         @Override
-        public void releaseDescrambler(int descramblerHandle, int clientId) {
+        public void releaseDescrambler(long descramblerHandle, int clientId) {
             enforceTunerAccessPermission("releaseDescrambler");
             enforceTrmAccessPermission("releaseDescrambler");
             if (DEBUG) {
@@ -456,7 +462,7 @@
         }
 
         @Override
-        public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException {
+        public void releaseCasSession(long casSessionHandle, int clientId) throws RemoteException {
             enforceTrmAccessPermission("releaseCasSession");
             if (!validateResourceHandle(
                     TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) {
@@ -480,7 +486,7 @@
         }
 
         @Override
-        public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException {
+        public void releaseCiCam(long ciCamHandle, int clientId) throws RemoteException {
             enforceTrmAccessPermission("releaseCiCam");
             if (!validateResourceHandle(
                     TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) {
@@ -508,7 +514,7 @@
         }
 
         @Override
-        public void releaseLnb(int lnbHandle, int clientId) throws RemoteException {
+        public void releaseLnb(long lnbHandle, int clientId) throws RemoteException {
             enforceTunerAccessPermission("releaseLnb");
             enforceTrmAccessPermission("releaseLnb");
             if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) {
@@ -812,7 +818,7 @@
         // A set to record the frontends pending on updating. Ids will be removed
         // from this set once its updating finished. Any frontend left in this set when all
         // the updates are done will be removed from mFrontendResources.
-        Set<Integer> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet());
+        Set<Long> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet());
 
         // Update frontendResources map and other mappings accordingly
         for (int i = 0; i < infos.length; i++) {
@@ -831,7 +837,7 @@
             }
         }
 
-        for (int removingHandle : updatingFrontendHandles) {
+        for (long removingHandle : updatingFrontendHandles) {
             // update the exclusive group id member list
             removeFrontendResource(removingHandle);
         }
@@ -849,7 +855,7 @@
         // A set to record the demuxes pending on updating. Ids will be removed
         // from this set once its updating finished. Any demux left in this set when all
         // the updates are done will be removed from mDemuxResources.
-        Set<Integer> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet());
+        Set<Long> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet());
 
         // Update demuxResources map and other mappings accordingly
         for (int i = 0; i < infos.length; i++) {
@@ -867,13 +873,13 @@
             }
         }
 
-        for (int removingHandle : updatingDemuxHandles) {
+        for (long removingHandle : updatingDemuxHandles) {
             // update the exclusive group id member list
             removeDemuxResource(removingHandle);
         }
     }
     @VisibleForTesting
-    protected void setLnbInfoListInternal(int[] lnbHandles) {
+    protected void setLnbInfoListInternal(long[] lnbHandles) {
         if (DEBUG) {
             for (int i = 0; i < lnbHandles.length; i++) {
                 Slog.d(TAG, "updateLnbInfo(lnbHanle=" + lnbHandles[i] + ")");
@@ -883,7 +889,7 @@
         // A set to record the Lnbs pending on updating. Handles will be removed
         // from this set once its updating finished. Any lnb left in this set when all
         // the updates are done will be removed from mLnbResources.
-        Set<Integer> updatingLnbHandles = new HashSet<>(getLnbResources().keySet());
+        Set<Long> updatingLnbHandles = new HashSet<>(getLnbResources().keySet());
 
         // Update lnbResources map and other mappings accordingly
         for (int i = 0; i < lnbHandles.length; i++) {
@@ -899,7 +905,7 @@
             }
         }
 
-        for (int removingHandle : updatingLnbHandles) {
+        for (long removingHandle : updatingLnbHandles) {
             removeLnbResource(removingHandle);
         }
     }
@@ -933,12 +939,12 @@
             return;
         }
         // Add the new Cas Resource.
-        int casSessionHandle = generateResourceHandle(
+        long casSessionHandle = generateResourceHandle(
                 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSystemId);
         cas = new CasResource.Builder(casSessionHandle, casSystemId)
                              .maxSessionNum(maxSessionNum)
                              .build();
-        int ciCamHandle = generateResourceHandle(
+        long ciCamHandle = generateResourceHandle(
                 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, casSystemId);
         ciCam = new CiCamResource.Builder(ciCamHandle, casSystemId)
                              .maxSessionNum(maxSessionNum)
@@ -948,7 +954,7 @@
     }
 
     @VisibleForTesting
-    protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) {
+    protected boolean requestFrontendInternal(TunerFrontendRequest request, long[] frontendHandle) {
         if (DEBUG) {
             Slog.d(TAG, "requestFrontend(request=" + request + ")");
         }
@@ -977,7 +983,7 @@
 
     protected boolean claimFrontend(
             TunerFrontendRequest request,
-            int[] frontendHandle,
+            long[] frontendHandle,
             int[] reclaimOwnerId
     ) {
         frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
@@ -1032,7 +1038,7 @@
                             // currently in primary use (and simply blocked due to exclusive group)
                             ClientProfile targetOwnerProfile =
                                     getClientProfile(fr.getOwnerClientId());
-                            int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
+                            long primaryFeId = targetOwnerProfile.getPrimaryFrontend();
                             FrontendResource primaryFe = getFrontendResource(primaryFeId);
                             if (fr.getType() != primaryFe.getType()
                                     && isFrontendMaxNumUseReached(fr.getType())) {
@@ -1081,7 +1087,7 @@
             getClientProfile(shareeFeClientId).stopSharingFrontend(selfClientId);
             getClientProfile(selfClientId).releaseFrontend();
         }
-        for (int feId : getClientProfile(targetClientId).getInUseFrontendHandles()) {
+        for (long feId : getClientProfile(targetClientId).getInUseFrontendHandles()) {
             getClientProfile(selfClientId).useFrontend(feId);
         }
         getClientProfile(selfClientId).setShareeFeClientId(targetClientId);
@@ -1096,14 +1102,14 @@
         currentOwnerProfile.stopSharingFrontend(newOwnerId);
         newOwnerProfile.setShareeFeClientId(ClientProfile.INVALID_RESOURCE_ID);
         currentOwnerProfile.setShareeFeClientId(newOwnerId);
-        for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) {
+        for (long inUseHandle : newOwnerProfile.getInUseFrontendHandles()) {
             getFrontendResource(inUseHandle).setOwner(newOwnerId);
         }
         // change the primary frontend
         newOwnerProfile.setPrimaryFrontend(currentOwnerProfile.getPrimaryFrontend());
         currentOwnerProfile.setPrimaryFrontend(TunerResourceManager.INVALID_RESOURCE_HANDLE);
         // double check there is no other resources tied to the previous owner
-        for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) {
+        for (long inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) {
             int ownerId = getFrontendResource(inUseHandle).getOwnerClientId();
             if (ownerId != newOwnerId) {
                 Slog.e(TAG, "something is wrong in transferFeOwner:" + inUseHandle
@@ -1135,8 +1141,8 @@
         ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
         ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
 
-        Set<Integer> inUseLnbHandles = new HashSet<>();
-        for (Integer lnbHandle : currentOwnerProfile.getInUseLnbHandles()) {
+        Set<Long> inUseLnbHandles = new HashSet<>();
+        for (Long lnbHandle : currentOwnerProfile.getInUseLnbHandles()) {
             // link lnb handle to the new profile
             newOwnerProfile.useLnb(lnbHandle);
 
@@ -1148,7 +1154,7 @@
         }
 
         // unlink lnb handles from the original owner
-        for (Integer lnbHandle : inUseLnbHandles) {
+        for (Long lnbHandle : inUseLnbHandles) {
             currentOwnerProfile.releaseLnb(lnbHandle);
         }
 
@@ -1171,7 +1177,7 @@
     }
 
     @VisibleForTesting
-    protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)
+    protected boolean requestLnbInternal(TunerLnbRequest request, long[] lnbHandle)
             throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestLnb(request=" + request + ")");
@@ -1199,7 +1205,7 @@
         return true;
     }
 
-    protected boolean claimLnb(TunerLnbRequest request, int[] lnbHandle, int[] reclaimOwnerId)
+    protected boolean claimLnb(TunerLnbRequest request, long[] lnbHandle, int[] reclaimOwnerId)
             throws RemoteException {
         lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         reclaimOwnerId[0] = INVALID_CLIENT_ID;
@@ -1256,7 +1262,7 @@
     }
 
     @VisibleForTesting
-    protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle)
+    protected boolean requestCasSessionInternal(CasSessionRequest request, long[] casSessionHandle)
             throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestCasSession(request=" + request + ")");
@@ -1284,7 +1290,7 @@
         return true;
     }
 
-    protected boolean claimCasSession(CasSessionRequest request, int[] casSessionHandle,
+    protected boolean claimCasSession(CasSessionRequest request, long[] casSessionHandle,
             int[] reclaimOwnerId) throws RemoteException {
         casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         reclaimOwnerId[0] = INVALID_CLIENT_ID;
@@ -1296,7 +1302,7 @@
             CasResource cas = getCasResource(request.casSystemId);
             // Unregistered Cas System is treated as having unlimited sessions.
             if (cas == null) {
-                int resourceHandle = generateResourceHandle(
+                long resourceHandle = generateResourceHandle(
                         TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, request.clientId);
                 cas = new CasResource.Builder(resourceHandle, request.casSystemId)
                                     .maxSessionNum(Integer.MAX_VALUE)
@@ -1341,7 +1347,7 @@
     }
 
     @VisibleForTesting
-    protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle)
+    protected boolean requestCiCamInternal(TunerCiCamRequest request, long[] ciCamHandle)
             throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")");
@@ -1369,7 +1375,7 @@
         return true;
     }
 
-    protected boolean claimCiCam(TunerCiCamRequest request, int[] ciCamHandle,
+    protected boolean claimCiCam(TunerCiCamRequest request, long[] ciCamHandle,
             int[] reclaimOwnerId) throws RemoteException {
         ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         reclaimOwnerId[0] = INVALID_CLIENT_ID;
@@ -1381,7 +1387,7 @@
             CiCamResource ciCam = getCiCamResource(request.ciCamId);
             // Unregistered CiCam is treated as having unlimited sessions.
             if (ciCam == null) {
-                int resourceHandle = generateResourceHandle(
+                long resourceHandle = generateResourceHandle(
                         TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, request.ciCamId);
                 ciCam = new CiCamResource.Builder(resourceHandle, request.ciCamId)
                                     .maxSessionNum(Integer.MAX_VALUE)
@@ -1454,7 +1460,7 @@
     }
 
     @VisibleForTesting
-    protected void releaseFrontendInternal(int frontendHandle, int clientId)
+    protected void releaseFrontendInternal(long frontendHandle, int clientId)
             throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "releaseFrontend(id=" + frontendHandle + ", clientId=" + clientId + " )");
@@ -1475,7 +1481,7 @@
         }
     }
 
-    private Set<Integer> unclaimFrontend(int frontendHandle, int clientId) throws RemoteException {
+    private Set<Integer> unclaimFrontend(long frontendHandle, int clientId) throws RemoteException {
         Set<Integer> reclaimedResourceOwnerIds = null;
         synchronized (mLock) {
             if (!checkClientExists(clientId)) {
@@ -1533,7 +1539,7 @@
 
     @VisibleForTesting
     public boolean requestDemuxInternal(@NonNull TunerDemuxRequest request,
-                @NonNull int[] demuxHandle) throws RemoteException {
+                @NonNull long[] demuxHandle) throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestDemux(request=" + request + ")");
         }
@@ -1560,7 +1566,8 @@
         return true;
     }
 
-    protected boolean claimDemux(TunerDemuxRequest request, int[] demuxHandle, int[] reclaimOwnerId)
+    protected boolean claimDemux(
+            TunerDemuxRequest request, long[] demuxHandle, int[] reclaimOwnerId)
             throws RemoteException {
         demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         reclaimOwnerId[0] = INVALID_CLIENT_ID;
@@ -1676,7 +1683,7 @@
 
     @VisibleForTesting
     protected boolean requestDescramblerInternal(
-            TunerDescramblerRequest request, int[] descramblerHandle) {
+            TunerDescramblerRequest request, long[] descramblerHandle) {
         if (DEBUG) {
             Slog.d(TAG, "requestDescrambler(request=" + request + ")");
         }
@@ -2008,20 +2015,20 @@
         return false;
     }
 
-    private void updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+    private void updateFrontendClientMappingOnNewGrant(long grantingHandle, int ownerClientId) {
         FrontendResource grantingFrontend = getFrontendResource(grantingHandle);
         ClientProfile ownerProfile = getClientProfile(ownerClientId);
         grantingFrontend.setOwner(ownerClientId);
         increFrontendNum(mFrontendUsedNums, grantingFrontend.getType());
         ownerProfile.useFrontend(grantingHandle);
-        for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) {
+        for (long exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) {
             getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
             ownerProfile.useFrontend(exclusiveGroupMember);
         }
         ownerProfile.setPrimaryFrontend(grantingHandle);
     }
 
-    private void updateDemuxClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+    private void updateDemuxClientMappingOnNewGrant(long grantingHandle, int ownerClientId) {
         DemuxResource grantingDemux = getDemuxResource(grantingHandle);
         if (grantingDemux != null) {
             ClientProfile ownerProfile = getClientProfile(ownerClientId);
@@ -2036,7 +2043,7 @@
         ownerProfile.releaseDemux(releasingDemux.getHandle());
     }
 
-    private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+    private void updateLnbClientMappingOnNewGrant(long grantingHandle, int ownerClientId) {
         LnbResource grantingLnb = getLnbResource(grantingHandle);
         ClientProfile ownerProfile = getClientProfile(ownerClientId);
         grantingLnb.setOwner(ownerClientId);
@@ -2120,23 +2127,23 @@
 
     @VisibleForTesting
     @Nullable
-    protected FrontendResource getFrontendResource(int frontendHandle) {
+    protected FrontendResource getFrontendResource(long frontendHandle) {
         return mFrontendResources.get(frontendHandle);
     }
 
     @VisibleForTesting
-    protected Map<Integer, FrontendResource> getFrontendResources() {
+    protected Map<Long, FrontendResource> getFrontendResources() {
         return mFrontendResources;
     }
 
     @VisibleForTesting
     @Nullable
-    protected DemuxResource getDemuxResource(int demuxHandle) {
+    protected DemuxResource getDemuxResource(long demuxHandle) {
         return mDemuxResources.get(demuxHandle);
     }
 
     @VisibleForTesting
-    protected Map<Integer, DemuxResource> getDemuxResources() {
+    protected Map<Long, DemuxResource> getDemuxResources() {
         return mDemuxResources;
     }
 
@@ -2195,8 +2202,8 @@
         }
     }
 
-    private void replaceFeResourceMap(Map<Integer, FrontendResource> srcMap, Map<Integer,
-            FrontendResource> dstMap) {
+    private void replaceFeResourceMap(
+            Map<Long, FrontendResource> srcMap, Map<Long, FrontendResource> dstMap) {
         if (dstMap != null) {
             dstMap.clear();
             if (srcMap != null && srcMap.size() > 0) {
@@ -2249,7 +2256,7 @@
             if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) {
                 newFe.addExclusiveGroupMemberFeHandle(fe.getHandle());
                 newFe.addExclusiveGroupMemberFeHandles(fe.getExclusiveGroupMemberFeHandles());
-                for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
+                for (long excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
                     getFrontendResource(excGroupmemberFeHandle)
                             .addExclusiveGroupMemberFeHandle(newFe.getHandle());
                 }
@@ -2267,7 +2274,7 @@
         mDemuxResources.put(newDemux.getHandle(), newDemux);
     }
 
-    private void removeFrontendResource(int removingHandle) {
+    private void removeFrontendResource(long removingHandle) {
         FrontendResource fe = getFrontendResource(removingHandle);
         if (fe == null) {
             return;
@@ -2279,7 +2286,7 @@
             }
             clearFrontendAndClientMapping(ownerClient);
         }
-        for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
+        for (long excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
             getFrontendResource(excGroupmemberFeHandle)
                     .removeExclusiveGroupMemberFeId(fe.getHandle());
         }
@@ -2287,7 +2294,7 @@
         mFrontendResources.remove(removingHandle);
     }
 
-    private void removeDemuxResource(int removingHandle) {
+    private void removeDemuxResource(long removingHandle) {
         DemuxResource demux = getDemuxResource(removingHandle);
         if (demux == null) {
             return;
@@ -2300,12 +2307,12 @@
 
     @VisibleForTesting
     @Nullable
-    protected LnbResource getLnbResource(int lnbHandle) {
+    protected LnbResource getLnbResource(long lnbHandle) {
         return mLnbResources.get(lnbHandle);
     }
 
     @VisibleForTesting
-    protected Map<Integer, LnbResource> getLnbResources() {
+    protected Map<Long, LnbResource> getLnbResources() {
         return mLnbResources;
     }
 
@@ -2314,7 +2321,7 @@
         mLnbResources.put(newLnb.getHandle(), newLnb);
     }
 
-    private void removeLnbResource(int removingHandle) {
+    private void removeLnbResource(long removingHandle) {
         LnbResource lnb = getLnbResource(removingHandle);
         if (lnb == null) {
             return;
@@ -2416,7 +2423,7 @@
         if (profile == null) {
             return;
         }
-        for (Integer feId : profile.getInUseFrontendHandles()) {
+        for (Long feId : profile.getInUseFrontendHandles()) {
             FrontendResource fe = getFrontendResource(feId);
             int ownerClientId = fe.getOwnerClientId();
             if (ownerClientId == profile.getId()) {
@@ -2427,10 +2434,9 @@
             if (ownerClientProfile != null) {
                 ownerClientProfile.stopSharingFrontend(profile.getId());
             }
-
         }
 
-        int primaryFeId = profile.getPrimaryFrontend();
+        long primaryFeId = profile.getPrimaryFrontend();
         if (primaryFeId != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
             FrontendResource primaryFe = getFrontendResource(primaryFeId);
             if (primaryFe != null) {
@@ -2448,7 +2454,7 @@
             return;
         }
         // Clear Lnb
-        for (Integer lnbHandle : profile.getInUseLnbHandles()) {
+        for (Long lnbHandle : profile.getInUseLnbHandles()) {
             getLnbResource(lnbHandle).removeOwner();
         }
         // Clear Cas
@@ -2460,7 +2466,7 @@
             getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId());
         }
         // Clear Demux
-        for (Integer demuxHandle : profile.getInUseDemuxHandles()) {
+        for (Long demuxHandle : profile.getInUseDemuxHandles()) {
             getDemuxResource(demuxHandle).removeOwner();
         }
         // Clear Frontend
@@ -2473,24 +2479,31 @@
         return mClientProfiles.keySet().contains(clientId);
     }
 
-    private int generateResourceHandle(
+    /**
+     *   Generate resource handle for resourceType and resourceId
+     *   Resource Handle Allotment : 64 bits (long)
+     *   8 bits - resourceType
+     *   32 bits - resourceId
+     *   24 bits - resourceRequestCount
+     */
+    private long generateResourceHandle(
             @TunerResourceManager.TunerResourceType int resourceType, int resourceId) {
-        return (resourceType & 0x000000ff) << 24
-                | (resourceId << 16)
-                | (mResourceRequestCount++ & 0xffff);
+        return (resourceType & RESOURCE_TYPE_MASK) << RESOURCE_TYPE_SHIFT
+                | (resourceId & RESOURCE_ID_MASK) << RESOURCE_ID_SHIFT
+                | (mResourceRequestCount++ & RESOURCE_COUNT_MASK);
     }
 
     @VisibleForTesting
-    protected int getResourceIdFromHandle(int resourceHandle) {
+    protected int getResourceIdFromHandle(long resourceHandle) {
         if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
-            return resourceHandle;
+            return (int) resourceHandle;
         }
-        return (resourceHandle & 0x00ff0000) >> 16;
+        return (int) ((resourceHandle >> RESOURCE_ID_SHIFT) & RESOURCE_ID_MASK);
     }
 
-    private boolean validateResourceHandle(int resourceType, int resourceHandle) {
+    private boolean validateResourceHandle(int resourceType, long resourceHandle) {
         if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE
-                || ((resourceHandle & 0xff000000) >> 24) != resourceType) {
+                || ((resourceHandle >> RESOURCE_TYPE_SHIFT) & RESOURCE_TYPE_MASK) != resourceType) {
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/uri/NeededUriGrants.java b/services/core/java/com/android/server/uri/NeededUriGrants.java
index 8c8f553..2fe61e0 100644
--- a/services/core/java/com/android/server/uri/NeededUriGrants.java
+++ b/services/core/java/com/android/server/uri/NeededUriGrants.java
@@ -17,10 +17,13 @@
 package com.android.server.uri;
 
 import android.util.ArraySet;
+import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.server.am.NeededUriGrantsProto;
 
+import java.util.Objects;
+
 /** List of {@link GrantUri} a process needs. */
 public class NeededUriGrants {
     final String targetPkg;
@@ -35,6 +38,20 @@
         this.uris = new ArraySet<>();
     }
 
+    public void merge(NeededUriGrants other) {
+        if (other == null) return;
+        if (!Objects.equals(this.targetPkg, other.targetPkg)
+                || this.targetUid != other.targetUid || this.flags != other.flags) {
+            Slog.wtf("NeededUriGrants",
+                    "The other NeededUriGrants does not share the same targetUid, targetPkg or "
+                            + "flags. It cannot be merged into this NeededUriGrants. This "
+                            + "NeededUriGrants: " + this.toStringWithoutUri()
+                            + ". Other NeededUriGrants: " + other.toStringWithoutUri());
+        } else {
+            this.uris.addAll(other.uris);
+        }
+    }
+
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
@@ -47,4 +64,12 @@
         }
         proto.end(token);
     }
+
+    public String toStringWithoutUri() {
+        return "NeededUriGrants{" +
+                "targetPkg='" + targetPkg + '\'' +
+                ", targetUid=" + targetUid +
+                ", flags=" + flags +
+                '}';
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index 6a4c9c2..a492a72 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -70,10 +70,6 @@
         return mIsInTestMode;
     }
 
-    public boolean isFlagNetworkMetricMonitorEnabled() {
-        return mFeatureFlags.networkMetricMonitor();
-    }
-
     public boolean isFlagIpSecTransformStateEnabled() {
         // TODO: b/328844044: Ideally this code should gate the behavior by checking the
         // android.net.platform.flags.ipsec_transform_state flag but that flag is not accessible
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index b574782..a81ad22 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1913,7 +1913,6 @@
                 mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
 
                 if (direction == IpSecManager.DIRECTION_IN
-                        && mVcnContext.isFlagNetworkMetricMonitorEnabled()
                         && mVcnContext.isFlagIpSecTransformStateEnabled()) {
                     mUnderlyingNetworkController.updateInboundTransform(mUnderlying, transform);
                 }
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 5f704a0..6f1e15b 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -29,7 +29,6 @@
 import android.net.ConnectivityManager;
 import android.net.IpSecTransformState;
 import android.net.Network;
-import android.net.vcn.Flags;
 import android.net.vcn.VcnManager;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -233,7 +232,7 @@
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static int getMaxSeqNumIncreasePerSecond(@Nullable PersistableBundleWrapper carrierConfig) {
         int maxSeqNumIncrease = MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED;
-        if (Flags.handleSeqNumLeap() && carrierConfig != null) {
+        if (carrierConfig != null) {
             maxSeqNumIncrease =
                     carrierConfig.getInt(
                             VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY,
@@ -287,10 +286,8 @@
         // with the new interval
         mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
 
-        if (Flags.handleSeqNumLeap()) {
-            mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
-            mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
-        }
+        mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
+        mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
 
         if (canStart() != isStarted()) {
             if (canStart()) {
@@ -438,13 +435,10 @@
                 onValidationResultReceivedInternal(true /* isFailed */);
             }
 
-            // In both "valid" or "unusual_seq_num_leap" cases, trigger network validation
-            if (Flags.validateNetworkOnIpsecLoss()) {
-                // Trigger re-validation of the underlying network; if it fails, the VCN will
-                // attempt to migrate away.
-                mConnectivityManager.reportNetworkConnectivity(
-                        getNetwork(), false /* hasConnectivity */);
-            }
+            // In both "invalid" and "unusual_seq_num_leap" cases, trigger network validation. If
+            // validation fails, the VCN will attempt to migrate away.
+            mConnectivityManager.reportNetworkConnectivity(
+                    getNetwork(), false /* hasConnectivity */);
         }
     }
 
@@ -474,8 +468,7 @@
             boolean isUnusualSeqNumLeap = false;
 
             // Handle sequence number leap
-            if (Flags.handleSeqNumLeap()
-                    && maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
+            if (maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
                 final long timeDiffMillis =
                         newState.getTimestampMillis() - oldState.getTimestampMillis();
                 final long maxSeqNumIncrease = timeDiffMillis * maxSeqNumIncreasePerSecond / 1000;
@@ -506,7 +499,7 @@
                             + " actualPktCntDiff: "
                             + actualPktCntDiff);
 
-            if (Flags.handleSeqNumLeap() && expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) {
+            if (expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) {
                 // The sample size is too small to ensure a reliable detection result
                 return PacketLossCalculationResult.invalid();
             }
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index b9b1060..0d4c373 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -62,12 +62,6 @@
             @Nullable PersistableBundleWrapper carrierConfig,
             @NonNull NetworkMetricMonitorCallback callback)
             throws IllegalAccessException {
-        if (!vcnContext.isFlagNetworkMetricMonitorEnabled()) {
-            // Caller error
-            logWtf("networkMetricMonitor flag disabled");
-            throw new IllegalAccessException("networkMetricMonitor flag disabled");
-        }
-
         mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
         mNetwork = Objects.requireNonNull(network, "Missing network");
         mCallback = Objects.requireNonNull(callback, "Missing callback");
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 2b0ca08..ad5bc72 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -204,8 +204,7 @@
         List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
         mCellBringupCallbacks.clear();
 
-        if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
-                && mVcnContext.isFlagIpSecTransformStateEnabled()) {
+        if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
             for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
                 evaluator.close();
             }
@@ -431,8 +430,7 @@
                 .getAllSubIdsInGroup(mSubscriptionGroup)
                 .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
 
-            if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
-                    && mVcnContext.isFlagIpSecTransformStateEnabled()) {
+            if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
                 reevaluateNetworks();
             }
             return;
@@ -447,8 +445,7 @@
      */
     public void updateInboundTransform(
             @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) {
-        if (!mVcnContext.isFlagNetworkMetricMonitorEnabled()
-                || !mVcnContext.isFlagIpSecTransformStateEnabled()) {
+        if (!mVcnContext.isFlagIpSecTransformStateEnabled()) {
             logWtf("#updateInboundTransform: unexpected call; flags missing");
             return;
         }
@@ -575,8 +572,7 @@
 
         @Override
         public void onLost(@NonNull Network network) {
-            if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
-                    && mVcnContext.isFlagIpSecTransformStateEnabled()) {
+            if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
                 mUnderlyingNetworkRecords.get(network).close();
             }
 
@@ -652,8 +648,7 @@
     class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback {
         @Override
         public void onEvaluationResultChanged() {
-            if (!mVcnContext.isFlagNetworkMetricMonitorEnabled()
-                    || !mVcnContext.isFlagIpSecTransformStateEnabled()) {
+            if (!mVcnContext.isFlagIpSecTransformStateEnabled()) {
                 logWtf("#onEvaluationResultChanged: unexpected call; flags missing");
                 return;
             }
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 78e06d4..53b0751 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -25,7 +25,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.vcn.Flags;
 import android.net.vcn.VcnManager;
 import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.os.Handler;
@@ -194,8 +193,7 @@
     }
 
     private static boolean isIpSecPacketLossDetectorEnabled(VcnContext vcnContext) {
-        return vcnContext.isFlagIpSecTransformStateEnabled()
-                && vcnContext.isFlagNetworkMetricMonitorEnabled();
+        return vcnContext.isFlagIpSecTransformStateEnabled();
     }
 
     /** Get the comparator for UnderlyingNetworkEvaluator */
@@ -297,10 +295,8 @@
         updatePriorityClass(
                 underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
 
-        if (Flags.evaluateIpsecLossOnLpNcChange()) {
-            for (NetworkMetricMonitor monitor : mMetricMonitors) {
-                monitor.onLinkPropertiesOrCapabilitiesChanged();
-            }
+        for (NetworkMetricMonitor monitor : mMetricMonitors) {
+            monitor.onLinkPropertiesOrCapabilitiesChanged();
         }
     }
 
@@ -316,10 +312,8 @@
         updatePriorityClass(
                 underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
 
-        if (Flags.evaluateIpsecLossOnLpNcChange()) {
-            for (NetworkMetricMonitor monitor : mMetricMonitors) {
-                monitor.onLinkPropertiesOrCapabilitiesChanged();
-            }
+        for (NetworkMetricMonitor monitor : mMetricMonitors) {
+            monitor.onLinkPropertiesOrCapabilitiesChanged();
         }
     }
 
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index fbcc856..d192e64 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -21,6 +21,8 @@
 import android.os.CombinedVibration;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.VibrationEffectSegment;
 import android.util.SparseArray;
@@ -145,19 +147,30 @@
                 originalEffect, mScaleLevel, mAdaptiveScale);
     }
 
-    /**
-     * Returns true if this vibration can pipeline with the specified one.
-     *
-     * <p>Note that currently, repeating vibrations can't pipeline with following vibrations,
-     * because the cancel() call to stop the repetition will cancel a pending vibration too. This
-     * can be changed if we have a use-case to reason around behavior for. It may also be nice to
-     * pipeline very short vibrations together, regardless of the flag.
-     */
-    public boolean canPipelineWith(HalVibration vib) {
-        return callerInfo.uid == vib.callerInfo.uid && callerInfo.attrs.isFlagSet(
-                VibrationAttributes.FLAG_PIPELINED_EFFECT)
-                && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
-                && (mOriginalEffect.getDuration() != Long.MAX_VALUE);
+    /** Returns true if this vibration can pipeline with the specified one. */
+    public boolean canPipelineWith(HalVibration vib,
+            @Nullable SparseArray<VibratorInfo> vibratorInfos, int durationThresholdMs) {
+        long effectDuration = Flags.vibrationPipelineEnabled() && (vibratorInfos != null)
+                ? mEffectToPlay.getDuration(vibratorInfos)
+                : mEffectToPlay.getDuration();
+        if (effectDuration == Long.MAX_VALUE) {
+            // Repeating vibrations can't pipeline with following vibrations, because the cancel()
+            // call to stop the repetition will cancel a pending vibration too. This can be changed
+            // if we have a use-case, requiring changes to how pipelined vibrations are cancelled.
+            return false;
+        }
+        if (Flags.vibrationPipelineEnabled()
+                && (effectDuration > 0) && (effectDuration < durationThresholdMs)) {
+            // Duration is known and it's less than the pipeline threshold, so allow it.
+            // No need to check UID, as we want to avoid cancelling any short effect and let the
+            // vibrator hardware gracefully finish the vibration.
+            return true;
+        }
+        // Check the same app is requesting multiple vibrations with the pipeline flag,
+        // independently of the effect durations.
+        return callerInfo.uid == vib.callerInfo.uid
+                && callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
+                && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT);
     }
 
     private void fillFallbacksForEffect(CombinedVibration effect,
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 006a5bb..a235ba15 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -148,8 +148,8 @@
             case HapticFeedbackConstants.SCROLL_TICK:
             case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
             case HapticFeedbackConstants.SCROLL_LIMIT:
-                attrs = hapticFeedbackInputSourceCustomizationEnabled() ? TOUCH_VIBRATION_ATTRIBUTES
-                        : HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
+                // TODO(b/372820923): use touch attributes by default.
+                attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
                 break;
             case HapticFeedbackConstants.KEYBOARD_TAP:
             case HapticFeedbackConstants.KEYBOARD_RELEASE:
@@ -176,14 +176,15 @@
             int inputSource,
             @HapticFeedbackConstants.Flags int flags,
             @HapticFeedbackConstants.PrivateFlags int privFlags) {
-        if (hapticFeedbackInputSourceCustomizationEnabled()
-                && inputSource == InputDevice.SOURCE_ROTARY_ENCODER) {
+        if (hapticFeedbackInputSourceCustomizationEnabled()) {
             switch (effectId) {
                 case HapticFeedbackConstants.SCROLL_TICK,
                         HapticFeedbackConstants.SCROLL_ITEM_FOCUS,
                         HapticFeedbackConstants.SCROLL_LIMIT -> {
-                    return getVibrationAttributesWithFlags(HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES,
-                            effectId, flags);
+                    VibrationAttributes attrs = inputSource == InputDevice.SOURCE_ROTARY_ENCODER
+                            ? HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES
+                            : TOUCH_VIBRATION_ATTRIBUTES;
+                    return getVibrationAttributesWithFlags(attrs, effectId, flags);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9b7bdec..7d5d34d 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -168,12 +168,15 @@
 
     @VisibleForTesting
     final VibrationSettings mVibrationSettings;
+    private final VibrationConfig mVibrationConfig;
     private final VibrationScaler mVibrationScaler;
     private final VibratorControlService mVibratorControlService;
     private final InputDeviceDelegate mInputDeviceDelegate;
     private final DeviceAdapter mDeviceAdapter;
 
     @GuardedBy("mLock")
+    @Nullable private SparseArray<VibratorInfo> mVibratorInfos;
+    @GuardedBy("mLock")
     @Nullable private VibratorInfo mCombinedVibratorInfo;
     @GuardedBy("mLock")
     @Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
@@ -247,9 +250,9 @@
         mHandler = injector.createHandler(Looper.myLooper());
         mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
 
-        VibrationConfig vibrationConfig = new VibrationConfig(context.getResources());
-        mVibrationSettings = new VibrationSettings(mContext, mHandler, vibrationConfig);
-        mVibrationScaler = new VibrationScaler(vibrationConfig, mVibrationSettings);
+        mVibrationConfig = new VibrationConfig(context.getResources());
+        mVibrationSettings = new VibrationSettings(mContext, mHandler, mVibrationConfig);
+        mVibrationScaler = new VibrationScaler(mVibrationConfig, mVibrationSettings);
         mVibratorControlService = new VibratorControlService(mContext,
                 injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings,
                 mFrameworkStatsLogger, mLock);
@@ -295,7 +298,9 @@
             mVibratorIds = vibratorIds;
             mVibrators = new SparseArray<>(mVibratorIds.length);
             for (int vibratorId : vibratorIds) {
-                mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener));
+                VibratorController vibratorController =
+                        injector.createVibratorController(vibratorId, listener);
+                mVibrators.put(vibratorId, vibratorController);
             }
         }
 
@@ -334,6 +339,15 @@
                 mVibrators.valueAt(i).reloadVibratorInfoIfNeeded();
             }
 
+            synchronized (mLock) {
+                mVibratorInfos = transformAllVibratorsLocked(VibratorController::getVibratorInfo);
+                VibratorInfo[] infos = new VibratorInfo[mVibratorInfos.size()];
+                for (int i = 0; i < mVibratorInfos.size(); i++) {
+                    infos[i] = mVibratorInfos.valueAt(i);
+                }
+                mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos);
+            }
+
             mVibrationSettings.onSystemReady();
             mInputDeviceDelegate.onSystemReady();
 
@@ -633,7 +647,8 @@
                         endExternalVibrateLocked(Status.CANCELLED_SUPERSEDED, callerInfo,
                                 /* continueExternalControl= */ false);
                     } else if (mCurrentVibration != null) {
-                        if (mCurrentVibration.getVibration().canPipelineWith(vib)) {
+                        if (mCurrentVibration.getVibration().canPipelineWith(vib, mVibratorInfos,
+                                mVibrationConfig.getVibrationPipelineMaxDurationMs())) {
                             // Don't cancel the current vibration if it's pipeline-able.
                             // Note that if there is a pending next vibration that can't be
                             // pipelined, it will have already cancelled the current one, so we
@@ -1871,33 +1886,11 @@
         }
     }
 
+    @Nullable
     private VibratorInfo getCombinedVibratorInfo() {
         synchronized (mLock) {
-            // Used a cached resolving vibrator if one exists.
-            if (mCombinedVibratorInfo != null) {
-                return mCombinedVibratorInfo;
-            }
-
-            // Return an empty resolving vibrator if the service has no vibrator.
-            if (mVibratorIds.length == 0) {
-                return mCombinedVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
-            }
-
-            // Combine the vibrator infos of all the service's vibrator to create a single resolving
-            // vibrator that is based on the combined info.
-            VibratorInfo[] infos = new VibratorInfo[mVibratorIds.length];
-            for (int i = 0; i < mVibratorIds.length; i++) {
-                VibratorInfo info = getVibratorInfo(mVibratorIds[i]);
-                // If any one of the service's vibrator does not have a valid vibrator info, stop
-                // trying to create and cache a combined resolving vibrator. Combine the infos only
-                // when infos for all vibrators are available.
-                if (info == null) {
-                    return null;
-                }
-                infos[i] = info;
-            }
-
-            return mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos);
+            // This is only initialized at system ready, when all vibrator infos are fully loaded.
+            return mCombinedVibratorInfo;
         }
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2d75f35..da9a676 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -537,7 +537,7 @@
      * @return true unless the wallpaper changed during the color computation
      */
     private boolean extractColors(WallpaperData wallpaper) {
-        if (offloadColorExtraction()) return !mImageWallpaper.equals(wallpaper.getComponent());
+        if (offloadColorExtraction()) return true;
         String cropFile = null;
         boolean defaultImageWallpaper = false;
         int wallpaperId;
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ae30fcd..d119a08 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -442,8 +442,6 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
-        mService.mAmInternal.addCreatorToken(resultData);
-
         final ActivityRecord r;
         synchronized (mGlobalLock) {
             r = ActivityRecord.isInRootTaskLocked(token);
@@ -502,6 +500,8 @@
                 r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis());
             }
 
+            mService.mAmInternal.addCreatorToken(resultData, r.packageName);
+
             final long origId = Binder.clearCallingIdentity();
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
             try {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index fb5c115..3f814f9 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -244,7 +244,7 @@
                     status = ":completed-same-process:";
                 } else {
                     if (endInfo.mTransitionType == TYPE_TRANSITION_HOT_LAUNCH) {
-                        status = ":completed-hot:";
+                        status = !endInfo.mRelaunched ? ":completed-hot:" : ":completed-relaunch:";
                     } else if (endInfo.mTransitionType == TYPE_TRANSITION_WARM_LAUNCH) {
                         status = ":completed-warm:";
                     } else {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 31f4d08..460de01 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -33,7 +33,6 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
 import static android.app.WaitResult.INVALID_DELAY;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
@@ -42,7 +41,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -287,9 +285,6 @@
 import android.app.servertransaction.TopResumedActivityChangeItem;
 import android.app.servertransaction.TransferSplashScreenViewStateItem;
 import android.app.usage.UsageEvents.Event;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -469,11 +464,6 @@
     // finished destroying itself.
     private static final int DESTROY_TIMEOUT = 10 * 1000;
 
-    @ChangeId
-    @Overridable
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415;
-
     final ActivityTaskManagerService mAtmService;
     final ActivityCallerState mCallerState;
     @NonNull
@@ -3194,10 +3184,13 @@
                 && mDisplayContent != null && mDisplayContent.getConfiguration()
                     .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
                 && mDisplayContent.getIgnoreOrientationRequest()
-                && info.isChangeEnabled(UNIVERSAL_RESIZABLE_BY_DEFAULT);
+                && info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
         if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) {
             return false;
         }
+        if (mWmService.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(packageName)) {
+            return false;
+        }
         // If the user preference respects aspect ratio, then it becomes non-resizable.
         return !mAppCompatController.getAppCompatOverrides().getAppCompatAspectRatioOverrides()
                 .shouldApplyUserMinAspectRatioOverride();
@@ -5877,6 +5870,7 @@
             return;
         }
 
+        final State prevState = mState;
         mState = state;
 
         if (getTaskFragment() != null) {
@@ -5917,6 +5911,14 @@
                 mAtmService.updateBatteryStats(this, false);
                 mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED);
                 break;
+            case STOPPING:
+                // It is possible that an Activity is scheduled to be STOPPED directly from RESUMED
+                // state. Updating the PAUSED usage state in that case, since the Activity will be
+                // STOPPED while cycled through the PAUSED state.
+                if (prevState == RESUMED) {
+                    mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED);
+                }
+                break;
             case STOPPED:
                 mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
                 if (mDisplayContent != null) {
@@ -8382,15 +8384,12 @@
         // and back which can cause visible issues (see b/184078928).
         final int parentWindowingMode =
                 newParentConfiguration.windowConfiguration.getWindowingMode();
-        final boolean isInCameraCompatFreeform = parentWindowingMode == WINDOWING_MODE_FREEFORM
-                && mAppCompatController.getAppCompatCameraOverrides().getFreeformCameraCompatMode()
-                        != CAMERA_COMPAT_FREEFORM_NONE;
 
         // Bubble activities should always fill their parent and should not be letterboxed.
         final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble()
                 && (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
                         || parentWindowingMode == WINDOWING_MODE_FULLSCREEN
-                        || isInCameraCompatFreeform
+                        || AppCompatCameraPolicy.shouldCameraCompatControlOrientation(this)
                         // When starting to switch between PiP and fullscreen, the task is pinned
                         // and the activity is fullscreen. But only allow to apply letterbox if the
                         // activity is exiting PiP because an entered PiP should fill the task.
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 35ec5ad..c1f5a27 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -25,6 +25,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
 
+import static com.android.server.wm.ActivityStarter.Request.DEFAULT_INTENT_CREATOR_UID;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
@@ -43,7 +44,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -442,6 +442,17 @@
                         0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid,
                         callingPid);
                 aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
+                int creatorUid = DEFAULT_INTENT_CREATOR_UID;
+                String creatorPackage = null;
+                if (ActivityManagerService.IntentCreatorToken.isValid(intent)) {
+                    ActivityManagerService.IntentCreatorToken creatorToken =
+                            (ActivityManagerService.IntentCreatorToken) intent.getCreatorToken();
+                    if (creatorToken.getCreatorUid() != filterCallingUid) {
+                        creatorUid = creatorToken.getCreatorUid();
+                        creatorPackage = creatorToken.getCreatorPackage();
+                    }
+                    // leave creatorUid as -1 if the intent creator is the same as the launcher
+                }
 
                 if (aInfo != null) {
                     try {
@@ -455,6 +466,24 @@
                         return START_CANCELED;
                     }
 
+                    if (creatorUid != DEFAULT_INTENT_CREATOR_UID) {
+                        try {
+                            NeededUriGrants creatorIntentGrants = mSupervisor.mService.mUgmInternal
+                                    .checkGrantUriPermissionFromIntent(intent, creatorUid,
+                                            aInfo.applicationInfo.packageName,
+                                            UserHandle.getUserId(aInfo.applicationInfo.uid));
+                            if (intentGrants == null) {
+                                intentGrants = creatorIntentGrants;
+                            } else {
+                                intentGrants.merge(creatorIntentGrants);
+                            }
+                        } catch (SecurityException securityException) {
+                            ActivityStarter.logForIntentRedirect(
+                                    "Creator URI Grant Caused Exception.", intent, creatorUid,
+                                    creatorPackage, filterCallingUid, callingPackage);
+                            // TODO b/368559093 - rethrow the securityException.
+                        }
+                    }
                     if ((aInfo.applicationInfo.privateFlags
                             & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
                         throw new IllegalArgumentException(
@@ -478,6 +507,8 @@
                         .setCallingUid(callingUid)
                         .setCallingPackage(callingPackage)
                         .setCallingFeatureId(callingFeatureId)
+                        .setIntentCreatorUid(creatorUid)
+                        .setIntentCreatorPackage(creatorPackage)
                         .setRealCallingPid(realCallingPid)
                         .setRealCallingUid(realCallingUid)
                         .setActivityOptions(checkedOptions)
@@ -550,14 +581,14 @@
      * Starts an activity in the TaskFragment.
      * @param taskFragment TaskFragment {@link TaskFragment} to start the activity in.
      * @param activityIntent intent to start the activity.
-     * @param activityOptions ActivityOptions to start the activity with.
+     * @param activityOptions SafeActivityOptions to start the activity with.
      * @param resultTo the caller activity
      * @param callingUid the caller uid
      * @param callingPid the caller pid
      * @return the start result.
      */
     int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
-            @NonNull Intent activityIntent, @Nullable Bundle activityOptions,
+            @NonNull Intent activityIntent, @Nullable SafeActivityOptions activityOptions,
             @Nullable IBinder resultTo, int callingUid, int callingPid,
             @Nullable IBinder errorCallbackToken) {
         final ActivityRecord caller =
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 87fa62a..5d3ae54 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -86,6 +86,7 @@
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
 
 import android.annotation.IntDef;
@@ -131,6 +132,7 @@
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.protolog.ProtoLog;
+import com.android.server.am.ActivityManagerService.IntentCreatorToken;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
 import com.android.server.pm.PackageArchiver;
@@ -383,6 +385,7 @@
         private static final int DEFAULT_CALLING_PID = 0;
         static final int DEFAULT_REAL_CALLING_UID = -1;
         static final int DEFAULT_REAL_CALLING_PID = 0;
+        static final int DEFAULT_INTENT_CREATOR_UID = -1;
 
         IApplicationThread caller;
         Intent intent;
@@ -403,6 +406,8 @@
         @Nullable String callingFeatureId;
         int realCallingPid = DEFAULT_REAL_CALLING_PID;
         int realCallingUid = DEFAULT_REAL_CALLING_UID;
+        int intentCreatorUid = DEFAULT_INTENT_CREATOR_UID;
+        String intentCreatorPackage;
         int startFlags;
         SafeActivityOptions activityOptions;
         boolean ignoreTargetSecurity;
@@ -463,6 +468,8 @@
             callingPid = DEFAULT_CALLING_PID;
             callingUid = DEFAULT_CALLING_UID;
             callingPackage = null;
+            intentCreatorUid = DEFAULT_INTENT_CREATOR_UID;
+            intentCreatorPackage = null;
             callingFeatureId = null;
             realCallingPid = DEFAULT_REAL_CALLING_PID;
             realCallingUid = DEFAULT_REAL_CALLING_UID;
@@ -555,12 +562,14 @@
             // "resolved" calling UID, where we try our best to identify the
             // actual caller that is starting this activity
             int resolvedCallingUid = callingUid;
+            String resolvedCallingPackage = callingPackage;
             if (caller != null) {
                 synchronized (supervisor.mService.mGlobalLock) {
                     final WindowProcessController callerApp = supervisor.mService
                             .getProcessController(caller);
                     if (callerApp != null) {
                         resolvedCallingUid = callerApp.mInfo.uid;
+                        resolvedCallingPackage = callerApp.mInfo.packageName;
                     }
                 }
             }
@@ -596,7 +605,23 @@
             // Collect information about the target of the Intent.
             activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
                     profilerInfo);
-
+            // Check if the Intent was redirected
+            if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN)
+                    != 0) {
+                ActivityStarter.logForIntentRedirect(
+                        "Unparceled intent does not have a creator token set.", intent,
+                        intentCreatorUid,
+                        intentCreatorPackage, resolvedCallingUid, resolvedCallingPackage);
+                // TODO b/368559093 - eventually ramp up to throw SecurityException
+            }
+            if (IntentCreatorToken.isValid(intent)) {
+                IntentCreatorToken creatorToken = (IntentCreatorToken) intent.getCreatorToken();
+                if (creatorToken.getCreatorUid() != resolvedCallingUid) {
+                    intentCreatorUid = creatorToken.getCreatorUid();
+                    intentCreatorPackage = creatorToken.getCreatorPackage();
+                }
+                // leave intentCreatorUid as -1 if the intent creator is the same as the launcher
+            }
             // Carefully collect grants without holding lock
             if (activityInfo != null) {
                 if (android.security.Flags.contentUriPermissionApis()) {
@@ -606,11 +631,52 @@
                                     UserHandle.getUserId(activityInfo.applicationInfo.uid),
                                     activityInfo.requireContentUriPermissionFromCaller,
                                     /* requestHashCode */ this.hashCode());
+                    if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID) {
+                        try {
+                            NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal
+                                    .checkGrantUriPermissionFromIntent(intent, intentCreatorUid,
+                                            activityInfo.applicationInfo.packageName,
+                                            UserHandle.getUserId(activityInfo.applicationInfo.uid),
+                                            activityInfo.requireContentUriPermissionFromCaller,
+                                            /* requestHashCode */ this.hashCode());
+                            if (intentGrants == null) {
+                                intentGrants = creatorIntentGrants;
+                            } else {
+                                intentGrants.merge(creatorIntentGrants);
+                            }
+                        } catch (SecurityException securityException) {
+                            ActivityStarter.logForIntentRedirect(
+                                    "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
+                                    intentCreatorPackage, resolvedCallingUid,
+                                    resolvedCallingPackage);
+                            // TODO b/368559093 - rethrow the securityException.
+                        }
+                    }
                 } else {
                     intentGrants = supervisor.mService.mUgmInternal
                             .checkGrantUriPermissionFromIntent(intent, resolvedCallingUid,
                                     activityInfo.applicationInfo.packageName,
                                     UserHandle.getUserId(activityInfo.applicationInfo.uid));
+                    if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID && intentGrants != null) {
+                        try {
+                            NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal
+                                    .checkGrantUriPermissionFromIntent(intent, intentCreatorUid,
+                                            activityInfo.applicationInfo.packageName,
+                                            UserHandle.getUserId(
+                                                    activityInfo.applicationInfo.uid));
+                            if (intentGrants == null) {
+                                intentGrants = creatorIntentGrants;
+                            } else {
+                                intentGrants.merge(creatorIntentGrants);
+                            }
+                        } catch (SecurityException securityException) {
+                            ActivityStarter.logForIntentRedirect(
+                                    "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
+                                    intentCreatorPackage, resolvedCallingUid,
+                                    resolvedCallingPackage);
+                            // TODO b/368559093 - rethrow the securityException.
+                        }
+                    }
                 }
             }
         }
@@ -977,7 +1043,9 @@
         int requestCode = request.requestCode;
         int callingPid = request.callingPid;
         int callingUid = request.callingUid;
-        String callingPackage = request.callingPackage;
+        int intentCreatorUid = request.intentCreatorUid;
+        String intentCreatorPackage = request.intentCreatorPackage;
+        String intentCallingPackage = request.callingPackage;
         String callingFeatureId = request.callingFeatureId;
         final int realCallingPid = request.realCallingPid;
         final int realCallingUid = request.realCallingUid;
@@ -1062,7 +1130,7 @@
                 // launched in the app flow to redirect to an activity picked by the user, where
                 // we want the final activity to consider it to have been launched by the
                 // previous app activity.
-                callingPackage = sourceRecord.launchedFromPackage;
+                intentCallingPackage = sourceRecord.launchedFromPackage;
                 callingFeatureId = sourceRecord.launchedFromFeatureId;
             }
         }
@@ -1084,7 +1152,7 @@
                 if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
                     err = packageArchiver
                             .requestUnarchiveOnActivityStart(
-                                    intent, callingPackage, mRequest.userId, realCallingUid);
+                                    intent, intentCallingPackage, mRequest.userId, realCallingUid);
                 }
             }
         }
@@ -1143,7 +1211,7 @@
         boolean abort;
         try {
             abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                    requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
+                    requestCode, callingPid, callingUid, intentCallingPackage, callingFeatureId,
                     request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord,
                     resultRootTask);
         } catch (SecurityException e) {
@@ -1171,7 +1239,47 @@
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
         abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
-                callingPackage);
+                intentCallingPackage);
+
+        if (intentCreatorUid != Request.DEFAULT_INTENT_CREATOR_UID) {
+            try {
+                if (!mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
+                        requestCode, 0, intentCreatorUid, intentCreatorPackage, "",
+                        request.ignoreTargetSecurity, inTask != null, null, resultRecord,
+                        resultRootTask)) {
+                    logForIntentRedirect("Creator checkStartAnyActivityPermission Caused abortion.",
+                            intent, intentCreatorUid, intentCreatorPackage, callingUid,
+                            intentCallingPackage);
+                    // TODO b/368559093 - set abort to true.
+                    // abort = true;
+                }
+            } catch (SecurityException e) {
+                logForIntentRedirect("Creator checkStartAnyActivityPermission Caused Exception.",
+                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
+                        intentCallingPackage);
+                // TODO b/368559093 - rethrow the exception.
+                //throw e;
+            }
+            if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid, 0,
+                    resolvedType, aInfo.applicationInfo)) {
+                logForIntentRedirect("Creator IntentFirewall.checkStartActivity Caused abortion.",
+                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
+                        intentCallingPackage);
+                // TODO b/368559093 - set abort to true.
+                // abort = true;
+            }
+
+            if (!mService.getPermissionPolicyInternal().checkStartActivity(intent, intentCreatorUid,
+                    intentCreatorPackage)) {
+                logForIntentRedirect(
+                        "Creator PermissionPolicyService.checkStartActivity Caused abortion.",
+                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
+                        intentCallingPackage);
+                // TODO b/368559093 - set abort to true.
+                // abort = true;
+            }
+            intent.removeCreatorTokenInfo();
+        }
 
         // Merge the two options bundles, while realCallerOptions takes precedence.
         ActivityOptions checkedOptions = options != null
@@ -1188,7 +1296,7 @@
                         balController.checkBackgroundActivityStart(
                             callingUid,
                             callingPid,
-                            callingPackage,
+                                intentCallingPackage,
                             realCallingUid,
                             realCallingPid,
                             callerApp,
@@ -1209,7 +1317,7 @@
         if (request.allowPendingRemoteAnimationRegistryLookup) {
             checkedOptions = mService.getActivityStartController()
                     .getPendingRemoteAnimationRegistry()
-                    .overrideOptionsIfNeeded(callingPackage, checkedOptions);
+                    .overrideOptionsIfNeeded(intentCallingPackage, checkedOptions);
         }
         if (mService.mController != null) {
             try {
@@ -1225,7 +1333,8 @@
 
         final TaskDisplayArea suggestedLaunchDisplayArea =
                 computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions);
-        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
+        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags,
+                intentCallingPackage,
                 callingFeatureId);
         if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
                 callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
@@ -1263,7 +1372,8 @@
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
                 final IIntentSender target = mService.getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId,
+                        ActivityManager.INTENT_SENDER_ACTIVITY, intentCallingPackage,
+                        callingFeatureId,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
@@ -1326,7 +1436,8 @@
         // app [on install success].
         if (rInfo != null && rInfo.auxiliaryInfo != null) {
             intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
-                    callingPackage, callingFeatureId, verificationBundle, resolvedType, userId);
+                    intentCallingPackage, callingFeatureId, verificationBundle, resolvedType,
+                    userId);
             resolvedType = null;
             callingUid = realCallingUid;
             callingPid = realCallingPid;
@@ -1349,7 +1460,7 @@
                 .setCaller(callerApp)
                 .setLaunchedFromPid(callingPid)
                 .setLaunchedFromUid(callingUid)
-                .setLaunchedFromPackage(callingPackage)
+                .setLaunchedFromPackage(intentCallingPackage)
                 .setLaunchedFromFeature(callingFeatureId)
                 .setIntent(intent)
                 .setResolvedType(resolvedType)
@@ -2786,10 +2897,22 @@
             }
         }
 
-        if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
-                && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) {
-            // ignore the flag if there is no the sourceRecord or without new_task flag
-            mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+        if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+            final boolean hasNewTaskFlag = (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0;
+            if (!hasNewTaskFlag || mSourceRecord == null) {
+                // ignore the flag if there is no the sourceRecord or without new_task flag
+                Slog.w(TAG_WM, !hasNewTaskFlag
+                        ? "Launch adjacent ignored due to missing NEW_TASK"
+                        : "Launch adjacent ignored due to missing source activity");
+                mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+            }
+            // Ensure that the source task or its parents has not disabled launch-adjacent
+            if (mSourceRecord != null && mSourceRecord.getTask() != null &&
+                    mSourceRecord.getTask().isLaunchAdjacentDisabled()) {
+                Slog.w(TAG_WM, "Launch adjacent blocked by source task or ancestor");
+                mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+            }
+
         }
     }
 
@@ -3295,6 +3418,16 @@
         return this;
     }
 
+    ActivityStarter setIntentCreatorUid(int uid) {
+        mRequest.intentCreatorUid = uid;
+        return this;
+    }
+
+    ActivityStarter setIntentCreatorPackage(String intentCreatorPackage) {
+        mRequest.intentCreatorPackage = intentCreatorPackage;
+        return this;
+    }
+
     /**
      * Sets the pid of the caller who requested to launch the activity.
      *
@@ -3454,4 +3587,19 @@
         pw.print(" mInTaskFragment=");
         pw.println(mInTaskFragment);
     }
+
+    static void logForIntentRedirect(String message, Intent intent, int intentCreatorUid,
+            String intentCreatorPackage, int callingUid, String callingPackage) {
+        String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
+                intentCreatorPackage, callingUid, callingPackage);
+        Slog.wtf(TAG, msg);
+    }
+
+    private static String getIntentRedirectPreventedLogMessage(String message, Intent intent,
+            int intentCreatorUid, String intentCreatorPackage, int callingUid,
+            String callingPackage) {
+        return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid
+                + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid
+                + "; callingPackage: " + callingPackage + "; intent: " + intent;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 3d6b64b..3560565 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -589,7 +589,7 @@
      * sensitive environment.
      */
     public abstract TaskSnapshot getTaskSnapshotBlocking(int taskId,
-            boolean isLowResolution);
+            boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage);
 
     /** Returns true if uid is considered foreground for activity start purposes. */
     public abstract boolean isUidForeground(int uid);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3dfc8f4..111e74e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1228,7 +1228,7 @@
             String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
             Bundle bOptions) {
-        mAmInternal.addCreatorToken(intent);
+        mAmInternal.addCreatorToken(intent, callingPackage);
         return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                 resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                 UserHandle.getCallingUserId());
@@ -1243,7 +1243,7 @@
         enforceNotIsolatedCaller(reason);
         if (intents != null) {
             for (Intent intent : intents) {
-                mAmInternal.addCreatorToken(intent);
+                mAmInternal.addCreatorToken(intent, callingPackage);
             }
         }
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
@@ -1275,7 +1275,7 @@
             @Nullable String callingFeatureId, Intent intent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
             ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
-        mAmInternal.addCreatorToken(intent);
+        mAmInternal.addCreatorToken(intent, callingPackage);
         final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
 
         assertPackageMatchesCallingUid(callingPackage);
@@ -1330,7 +1330,6 @@
             }
             // Remove existing mismatch flag so it can be properly updated later
             fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
-            mAmInternal.addCreatorToken(fillInIntent);
         }
 
         if (!(target instanceof PendingIntentRecord)) {
@@ -1339,6 +1338,10 @@
 
         PendingIntentRecord pir = (PendingIntentRecord) target;
 
+        if (fillInIntent != null) {
+            mAmInternal.addCreatorToken(fillInIntent, pir.getPackageName());
+        }
+
         synchronized (mGlobalLock) {
             // If this is coming from the currently resumed activity, it is
             // effectively saying that app switches are allowed at this point.
@@ -1349,6 +1352,7 @@
                 mAppSwitchesState = APP_SWITCH_ALLOW;
             }
         }
+
         return pir.sendInner(caller, 0, fillInIntent, resolvedType, allowlistToken, null, null,
                 resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
     }
@@ -1361,8 +1365,6 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
-        mAmInternal.addCreatorToken(intent);
-
         SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
 
         synchronized (mGlobalLock) {
@@ -1376,6 +1378,9 @@
                 SafeActivityOptions.abort(options);
                 return false;
             }
+
+            mAmInternal.addCreatorToken(intent, r.packageName);
+
             intent = new Intent(intent);
             // Remove existing mismatch flag so it can be properly updated later
             intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
@@ -3946,6 +3951,28 @@
         }
     }
 
+    private TaskSnapshot getTaskSnapshotInner(int taskId, boolean isLowResolution,
+            @TaskSnapshot.ReferenceFlags int usage) {
+        final Task task;
+        synchronized (mGlobalLock) {
+            task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
+            if (task == null) {
+                Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+                return null;
+            }
+            // Try to load snapshot from cache first, and add reference if the snapshot is in cache.
+            final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+                    task.mUserId, false /* restoreFromDisk */, isLowResolution);
+            if (snapshot != null) {
+                snapshot.addReference(usage);
+                return snapshot;
+            }
+        }
+        // Don't call this while holding the lock as this operation might hit the disk.
+        return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+                task.mUserId, true /* restoreFromDisk */, isLowResolution);
+    }
+
     @Override
     public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
         mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
@@ -6725,6 +6752,8 @@
                         break;
                     }
                 }
+                ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
+                        wpc.mName, wpc.getConfiguration());
                 // The "info" can be the target of instrumentation.
                 return new PreBindInfo(compatibilityInfoForPackageLocked(info),
                         new Configuration(wpc.getConfiguration()));
@@ -7274,8 +7303,9 @@
 
         @Override
         public TaskSnapshot getTaskSnapshotBlocking(
-                int taskId, boolean isLowResolution) {
-            return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution);
+                int taskId, boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage) {
+            return ActivityTaskManagerService.this.getTaskSnapshotInner(
+                    taskId, isLowResolution, usage);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 4d17ed2..eee4c86 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -160,7 +160,7 @@
             Intent intent, String resolvedType, Bundle bOptions) {
         checkCallerOrSystemOrRoot();
         mService.assertPackageMatchesCallingUid(callingPackage);
-        mService.mAmInternal.addCreatorToken(intent);
+        mService.mAmInternal.addCreatorToken(intent, callingPackage);
 
         int callingUser = UserHandle.getCallingUserId();
         Task task;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 94cd2e6..70f9ebb 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -34,6 +34,7 @@
 import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY;
 import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1237,8 +1238,9 @@
                 }
                 allWindowDrawn &= next.mAppWindowDrawn;
             }
-            // Do not remove until transition ready.
-            if (!activity.isVisible()) {
+            // Do not remove windowless surfaces if the transaction has not been applied.
+            if (activity.getSyncTransactionCommitCallbackDepth() > 0
+                    || activity.mSyncState != SYNC_STATE_NONE) {
                 return;
             }
             if (allWindowDrawn) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 515f148..f9902cf 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -66,6 +66,7 @@
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -129,6 +130,7 @@
     /** If enabled the creator will not allow BAL on its behalf by default. */
     @ChangeId
     @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
+    @Overridable
     private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR =
             296478951;
     public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
@@ -558,7 +560,7 @@
                     .append(mBalAllowedByPiCreatorWithHardening);
             sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller);
             sb.append("; callerStartMode: ").append(balStartModeToString(
-                    mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
+                    mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()));
             sb.append("; hasRealCaller: ").append(hasRealCaller());
             sb.append("; isCallForResult: ").append(mIsCallForResult);
             sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -585,7 +587,7 @@
                 sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
                 sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller);
                 sb.append("; realCallerStartMode: ").append(balStartModeToString(
-                        mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()));
+                        mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
             }
             // features
             sb.append("; balImproveRealCallerVisibilityCheck: ")
@@ -792,7 +794,8 @@
             // Allowed before V by creator
             if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG, "With Android 15 BAL hardening this activity start may be blocked"
-                        + " if the PI creator upgrades target_sdk to 35+! "
+                        + " if the PI creator upgrades target_sdk to 35+!"
+                        + " goo.gle/android-bal"
                         + " (missing opt in by PI creator)!" + state);
                 return allowBasedOnCaller(state);
             }
@@ -802,6 +805,7 @@
             if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG, "With Android 14 BAL hardening this activity start will be blocked"
                         + " if the PI sender upgrades target_sdk to 34+! "
+                        + " goo.gle/android-bal"
                         + " (missing opt in by PI sender)!" + state);
                 return allowBasedOnRealCaller(state);
             }
@@ -829,7 +833,8 @@
     }
 
     private BalVerdict abortLaunch(BalState state) {
-        Slog.wtf(TAG, "Background activity launch blocked! " + state);
+        Slog.wtf(TAG, "Background activity launch blocked! goo.gle/android-bal "
+                + state);
         if (balShowToastsBlocked()
                 && (state.mResultForCaller.allows() || state.mResultForRealCaller.allows())) {
             // only show a toast if either caller or real caller could launch if they opted in
@@ -1044,6 +1049,24 @@
                     "realCallingUid has BAL permission.");
         }
 
+        // don't abort if the realCallingUid has SYSTEM_ALERT_WINDOW permission
+        Slog.i(TAG, "hasSystemAlertWindowPermission(" + state.mRealCallingUid + ", "
+                + state.mRealCallingPid + ", " + state.mRealCallingPackage + ") "
+                + balStartModeToString(
+                state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
+        if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+                == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+                && mService.hasSystemAlertWindowPermission(state.mRealCallingUid,
+                state.mRealCallingPid, state.mRealCallingPackage)) {
+            Slog.w(
+                    TAG,
+                    "Background activity start for "
+                            + state.mRealCallingPackage
+                            + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+            return new BalVerdict(BAL_ALLOW_SAW_PERMISSION,
+                    /*background*/ true, "SYSTEM_ALERT_WINDOW permission is granted");
+        }
+
         // if the realCallingUid is a persistent system process, abort if the IntentSender
         // wasn't allowed to start an activity
         if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 670a61d..05dcbb7 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
@@ -268,7 +269,16 @@
             }
             final DisplayPolicy.DecorInsets.Info decor =
                     displayContent.getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh);
-            outAppBounds.intersectUnchecked(decor.mOverrideNonDecorFrame);
+            if (!outAppBounds.intersect(decor.mOverrideNonDecorFrame)) {
+                // TODO (b/364883053): When a split screen is requested from an app intent for a new
+                //  task, the bounds is not the final bounds, and this is also not a bounds change
+                //  event handled correctly with the offset. Revert back to legacy method for this
+                //  case.
+                if (inOutConfig.windowConfiguration.getWindowingMode()
+                        == WINDOWING_MODE_MULTI_WINDOW) {
+                    outAppBounds.inset(decor.mOverrideNonDecorInsets);
+                }
+            }
             if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) {
                 outAppBounds.offset(-task.mOffsetXForInsets, -task.mOffsetYForInsets);
             }
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index bc33946..0b5872b 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -285,6 +285,11 @@
         }
     }
 
+    private boolean isDisplayReadyForMirroring() {
+        return mDisplayContent.getDisplayInfo().type != Display.TYPE_EXTERNAL
+                || mDisplayContent.mWmService.mDisplayManagerInternal.isDisplayReadyForMirroring(
+                        mDisplayContent.getDisplayId());
+    }
 
     /**
      * Ensure recording does not fall back to the display stack; ensure the recording is stopped
@@ -335,7 +340,7 @@
             return;
         }
 
-        if (mContentRecordingSession.isWaitingForConsent()) {
+        if (mContentRecordingSession.isWaitingForConsent() || !isDisplayReadyForMirroring()) {
             ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: waiting to record, so do "
                     + "nothing");
             return;
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 3dc035e..cbe3d79 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -401,6 +401,7 @@
                 || !Objects.equals(first.deviceProductInfo, second.deviceProductInfo)
                 || first.modeId != second.modeId
                 || first.renderFrameRate != second.renderFrameRate
+                || first.hasArrSupport != second.hasArrSupport
                 || first.defaultModeId != second.defaultModeId
                 || first.userPreferredModeId != second.userPreferredModeId
                 || !Arrays.equals(first.supportedModes, second.supportedModes)
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 1a8f5fc..fcf88d3 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -36,7 +36,7 @@
 import android.os.SystemProperties;
 import android.util.Size;
 import android.view.Gravity;
-import android.window.flags.DesktopModeFlags;
+import android.window.DesktopModeFlags;
 
 import java.util.function.Consumer;
 
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index b5ea0bd..6bf1c46 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.SystemProperties;
-import android.window.flags.DesktopModeFlags;
+import android.window.DesktopModeFlags;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 6a23aaa..e9c6e93 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -274,34 +274,7 @@
         if (caller != controlTarget) {
             if (Flags.refactorInsetsController()) {
                 if (isImeInputTarget(caller)) {
-                    // In case of the multi window mode, update the requestedVisibleTypes from
-                    // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
-                    // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
-                    // its new requested visibility for the IME
-                    boolean imeVisible = caller.isRequestedVisible(WindowInsets.Type.ime());
-                    if (controlTarget != null) {
-                        ImeTracker.forLogging().onProgress(statsToken,
-                                ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
-                        controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
-                    } else if (caller instanceof InsetsControlTarget) {
-                        // In case of a virtual display that cannot show the IME, the
-                        // controlTarget will be null here, as no controlTarget was set yet. In
-                        // that case, proceed similar to the multi window mode (fallback =
-                        // RemoteInsetsControlTarget of the default display)
-                        controlTarget = mDisplayContent.getImeHostOrFallback(
-                                ((InsetsControlTarget) caller).getWindow());
-
-                        if (controlTarget != caller) {
-                            ImeTracker.forLogging().onProgress(statsToken,
-                                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
-                            controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
-                        } else {
-                            ImeTracker.forLogging().onFailed(statsToken,
-                                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
-                        }
-                    }
-
-                    invokeOnImeRequestedChangedListener(caller, statsToken);
+                    reportImeInputTargetStateToControlTarget(caller, controlTarget, statsToken);
                 } else {
                     // TODO(b/353463205) add ImeTracker?
                 }
@@ -332,16 +305,44 @@
         if (Flags.refactorInsetsController() && target != null) {
             InsetsControlTarget imeControlTarget = getControlTarget();
             if (target != imeControlTarget) {
-                // If the targetWin is not the imeControlTarget (=RemoteInsetsControlTarget) let it
-                // know about the new requestedVisibleTypes for the IME.
-                if (imeControlTarget != null) {
-                    imeControlTarget.setImeInputTargetRequestedVisibility(
-                            (target.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
-                }
+                // TODO(b/353463205): start new request here?
+                reportImeInputTargetStateToControlTarget(target, imeControlTarget,
+                        null /* statsToken */);
             }
         }
     }
 
+    private void reportImeInputTargetStateToControlTarget(@NonNull InsetsTarget imeInsetsTarget,
+            InsetsControlTarget controlTarget, @Nullable ImeTracker.Token statsToken) {
+        // In case of the multi window mode, update the requestedVisibleTypes from
+        // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
+        // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
+        // its new requested visibility for the IME
+        boolean imeVisible = imeInsetsTarget.isRequestedVisible(WindowInsets.Type.ime());
+        if (controlTarget != null) {
+            ImeTracker.forLogging().onProgress(statsToken,
+                    ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+            controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+        } else if (imeInsetsTarget instanceof InsetsControlTarget) {
+            // In case of a virtual display that cannot show the IME, the
+            // controlTarget will be null here, as no controlTarget was set yet. In
+            // that case, proceed similar to the multi window mode (fallback =
+            // RemoteInsetsControlTarget of the default display)
+            controlTarget = mDisplayContent.getImeHostOrFallback(
+                    ((InsetsControlTarget) imeInsetsTarget).getWindow());
+
+            if (controlTarget != imeInsetsTarget) {
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+                controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+            } else {
+                ImeTracker.forLogging().onFailed(statsToken,
+                        ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+            }
+        }
+        invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken);
+    }
+
     // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
     private void invokeOnImeRequestedChangedListener(InsetsTarget insetsTarget,
             @Nullable ImeTracker.Token statsToken) {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index dcf0319..9d21183 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -16,6 +16,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
@@ -28,9 +29,12 @@
 import android.gui.StalledTransactionInfo;
 import android.os.Debug;
 import android.os.IBinder;
+import android.os.Trace;
 import android.util.Slog;
+import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.InputApplicationHandle;
+import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -64,6 +68,12 @@
     // which point the ActivityManager will enable dispatching.
     private boolean mInputDispatchEnabled;
 
+    /**
+     * The last input devices info which may affect display configuration. This is a quick lookup
+     * to detect interested changes without entering WM lock.
+     */
+    private SparseIntArray mLastInputConfigurationSources;
+
     public InputManagerCallback(WindowManagerService service) {
         mService = service;
     }
@@ -117,8 +127,18 @@
     /** Notifies that the input device configuration has changed. */
     @Override
     public void notifyConfigurationChanged() {
-        synchronized (mService.mGlobalLock) {
-            mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "notifyConfigurationChanged");
+        final boolean changed = !com.android.window.flags.Flags.filterIrrelevantInputDeviceChange()
+                || updateLastInputConfigurationSources();
+
+        // Even if the input devices are not changed, there could be other pending changes
+        // during booting. It's fine to apply earlier.
+        if (changed || !mService.mDisplayEnabled) {
+            synchronized (mService.mGlobalLock) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "inputDeviceConfigChanged");
+                mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            }
         }
 
         synchronized (mInputDevicesReadyMonitor) {
@@ -127,6 +147,40 @@
                 mInputDevicesReadyMonitor.notifyAll();
             }
         }
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+    }
+
+    /** Returns {@code true} if the change of input devices may affect display configuration. */
+    private boolean updateLastInputConfigurationSources() {
+        final InputDevice[] devices = mService.mInputManager.getInputDevices();
+        final SparseIntArray newSources = new SparseIntArray(8);
+        final SparseIntArray lastSources = mLastInputConfigurationSources;
+        boolean changed = lastSources == null;
+        for (InputDevice device : devices) {
+            final String descriptor = device.getDescriptor();
+            if (descriptor == null || device.isVirtual()) {
+                continue;
+            }
+            final int key = descriptor.hashCode();
+            // The interested attributes from DisplayContent#computeScreenConfiguration.
+            int newSourceHash = device.getSources();
+            newSourceHash = newSourceHash * 31 + device.getKeyboardType();
+            newSourceHash = newSourceHash * 31 + device.getAssociatedDisplayId();
+            newSourceHash = newSourceHash * 31 + (device.isExternal() ? 1 : 0);
+            newSourceHash = newSourceHash * 31 + (device.isEnabled() ? 1 : 0);
+            newSources.put(key, newSourceHash);
+            if (lastSources != null && !changed) {
+                final int lastSource = lastSources.get(key, 0 /* valueIfKeyNotFound */);
+                if (lastSource != newSourceHash) {
+                    changed = true;
+                }
+            }
+        }
+        if (lastSources != null && lastSources.size() != newSources.size()) {
+            changed = true;
+        }
+        mLastInputConfigurationSources = newSources;
+        return changed;
     }
 
     /** Notifies that the pointer location configuration has changed. */
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 8f28f59..1d4d6eb 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -384,16 +384,19 @@
         }
         final boolean serverVisibleChanged = mServerVisible != isServerVisible;
         setServerVisible(isServerVisible);
-        final boolean positionChanged = updateInsetsControlPosition(windowState);
-        if (mControl != null && !positionChanged
-                // The insets hint would be updated if the position is changed. Here updates it for
-                // the possible change of the bounds or the server visibility.
-                && (updateInsetsHint()
-                        || serverVisibleChanged
-                                && android.view.inputmethod.Flags.refactorInsetsController())) {
-            // Only call notifyControlChanged here when the position is not changed. Otherwise, it
-            // is called or is scheduled to be called during updateInsetsControlPosition.
-            mStateController.notifyControlChanged(mControlTarget, this);
+        if (mControl != null) {
+            final boolean positionChanged = updateInsetsControlPosition(windowState);
+            if (!(positionChanged || mHasPendingPosition)
+                    // The insets hint would be updated while changing the position. Here updates it
+                    // for the possible change of the bounds or the server visibility.
+                    && (updateInsetsHint()
+                            || (android.view.inputmethod.Flags.refactorInsetsController()))
+                                    && serverVisibleChanged) {
+                // Only call notifyControlChanged here when the position hasn't been or won't be
+                // changed. Otherwise, it has been called or scheduled to be called during
+                // updateInsetsControlPosition.
+                mStateController.notifyControlChanged(mControlTarget, this);
+            }
         }
     }
 
@@ -406,9 +409,10 @@
         }
         final Point position = getWindowFrameSurfacePosition();
         if (!mPosition.equals(position)) {
-            mPosition.set(position.x, position.y);
+            mPosition.set(position);
             if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
                     && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
+                mHasPendingPosition = true;
                 windowState.applyWithNextDraw(mSetControlPositionConsumer);
             } else {
                 Transaction t = mWindowContainer.getSyncTransaction();
@@ -549,6 +553,7 @@
         }
         boolean initiallyVisible = mClientVisible;
         final Point surfacePosition = getWindowFrameSurfacePosition();
+        mPosition.set(surfacePosition);
         mAdapter = new ControlAdapter(surfacePosition);
         if (mSource.getType() == WindowInsets.Type.ime()) {
             if (android.view.inputmethod.Flags.refactorInsetsController()) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 266fdbb..41a5804 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1407,7 +1407,7 @@
             displayId = rootTask != null ? rootTask.getDisplayId() : DEFAULT_DISPLAY;
         }
 
-        final DisplayContent display = getDisplayContent(displayId);
+        final DisplayContent display = getDisplayContentOrCreate(displayId);
         return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
                         result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                                 allowInstrumenting, fromHomeKey),
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 0d3fa1b..4bd294b 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -32,7 +32,6 @@
 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.utils.CoordinateTransforms.computeRotationMatrix;
-import static com.android.window.flags.Flags.deleteCaptureDisplay;
 
 import android.animation.ArgbEvaluator;
 import android.content.Context;
@@ -41,11 +40,9 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
-import android.os.IBinder;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.DisplayAddress;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
@@ -58,7 +55,6 @@
 import com.android.internal.R;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.protolog.ProtoLog;
-import com.android.server.display.DisplayControl;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
@@ -171,33 +167,7 @@
 
         try {
             final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer;
-            if (isSizeChanged && !deleteCaptureDisplay()) {
-                final DisplayAddress address = displayInfo.address;
-                if (!(address instanceof DisplayAddress.Physical)) {
-                    Slog.e(TAG, "Display does not have a physical address: " + displayId);
-                    return;
-                }
-                final DisplayAddress.Physical physicalAddress =
-                        (DisplayAddress.Physical) address;
-                final IBinder displayToken = DisplayControl.getPhysicalDisplayToken(
-                        physicalAddress.getPhysicalDisplayId());
-                if (displayToken == null) {
-                    Slog.e(TAG, "Display token is null.");
-                    return;
-                }
-                // Temporarily not skip screenshot for the rounded corner overlays and screenshot
-                // the whole display to include the rounded corner overlays.
-                setSkipScreenshotForRoundedCornerOverlays(false, t);
-                mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays();
-                final ScreenCapture.DisplayCaptureArgs captureArgs =
-                        new ScreenCapture.DisplayCaptureArgs.Builder(displayToken)
-                                .setSourceCrop(new Rect(0, 0, width, height))
-                                .setAllowProtected(true)
-                                .setCaptureSecureLayers(true)
-                                .setHintForSeamlessTransition(true)
-                                .build();
-                screenshotBuffer = ScreenCapture.captureDisplay(captureArgs);
-            } else if (isSizeChanged) {
+            if (isSizeChanged) {
                 // Temporarily not skip screenshot for the rounded corner overlays and screenshot
                 // the whole display to include the rounded corner overlays.
                 setSkipScreenshotForRoundedCornerOverlays(false, t);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 98919d9..4861341 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -503,6 +503,11 @@
     boolean mIsTrimmableFromRecents;
 
     /**
+     * Sets whether the launch-adjacent flag is respected or not for this task or its child tasks.
+     */
+    private boolean mLaunchAdjacentDisabled;
+
+    /**
      * Bounds offset should be applied when calculating compatible configuration for apps targeting
      * SDK level 34 or before.
      */
@@ -3802,6 +3807,9 @@
         pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
         pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
         pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents);
+        if (mLaunchAdjacentDisabled) {
+            pw.println(prefix + "mLaunchAdjacentDisabled=true");
+        }
     }
 
     @Override
@@ -5734,6 +5742,12 @@
     }
 
     private boolean canMoveTaskToBack(Task task) {
+        // Checks whether a task is a child of this task because it can be reparented when
+        // transition is deferred.
+        if (task != this && !task.isDescendantOf(this)) {
+            return false;
+        }
+
         // In LockTask mode, moving a locked task to the back of the root task may expose unlocked
         // ones. Therefore we need to check if this operation is allowed.
         if (!mAtmService.getLockTaskController().canMoveTaskToBack(task)) {
@@ -5803,7 +5817,7 @@
                     (deferred) -> {
                         // Need to check again if deferred since the system might
                         // be in a different state.
-                        if (!isAttached() || (deferred && !canMoveTaskToBack(tr))) {
+                        if (!tr.isAttached() || (deferred && !canMoveTaskToBack(tr))) {
                             Slog.e(TAG, "Failed to move task to back after saying we could: "
                                     + tr.mTaskId);
                             transition.abort();
@@ -6268,6 +6282,28 @@
     }
 
     /**
+     * Sets this task and its children to disable respecting launch-adjacent.
+     */
+    void setLaunchAdjacentDisabled(boolean disabled) {
+        mLaunchAdjacentDisabled = disabled;
+    }
+
+    /**
+     * Returns whether this task or any of its ancestors have disabled respecting the
+     * launch-adjacent flag.
+     */
+    boolean isLaunchAdjacentDisabled() {
+        Task t = this;
+        while (t != null) {
+            if (t.mLaunchAdjacentDisabled) {
+                return true;
+            }
+            t = t.getParent().asTask();
+        }
+        return false;
+    }
+
+    /**
      * Return true if the activityInfo has the same requiredDisplayCategory as this task.
      */
     boolean isSameRequiredDisplayCategory(@NonNull ActivityInfo info) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 5dd3bbc..2c71c1a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1074,6 +1074,8 @@
             final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask());
             // We only allow this for created by organizer tasks.
             if (launchRootTask != null && launchRootTask.mCreatedByOrganizer) {
+                Slog.i(TAG_WM, "Using launch root task from activity options: taskId="
+                        + launchRootTask.mTaskId);
                 return launchRootTask;
             }
         }
@@ -1081,19 +1083,25 @@
         // Use launch-adjacent-flag-root if launching with launch-adjacent flag.
         if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
                 && mLaunchAdjacentFlagRootTask != null) {
+            final Task launchAdjacentRootAdjacentTask =
+                    mLaunchAdjacentFlagRootTask.getAdjacentTask();
             if (sourceTask != null && (sourceTask == candidateTask
                     || sourceTask.topRunningActivity() == null)) {
                 // Do nothing when task that is getting opened is same as the source or when
                 // the source is no-longer valid.
                 Slog.w(TAG_WM, "Ignoring LAUNCH_ADJACENT because adjacent source is gone.");
             } else if (sourceTask != null
-                    && mLaunchAdjacentFlagRootTask.getAdjacentTask() != null
+                    && launchAdjacentRootAdjacentTask != null
                     && (sourceTask == mLaunchAdjacentFlagRootTask
                     || sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
-                // If the adjacent launch is coming from the same root, launch to
-                // adjacent root instead.
-                return mLaunchAdjacentFlagRootTask.getAdjacentTask();
+                // If the adjacent launch is coming from the same root that was specified as the
+                // launch-adjacent task, so instead we launch to its adjacent root instead.
+                Slog.i(TAG_WM, "Using adjacent-to specified launch-adjacent task: taskId="
+                        + launchAdjacentRootAdjacentTask.mTaskId);
+                return launchAdjacentRootAdjacentTask;
             } else {
+                Slog.i(TAG_WM, "Using specified launch-adjacent task: taskId="
+                        + mLaunchAdjacentFlagRootTask.mTaskId);
                 return mLaunchAdjacentFlagRootTask;
             }
         }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1659f7b..585537b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -547,13 +547,12 @@
             if (!ar.isVisible() || !ar.isVisibleRequested()) return;
             if (mConfigAtEndActivities == null) {
                 mConfigAtEndActivities = new ArrayList<>();
-            }
-            if (mConfigAtEndActivities.contains(ar)) {
+            } else if (mConfigAtEndActivities.contains(ar)) {
                 return;
             }
             mConfigAtEndActivities.add(ar);
             ar.pauseConfigurationDispatch();
-            snapshotStartState(ar);
+            collect(ar);
             mChanges.get(ar).mFlags |= ChangeInfo.FLAG_CHANGE_CONFIG_AT_END;
         });
     }
@@ -1705,54 +1704,6 @@
         change.mFlags |= ChangeInfo.FLAG_CHANGE_NO_ANIMATION;
     }
 
-    void prepareConfigAtEnd(SurfaceControl.Transaction transact, ArrayList<ChangeInfo> targets) {
-        if (mConfigAtEndActivities == null) return;
-        for (int i = 0; i < mConfigAtEndActivities.size(); ++i) {
-            final ActivityRecord ar = mConfigAtEndActivities.get(i);
-            if (!ar.isVisibleRequested()) continue;
-            final SurfaceControl sc = ar.getSurfaceControl();
-            if (sc == null) continue;
-            final Task task = ar.getTask();
-            if (task == null) continue;
-            // If task isn't animating, then it means shell is animating activity directly (within
-            // task), so don't do any setup.
-            if (!containsChangeFor(task, targets)) continue;
-            final ChangeInfo change = mChanges.get(ar);
-            final Rect startBounds = change.mAbsoluteBounds;
-            Rect hintRect = null;
-            if (ar.getWindowingMode() == WINDOWING_MODE_PINNED && ar.pictureInPictureArgs != null
-                    && ar.pictureInPictureArgs.getSourceRectHint() != null) {
-                hintRect = ar.pictureInPictureArgs.getSourceRectHint();
-            }
-            if (hintRect == null) {
-                hintRect = new Rect(startBounds);
-                hintRect.offsetTo(0, 0);
-            }
-            final Rect endBounds = ar.getBounds();
-            final Rect taskEndBounds = task.getBounds();
-            // FA = final activity bounds (absolute)
-            // FT = final task bounds (absolute)
-            // SA = start activity bounds (absolute)
-            // H = source hint (relative to start activity bounds)
-            // We want to transform the activity so that when the task is at FT, H overlaps with FA
-
-            // This scales the activity such that the hint rect has the same dimensions
-            // as the final activity bounds.
-            float hintToEndScaleX = ((float) endBounds.width()) / ((float) hintRect.width());
-            float hintToEndScaleY = ((float) endBounds.height()) / ((float) hintRect.height());
-            // top-left needs to be (FA.tl - FT.tl) - H.tl * hintToEnd . H is relative to the
-            // activity; so, for example, if shrinking H to FA (hintToEnd < 1), then the tl of the
-            // shrunk SA is closer to H than expected, so we need to reduce how much we offset SA
-            // to get H.tl to match.
-            float startActPosInTaskEndX =
-                    (endBounds.left - taskEndBounds.left) - hintRect.left * hintToEndScaleX;
-            float startActPosInTaskEndY =
-                    (endBounds.top - taskEndBounds.top) - hintRect.top * hintToEndScaleY;
-            transact.setScale(sc, hintToEndScaleX, hintToEndScaleY);
-            transact.setPosition(sc, startActPosInTaskEndX, startActPosInTaskEndY);
-        }
-    }
-
     static boolean containsChangeFor(WindowContainer wc, ArrayList<ChangeInfo> list) {
         for (int i = list.size() - 1; i >= 0; --i) {
             if (list.get(i).mContainer == wc) return true;
@@ -1833,7 +1784,6 @@
 
         // Resolve the animating targets from the participants.
         mTargets = calculateTargets(mParticipants, mChanges);
-        prepareConfigAtEnd(transaction, mTargets);
 
         // Check whether the participants were animated from back navigation.
         mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets,
@@ -2669,6 +2619,11 @@
             if (reportIfNotTop(target)) {
                 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
                         "        keep as target %s", target);
+            } else if ((targetChange.mFlags & ChangeInfo.FLAG_CHANGE_CONFIG_AT_END) != 0) {
+                // config-at-end activities do not match the end-state, so they should be treated
+                // as independent.
+                ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+                        "        keep as cfg-at-end target %s", target);
             } else {
                 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
                         "        remove from targets %s", target);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index af57c84..95cf6bc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -36,7 +36,7 @@
 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
-import static android.window.flags.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
+import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
 
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index e0f24d8..31ca24c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -22,10 +22,12 @@
 import android.provider.AndroidDeviceConfig;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfigInterface;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -38,6 +40,10 @@
     private static final String KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST =
             "ignore_activity_orientation_request";
 
+    /** The packages that ignore {@link #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST}. */
+    private static final String KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST =
+            "opt_out_ignore_activity_orientation_request_list";
+
     /**
      * The minimum duration between gesture exclusion logging for a given window in
      * milliseconds.
@@ -65,6 +71,9 @@
     /** @see #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST */
     boolean mIgnoreActivityOrientationRequest;
 
+    /** @see #KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST */
+    private ArraySet<String> mOptOutIgnoreActivityOrientationRequestPackages;
+
     private final WindowManagerGlobalLock mGlobalLock;
     private final Runnable mUpdateSystemGestureExclusionCallback;
     private final DeviceConfigInterface mDeviceConfig;
@@ -97,6 +106,7 @@
         updateSystemGestureExclusionLimitDp();
         updateSystemGestureExcludedByPreQStickyImmersive();
         updateIgnoreActivityOrientationRequest();
+        updateOptOutIgnoreActivityOrientationRequestList();
     }
 
     private void onAndroidPropertiesChanged(DeviceConfig.Properties properties) {
@@ -138,6 +148,9 @@
                     case KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST:
                         updateIgnoreActivityOrientationRequest();
                         break;
+                    case KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST:
+                        updateOptOutIgnoreActivityOrientationRequestList();
+                        break;
                     default:
                         break;
                 }
@@ -169,6 +182,25 @@
                 KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST, false);
     }
 
+    private void updateOptOutIgnoreActivityOrientationRequestList() {
+        final String packageList = mDeviceConfig.getString(
+                DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST, "");
+        if (packageList.isEmpty()) {
+            mOptOutIgnoreActivityOrientationRequestPackages = null;
+            return;
+        }
+        mOptOutIgnoreActivityOrientationRequestPackages = new ArraySet<>();
+        mOptOutIgnoreActivityOrientationRequestPackages.addAll(
+                Arrays.asList(packageList.split(",")));
+    }
+
+    boolean isPackageOptOutIgnoreActivityOrientationRequest(String packageName) {
+        return mIgnoreActivityOrientationRequest
+                && mOptOutIgnoreActivityOrientationRequestPackages != null
+                && mOptOutIgnoreActivityOrientationRequestPackages.contains(packageName);
+    }
+
     void dump(PrintWriter pw) {
         pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
 
@@ -180,6 +212,10 @@
         pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
         pw.print("  "); pw.print(KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
         pw.print("="); pw.println(mIgnoreActivityOrientationRequest);
+        if (mOptOutIgnoreActivityOrientationRequestPackages != null) {
+            pw.print("  "); pw.print(KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST);
+            pw.print("="); pw.println(mOptOutIgnoreActivityOrientationRequestPackages);
+        }
         pw.println();
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 2229807..166d74b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -69,6 +69,7 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
@@ -1450,6 +1451,17 @@
                 task.setTrimmableFromRecents(hop.isTrimmableFromRecents());
                 break;
             }
+            case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: {
+                final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+                final Task task = container != null ? container.asTask() : null;
+                if (task == null || !task.isAttached()) {
+                    Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+                            + container);
+                    break;
+                }
+                task.setLaunchAdjacentDisabled(hop.isLaunchAdjacentDisabled());
+                break;
+            }
             case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: {
                 if (mService.mBackNavigationController.restoreBackNavigation()) {
                     effects |= TRANSACT_EFFECTS_LIFECYCLE;
@@ -1523,8 +1535,10 @@
                 final IBinder callerActivityToken = operation.getActivityToken();
                 final Intent activityIntent = operation.getActivityIntent();
                 final Bundle activityOptions = operation.getBundle();
+                final SafeActivityOptions safeOptions =
+                        SafeActivityOptions.fromBundle(activityOptions, caller.mPid, caller.mUid);
                 final int result = waitAsyncStart(() -> mService.getActivityStartController()
-                        .startActivityInTaskFragment(taskFragment, activityIntent, activityOptions,
+                        .startActivityInTaskFragment(taskFragment, activityIntent, safeOptions,
                                 callerActivityToken, caller.mUid, caller.mPid,
                                 errorCallbackToken));
                 if (!isStartResultSuccessful(result)) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 86adc19..1a107c2 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -133,7 +133,7 @@
     private int mRapidActivityLaunchCount;
 
     // all about the first app in the process
-    final ApplicationInfo mInfo;
+    volatile ApplicationInfo mInfo;
     final String mName;
     final int mUid;
 
@@ -1805,12 +1805,17 @@
             Configuration overrideConfig = new Configuration(r.getRequestedOverrideConfiguration());
             overrideConfig.assetsSeq = assetSeq;
             r.onRequestedOverrideConfigurationChanged(overrideConfig);
+            r.updateApplicationInfo(mInfo);
             if (r.isVisibleRequested()) {
                 r.ensureActivityConfiguration();
             }
         }
     }
 
+    public void updateApplicationInfo(ApplicationInfo aInfo) {
+        mInfo = aInfo;
+    }
+
     /**
      * This is called for sending {@link android.app.servertransaction.LaunchActivityItem}.
      * The caller must call {@link #setLastReportedConfiguration} if the delivered configuration
diff --git a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
index 6e8094a..1b42e13 100644
--- a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
+++ b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
@@ -159,22 +159,28 @@
 
     void onStart(WindowTracingDataSource.Config config) {
         if (config.mLogFrequency == WindowTracingLogFrequency.FRAME) {
+            Log.i(TAG, "Started session (frequency=FRAME, log level=" + config.mLogFrequency + ")");
             mCountSessionsOnFrame.incrementAndGet();
         } else if (config.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
+            Log.i(TAG, "Started session (frequency=TRANSACTION, log level="
+                    + config.mLogFrequency + ")");
             mCountSessionsOnTransaction.incrementAndGet();
         }
 
-        Log.i(TAG, "Started with logLevel: " + config.mLogLevel
-                + " logFrequency: " + config.mLogFrequency);
+        Log.i(TAG, getStatus());
+
         log(WHERE_START_TRACING);
     }
 
     void onStop(WindowTracingDataSource.Config config) {
         if (config.mLogFrequency == WindowTracingLogFrequency.FRAME) {
+            Log.i(TAG, "Stopped session (frequency=FRAME)");
             mCountSessionsOnFrame.decrementAndGet();
+            Log.i(TAG, "Stopped session (frequency=TRANSACTION)");
         } else if (config.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
             mCountSessionsOnTransaction.decrementAndGet();
         }
-        Log.i(TAG, "Stopped");
+
+        Log.i(TAG, getStatus());
     }
 }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index efca902..248ed1a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -337,6 +337,8 @@
     int32_t getMousePointerSpeed();
     void setPointerSpeed(int32_t speed);
     void setMousePointerAccelerationEnabled(ui::LogicalDisplayId displayId, bool enabled);
+    void setMouseReverseVerticalScrollingEnabled(bool enabled);
+    void setMouseSwapPrimaryButtonEnabled(bool enabled);
     void setTouchpadPointerSpeed(int32_t speed);
     void setTouchpadNaturalScrollingEnabled(bool enabled);
     void setTouchpadTapToClickEnabled(bool enabled);
@@ -482,6 +484,12 @@
         // True if stylus button reporting through motion events is enabled.
         bool stylusButtonMotionEventsEnabled{true};
 
+        // True if mouse vertical scrolling is reversed.
+        bool mouseReverseVerticalScrollingEnabled{false};
+
+        // True if the mouse primary button is swapped (left/right buttons).
+        bool mouseSwapPrimaryButtonEnabled{false};
+
         // The touchpad pointer speed, as a number from -7 (slowest) to 7 (fastest).
         int32_t touchpadPointerSpeed{0};
 
@@ -762,6 +770,10 @@
 
         outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId;
 
+        outConfig->mouseReverseVerticalScrollingEnabled =
+                mLocked.mouseReverseVerticalScrollingEnabled;
+        outConfig->mouseSwapPrimaryButtonEnabled = mLocked.mouseSwapPrimaryButtonEnabled;
+
         outConfig->touchpadPointerSpeed = mLocked.touchpadPointerSpeed;
         outConfig->touchpadNaturalScrollingEnabled = mLocked.touchpadNaturalScrollingEnabled;
         outConfig->touchpadTapToClickEnabled = mLocked.touchpadTapToClickEnabled;
@@ -1317,6 +1329,36 @@
     return mLocked.pointerSpeed;
 }
 
+void NativeInputManager::setMouseReverseVerticalScrollingEnabled(bool enabled) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        if (mLocked.mouseReverseVerticalScrollingEnabled == enabled) {
+            return;
+        }
+
+        mLocked.mouseReverseVerticalScrollingEnabled = enabled;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::Change::MOUSE_SETTINGS);
+}
+
+void NativeInputManager::setMouseSwapPrimaryButtonEnabled(bool enabled) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        if (mLocked.mouseSwapPrimaryButtonEnabled == enabled) {
+            return;
+        }
+
+        mLocked.mouseSwapPrimaryButtonEnabled = enabled;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::Change::MOUSE_SETTINGS);
+}
+
 void NativeInputManager::setPointerSpeed(int32_t speed) {
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -3002,6 +3044,18 @@
     return static_cast<jint>(im->getInputManager()->getReader().getLastUsedInputDeviceId());
 }
 
+static void nativeSetMouseReverseVerticalScrollingEnabled(JNIEnv* env, jobject nativeImplObj,
+                                                          bool enabled) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    im->setMouseReverseVerticalScrollingEnabled(enabled);
+}
+
+static void nativeSetMouseSwapPrimaryButtonEnabled(JNIEnv* env, jobject nativeImplObj,
+                                                   bool enabled) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    im->setMouseSwapPrimaryButtonEnabled(enabled);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -3048,6 +3102,9 @@
         {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed},
         {"setMousePointerAccelerationEnabled", "(IZ)V",
          (void*)nativeSetMousePointerAccelerationEnabled},
+        {"setMouseReverseVerticalScrollingEnabled", "(Z)V",
+         (void*)nativeSetMouseReverseVerticalScrollingEnabled},
+        {"setMouseSwapPrimaryButtonEnabled", "(Z)V", (void*)nativeSetMouseSwapPrimaryButtonEnabled},
         {"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed},
         {"setTouchpadNaturalScrollingEnabled", "(Z)V",
          (void*)nativeSetTouchpadNaturalScrollingEnabled},
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
index 2836d46..2add5b0 100644
--- a/services/core/jni/com_android_server_utils_AnrTimer.cpp
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -349,7 +349,7 @@
         return nullptr;
     }
 
-    // Return the currently watched pids.  The lock must be held.
+    // Return the currently watched pids as a comma-separated list.  The lock must be held.
     std::string watchedPidsLocked() const {
         if (watched_.size() == 0) return "none";
         bool first = true;
@@ -357,6 +357,7 @@
         for (auto i = watched_.cbegin(); i != watched_.cend(); i++) {
             if (first) {
                 result += StringPrintf("%d", *i);
+                first = false;
             } else {
                 result += StringPrintf(",%d", *i);
             }
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index bcd0b94..903d892 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -43,6 +43,8 @@
 static jmethodID sMethodIdOnComplete;
 static jclass sFrequencyProfileLegacyClass;
 static jmethodID sFrequencyProfileLegacyCtor;
+static jclass sFrequencyProfileClass;
+static jmethodID sFrequencyProfileCtor;
 static struct {
     jmethodID setCapabilities;
     jmethodID setSupportedEffects;
@@ -54,6 +56,7 @@
     jmethodID setCompositionSizeMax;
     jmethodID setQFactor;
     jmethodID setFrequencyProfileLegacy;
+    jmethodID setFrequencyProfile;
     jmethodID setMaxEnvelopeEffectSize;
     jmethodID setMinEnvelopeEffectControlPointDurationMillis;
     jmethodID setMaxEnvelopeEffectControlPointDurationMillis;
@@ -524,6 +527,40 @@
                           sVibratorInfoBuilderClassInfo.setFrequencyProfileLegacy,
                           frequencyProfileLegacy);
 
+    if (info.frequencyToOutputAccelerationMap.isOk()) {
+        size_t mapSize = info.frequencyToOutputAccelerationMap.value().size();
+
+        jfloatArray frequenciesHz = env->NewFloatArray(mapSize);
+        jfloatArray outputAccelerationsGs = env->NewFloatArray(mapSize);
+
+        jfloat* frequenciesHzPtr = env->GetFloatArrayElements(frequenciesHz, nullptr);
+        jfloat* outputAccelerationsGsPtr =
+                env->GetFloatArrayElements(outputAccelerationsGs, nullptr);
+
+        size_t i = 0;
+        for (auto const& dataEntry : info.frequencyToOutputAccelerationMap.value()) {
+            frequenciesHzPtr[i] = static_cast<jfloat>(dataEntry.frequencyHz);
+            outputAccelerationsGsPtr[i] = static_cast<jfloat>(dataEntry.maxOutputAccelerationGs);
+            i++;
+        }
+
+        // Release the float pointers
+        env->ReleaseFloatArrayElements(frequenciesHz, frequenciesHzPtr, 0);
+        env->ReleaseFloatArrayElements(outputAccelerationsGs, outputAccelerationsGsPtr, 0);
+
+        jobject frequencyProfile =
+                env->NewObject(sFrequencyProfileClass, sFrequencyProfileCtor, resonantFrequency,
+                               frequenciesHz, outputAccelerationsGs);
+
+        env->CallObjectMethod(vibratorInfoBuilder,
+                              sVibratorInfoBuilderClassInfo.setFrequencyProfile, frequencyProfile);
+
+        // Delete local references to avoid memory leaks
+        env->DeleteLocalRef(frequenciesHz);
+        env->DeleteLocalRef(outputAccelerationsGs);
+        env->DeleteLocalRef(frequencyProfile);
+    }
+
     return info.shouldRetry() ? JNI_FALSE : JNI_TRUE;
 }
 
@@ -574,6 +611,10 @@
     sFrequencyProfileLegacyCtor =
             GetMethodIDOrDie(env, sFrequencyProfileLegacyClass, "<init>", "(FFF[F)V");
 
+    jclass frequencyProfileClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyProfile");
+    sFrequencyProfileClass = static_cast<jclass>(env->NewGlobalRef(frequencyProfileClass));
+    sFrequencyProfileCtor = GetMethodIDOrDie(env, sFrequencyProfileClass, "<init>", "(F[F[F)V");
+
     jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
     sVibratorInfoBuilderClassInfo.setCapabilities =
             GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCapabilities",
@@ -606,6 +647,10 @@
             GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyProfileLegacy",
                              "(Landroid/os/VibratorInfo$FrequencyProfileLegacy;)"
                              "Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setFrequencyProfile =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyProfile",
+                             "(Landroid/os/VibratorInfo$FrequencyProfile;)"
+                             "Landroid/os/VibratorInfo$Builder;");
     sVibratorInfoBuilderClassInfo.setMaxEnvelopeEffectSize =
             GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setMaxEnvelopeEffectSize",
                              "(I)Landroid/os/VibratorInfo$Builder;");
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 776de2e..20c69ac 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -464,7 +464,7 @@
             <xs:annotation name="nonnull"/>
             <xs:annotation name="final"/>
         </xs:element>
-        <xs:element name="customAnimationRateSec" type="nonNegativeDecimal" minOccurs="0" maxOccurs="1">
+        <xs:element name="customAnimationRate" type="nonNegativeDecimal" minOccurs="0" maxOccurs="1">
             <xs:annotation name="nonnull"/>
             <xs:annotation name="final"/>
         </xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 110a5a2..a8f18b3 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -345,12 +345,12 @@
   public class PowerThrottlingConfig {
     ctor public PowerThrottlingConfig();
     method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed();
-    method @NonNull public final java.math.BigDecimal getCustomAnimationRateSec();
+    method @NonNull public final java.math.BigDecimal getCustomAnimationRate();
     method @NonNull public final java.math.BigInteger getPollingWindowMaxMillis();
     method @NonNull public final java.math.BigInteger getPollingWindowMinMillis();
     method public final java.util.List<com.android.server.display.config.PowerThrottlingMap> getPowerThrottlingMap();
     method public final void setBrightnessLowestCapAllowed(@NonNull java.math.BigDecimal);
-    method public final void setCustomAnimationRateSec(@NonNull java.math.BigDecimal);
+    method public final void setCustomAnimationRate(@NonNull java.math.BigDecimal);
     method public final void setPollingWindowMaxMillis(@NonNull java.math.BigInteger);
     method public final void setPollingWindowMinMillis(@NonNull java.math.BigInteger);
   }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2be999f..aca6f72 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11874,75 +11874,51 @@
             throw new IllegalArgumentException("Invalid package name: " + validationResult);
         }
 
-        if (Flags.setApplicationRestrictionsCoexistence()) {
-            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                    who,
-                    MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
-                    caller.getPackageName(),
-                    caller.getUserId()
-            );
-
+        final boolean isRoleHolder;
+        if (who != null) {
+            // DO or PO
+            Preconditions.checkCallAuthorization(
+                    (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
+            Preconditions.checkCallAuthorization(!parent,
+                    "DO or PO cannot call this on parent");
+            // Caller has opted to be treated as DPC (by passing a non-null who), so don't
+            // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
+            isRoleHolder = false;
+        } else {
+            // Delegates, or the DMRH. Only DMRH can call this on COPE parent
+            isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
+            if (parent) {
+                Preconditions.checkCallAuthorization(isRoleHolder);
+                Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
+                        "Role Holder can only operate parent app restriction on COPE devices");
+            } else {
+                Preconditions.checkCallAuthorization(isRoleHolder
+                        || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+            }
+        }
+        // DMRH caller uses policy engine, others still use legacy code path
+        if (isRoleHolder) {
+            EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
+                    caller.getPackageName());
+            int affectedUserId = parent
+                    ? getProfileParentId(caller.getUserId()) : caller.getUserId();
             if (restrictions == null || restrictions.isEmpty()) {
                 mDevicePolicyEngine.removeLocalPolicy(
                         PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
                         enforcingAdmin,
-                        caller.getUserId());
+                        affectedUserId);
             } else {
                 mDevicePolicyEngine.setLocalPolicy(
                         PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
                         enforcingAdmin,
                         new BundlePolicyValue(restrictions),
-                        caller.getUserId());
+                        affectedUserId);
             }
-            setBackwardsCompatibleAppRestrictions(
-                    caller, packageName, restrictions, caller.getUserHandle());
         } else {
-            final boolean isRoleHolder;
-            if (who != null) {
-                // DO or PO
-                Preconditions.checkCallAuthorization(
-                        (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
-                Preconditions.checkCallAuthorization(!parent,
-                        "DO or PO cannot call this on parent");
-                // Caller has opted to be treated as DPC (by passing a non-null who), so don't
-                // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
-                isRoleHolder = false;
-            } else {
-                // Delegates, or the DMRH. Only DMRH can call this on COPE parent
-                isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
-                if (parent) {
-                    Preconditions.checkCallAuthorization(isRoleHolder);
-                    Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
-                            "Role Holder can only operate parent app restriction on COPE devices");
-                } else {
-                    Preconditions.checkCallAuthorization(isRoleHolder
-                            || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
-                }
-            }
-            // DMRH caller uses policy engine, others still use legacy code path
-            if (isRoleHolder) {
-                EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
-                        caller.getPackageName());
-                int affectedUserId = parent
-                        ? getProfileParentId(caller.getUserId()) : caller.getUserId();
-                if (restrictions == null || restrictions.isEmpty()) {
-                    mDevicePolicyEngine.removeLocalPolicy(
-                            PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
-                            enforcingAdmin,
-                            affectedUserId);
-                } else {
-                    mDevicePolicyEngine.setLocalPolicy(
-                            PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
-                            enforcingAdmin,
-                            new BundlePolicyValue(restrictions),
-                            affectedUserId);
-                }
-            } else {
-                mInjector.binderWithCleanCallingIdentity(() -> {
-                    mUserManager.setApplicationRestrictions(packageName, restrictions,
-                            caller.getUserHandle());
-                });
-            }
+            mInjector.binderWithCleanCallingIdentity(() -> {
+                mUserManager.setApplicationRestrictions(packageName, restrictions,
+                        caller.getUserHandle());
+            });
         }
 
         DevicePolicyEventLogger
@@ -11953,31 +11929,6 @@
                 .write();
     }
 
-    /**
-     * Set app restrictions in user manager for DPC callers only to keep backwards compatibility
-     * for the old getApplicationRestrictions API.
-     */
-    private void setBackwardsCompatibleAppRestrictions(
-            CallerIdentity caller, String packageName, Bundle restrictions, UserHandle userHandle) {
-        if ((caller.hasAdminComponent() && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
-                || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS))) {
-            Bundle restrictionsToApply = restrictions == null || restrictions.isEmpty()
-                    ? getAppRestrictionsSetByAnyAdmin(packageName, userHandle)
-                    : restrictions;
-            mInjector.binderWithCleanCallingIdentity(() -> {
-                mUserManager.setApplicationRestrictions(packageName, restrictionsToApply,
-                        userHandle);
-            });
-        } else {
-            // Notify package of changes via an intent - only sent to explicitly registered
-            // receivers. Sending here because For DPCs, this is being sent in UMS.
-            final Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
-            changeIntent.setPackage(packageName);
-            changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            mContext.sendBroadcastAsUser(changeIntent, userHandle);
-        }
-    }
-
     private Bundle getAppRestrictionsSetByAnyAdmin(String packageName, UserHandle userHandle) {
         LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
                 mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
@@ -13257,68 +13208,47 @@
             String packageName, boolean parent) {
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
 
-        // IMPORTANT: The code behind the if branch is OUTDATED and requires additional work before
-        // enabling the feature flag below.
-        // TODO(b/369141952): Update DPM.getApplicationRestrictions coexistence code
-        if (Flags.setApplicationRestrictionsCoexistence()) {
-            EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
-                    who,
-                    MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
-                    caller.getPackageName(),
-                    caller.getUserId()
-            );
-
+        final boolean isRoleHolder;
+        if (who != null) {
+            // Caller is DO or PO. They cannot call this on parent
+            Preconditions.checkCallAuthorization(!parent
+                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
+            // Caller has opted to be treated as DPC (by passing a non-null who), so don't
+            // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
+            isRoleHolder = false;
+        } else {
+            // Caller is delegates or the DMRH. Only DMRH can call this on parent
+            isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
+            if (parent) {
+                Preconditions.checkCallAuthorization(isRoleHolder);
+                Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
+                        "Role Holder can only operate parent app restriction on COPE devices");
+            } else {
+                Preconditions.checkCallAuthorization(isRoleHolder
+                        || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+            }
+        }
+        if (isRoleHolder) {
+            EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
+                    caller.getPackageName());
+            int affectedUserId = parent
+                    ? getProfileParentId(caller.getUserId()) : caller.getUserId();
             LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
                     mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
                             PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
-                            caller.getUserId());
-            if (policies.isEmpty() || !policies.containsKey(enforcingAdmin)) {
+                            affectedUserId);
+            if (!policies.containsKey(enforcingAdmin)) {
                 return Bundle.EMPTY;
             }
             return policies.get(enforcingAdmin).getValue();
         } else {
-            final boolean isRoleHolder;
-            if (who != null) {
-                // Caller is DO or PO. They cannot call this on parent
-                Preconditions.checkCallAuthorization(!parent
-                        && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
-                // Caller has opted to be treated as DPC (by passing a non-null who), so don't
-                // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
-                isRoleHolder = false;
-            } else {
-                // Caller is delegates or the DMRH. Only DMRH can call this on parent
-                isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
-                if (parent) {
-                    Preconditions.checkCallAuthorization(isRoleHolder);
-                    Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
-                            "Role Holder can only operate parent app restriction on COPE devices");
-                } else {
-                    Preconditions.checkCallAuthorization(isRoleHolder
-                            || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
-                }
-            }
-            if (isRoleHolder) {
-                EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
-                        caller.getPackageName());
-                int affectedUserId = parent
-                        ? getProfileParentId(caller.getUserId()) : caller.getUserId();
-                LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
-                        mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
-                                PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
-                                affectedUserId);
-                if (!policies.containsKey(enforcingAdmin)) {
-                    return Bundle.EMPTY;
-                }
-                return policies.get(enforcingAdmin).getValue();
-            } else {
-                return mInjector.binderWithCleanCallingIdentity(() -> {
-                    Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
-                            caller.getUserHandle());
-                    // if no restrictions were saved, mUserManager.getApplicationRestrictions
-                    // returns null, but DPM method should return an empty Bundle as per JavaDoc
-                    return bundle != null ? bundle : Bundle.EMPTY;
-                });
-            }
+            return mInjector.binderWithCleanCallingIdentity(() -> {
+                Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
+                        caller.getUserHandle());
+                // if no restrictions were saved, mUserManager.getApplicationRestrictions
+                // returns null, but DPM method should return an empty Bundle as per JavaDoc
+                return bundle != null ? bundle : Bundle.EMPTY;
+            });
         }
     }
 
@@ -16885,6 +16815,9 @@
             }
         }
         EnforcingAdmin enforcingAdmin;
+
+        // TODO(b/370472975): enable when we stop policy enforecer callback from blocking the main
+        //  thread
         if (Flags.setPermissionGrantStateCoexistence()) {
             enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     admin,
@@ -16910,54 +16843,7 @@
                 callback.sendResult(null);
                 return;
             }
-        } else {
-            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
-                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
-                    || isFinancedDeviceOwner(caller)))
-                    || (caller.hasPackage() && isCallerDelegate(caller,
-                    DELEGATION_PERMISSION_GRANT)));
-            if (SENSOR_PERMISSIONS.contains(permission)
-                    && grantState == PERMISSION_GRANT_STATE_GRANTED
-                    && !canAdminGrantSensorsPermissions()) {
-                if (mInjector.isChangeEnabled(THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS,
-                        caller.getPackageName(), caller.getUserId())) {
-                    throw new SecurityException(
-                            "Caller not permitted to grant sensor permissions.");
-                } else {
-                    Slogf.e(LOG_TAG, "Caller attempted to grant sensor permissions but denied");
-                    // This is to match the legacy behaviour.
-                    callback.sendResult(Bundle.EMPTY);
-                    return;
-                }
-            }
-            synchronized (getLockObject()) {
-                long ident = mInjector.binderClearCallingIdentity();
-                try {
-                    boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
-                            >= android.os.Build.VERSION_CODES.Q;
-                    if (!isPostQAdmin) {
-                        // Legacy admins assume that they cannot control pre-M apps
-                        if (getTargetSdk(packageName, caller.getUserId())
-                                < android.os.Build.VERSION_CODES.M) {
-                            callback.sendResult(null);
-                            return;
-                        }
-                    }
-                    if (!isRuntimePermission(permission)) {
-                        callback.sendResult(null);
-                        return;
-                    }
-                } catch (SecurityException e) {
-                    Slogf.e(LOG_TAG, "Could not set permission grant state", e);
-                    callback.sendResult(null);
-                } finally {
-                    mInjector.binderRestoreCallingIdentity(ident);
-                }
-            }
-        }
-        // TODO(b/278710449): enable when we stop policy enforecer callback from blocking the main
-        //  thread
-        if (false) {
+
             // TODO(b/266924257): decide how to handle the internal state if the package doesn't
             //  exist, or the permission isn't requested by the app, because we could end up with
             //  inconsistent state between the policy engine and package manager. Also a package
@@ -16983,11 +16869,43 @@
                 callback.sendResult(null);
             }
         } else {
+            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+                    || isFinancedDeviceOwner(caller)))
+                    || (caller.hasPackage() && isCallerDelegate(caller,
+                    DELEGATION_PERMISSION_GRANT)));
+            if (SENSOR_PERMISSIONS.contains(permission)
+                    && grantState == PERMISSION_GRANT_STATE_GRANTED
+                    && !canAdminGrantSensorsPermissions()) {
+                if (mInjector.isChangeEnabled(THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS,
+                        caller.getPackageName(), caller.getUserId())) {
+                    throw new SecurityException(
+                            "Caller not permitted to grant sensor permissions.");
+                } else {
+                    Slogf.e(LOG_TAG, "Caller attempted to grant sensor permissions but denied");
+                    // This is to match the legacy behaviour.
+                    callback.sendResult(Bundle.EMPTY);
+                    return;
+                }
+            }
             synchronized (getLockObject()) {
                 long ident = mInjector.binderClearCallingIdentity();
+                boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
+                        >= android.os.Build.VERSION_CODES.Q;
+
                 try {
-                    boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
-                            >= android.os.Build.VERSION_CODES.Q;
+                    if (!isPostQAdmin) {
+                        // Legacy admins assume that they cannot control pre-M apps
+                        if (getTargetSdk(packageName, caller.getUserId())
+                                < android.os.Build.VERSION_CODES.M) {
+                            callback.sendResult(null);
+                            return;
+                        }
+                    }
+                    if (!isRuntimePermission(permission)) {
+                        callback.sendResult(null);
+                        return;
+                    }
                     if (grantState == PERMISSION_GRANT_STATE_GRANTED
                             || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
                             || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
@@ -17009,7 +16927,6 @@
                     }
                 } catch (SecurityException e) {
                     Slogf.e(LOG_TAG, "Could not set permission grant state", e);
-
                     callback.sendResult(null);
                 } finally {
                     mInjector.binderRestoreCallingIdentity(ident);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7ea1dcd..0b7ce75 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -159,7 +159,7 @@
 import com.android.server.contextualsearch.ContextualSearchManagerService;
 import com.android.server.coverage.CoverageService;
 import com.android.server.cpu.CpuMonitorService;
-import com.android.server.crashrecovery.CrashRecoveryModule;
+import com.android.server.crashrecovery.CrashRecoveryAdaptor;
 import com.android.server.credentials.CredentialManagerService;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -246,6 +246,7 @@
 import com.android.server.security.FileIntegrityService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
+import com.android.server.security.advancedprotection.AdvancedProtectionService;
 import com.android.server.security.rkp.RemoteProvisioningService;
 import com.android.server.selinux.SelinuxAuditLogsService;
 import com.android.server.sensorprivacy.SensorPrivacyService;
@@ -327,8 +328,6 @@
      * Implementation class names for services in the {@code SYSTEMSERVERCLASSPATH}
      * from {@code PRODUCT_SYSTEM_SERVER_JARS} that are *not* in {@code services.jar}.
      */
-    private static final String ARC_NETWORK_SERVICE_CLASS =
-            "com.android.server.arc.net.ArcNetworkService";
     private static final String ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS =
             "com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService";
     private static final String ARC_SYSTEM_HEALTH_SERVICE =
@@ -1230,12 +1229,12 @@
 
         if (!Flags.refactorCrashrecovery()) {
             // Initialize RescueParty.
-            RescueParty.registerHealthObserver(mSystemContext);
+            CrashRecoveryAdaptor.rescuePartyRegisterHealthObserver(mSystemContext);
             if (!Flags.recoverabilityDetection()) {
                 // Now that we have the bare essentials of the OS up and running, take
                 // note that we just booted, which might send out a rescue party if
                 // we're stuck in a runtime restart loop.
-                PackageWatchdog.getInstance(mSystemContext).noteBoot();
+                CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
             }
         }
 
@@ -1617,7 +1616,7 @@
             mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
             t.traceEnd();
 
-            if (android.app.supervision.flags.Flags.supervisionApi()) {
+            if (!isWatch && android.app.supervision.flags.Flags.supervisionApi()) {
                 t.traceBegin("StartSupervisionService");
                 mSystemServiceManager.startService(SupervisionService.Lifecycle.class);
                 t.traceEnd();
@@ -1760,6 +1759,12 @@
                 mSystemServiceManager.startService(AppFunctionManagerService.class);
                 t.traceEnd();
             }
+
+            if (android.security.Flags.aapmApi()) {
+                t.traceBegin("StartAdvancedProtectionService");
+                mSystemServiceManager.startService(AdvancedProtectionService.Lifecycle.class);
+                t.traceEnd();
+            }
         } catch (Throwable e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service");
@@ -2101,24 +2106,13 @@
             if (context.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_WIFI)) {
                 // Wifi Service must be started first for wifi-related services.
-                if (!isArc) {
-                    t.traceBegin("StartWifi");
-                    mSystemServiceManager.startServiceFromJar(
-                            WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
-                    t.traceEnd();
-                    t.traceBegin("StartWifiScanning");
-                    mSystemServiceManager.startServiceFromJar(
-                            WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
-                    t.traceEnd();
-                }
-            }
-
-            // ARC - ArcNetworkService registers the ARC network stack and replaces the
-            // stock WiFi service in both ARC++ container and ARCVM. Always starts the ARC network
-            // stack regardless of whether FEATURE_WIFI is enabled/disabled (b/254755875).
-            if (isArc) {
-                t.traceBegin("StartArcNetworking");
-                mSystemServiceManager.startService(ARC_NETWORK_SERVICE_CLASS);
+                t.traceBegin("StartWifi");
+                mSystemServiceManager.startServiceFromJar(
+                        WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+                t.traceEnd();
+                t.traceBegin("StartWifiScanning");
+                mSystemServiceManager.startServiceFromJar(
+                        WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
             }
 
@@ -2979,7 +2973,7 @@
 
         if (Flags.refactorCrashrecovery()) {
             t.traceBegin("StartCrashRecoveryModule");
-            mSystemServiceManager.startService(CrashRecoveryModule.Lifecycle.class);
+            CrashRecoveryAdaptor.initializeCrashrecoveryModuleService(mSystemServiceManager);
             t.traceEnd();
         } else {
             if (Flags.recoverabilityDetection()) {
@@ -2987,7 +2981,7 @@
                 // with package watchdog.
                 // Note that we just booted, which might send out a rescue party if we're stuck in a
                 // runtime restart loop.
-                PackageWatchdog.getInstance(mSystemContext).noteBoot();
+                CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
             }
         }
 
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 0990691..e2ac22d 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -10,6 +10,14 @@
 }
 
 flag {
+    name: "remove_java_service_manager_cache"
+    namespace: "system_performance"
+    description: "This flag turns off Java's Service Manager caching mechanism."
+    bug: "333854840"
+    is_fixed_read_only: true
+}
+
+flag {
      name: "remove_text_service"
      namespace: "wear_frameworks"
      description: "Remove TextServiceManagerService on Wear"
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index cc340c0..891c334 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -58,6 +58,7 @@
 import android.os.UserManager;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -1737,6 +1738,11 @@
         pw.decreaseIndent();
     }
 
+    @Override
+    protected void onUnhandledException(int code, int flags, Exception e) {
+        Slog.wtf(TAG, "Uncaught exception in AudioService: " + code + ", " + flags, e);
+    }
+
     @GuardedBy("mUsbMidiLock")
     private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
         String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
index ff901af..30df4c8 100644
--- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
@@ -96,6 +96,8 @@
         } catch (SecurityException ex) {
             Slog.e(TAG, "Query call log failed: " + ex);
             return false;
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception when querying call log.", e);
         }
         return hasResults;
     }
diff --git a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
index 2505abf2..2bd9d87 100644
--- a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
@@ -151,9 +151,11 @@
                 found = true;
             }
         } catch (SQLiteException exception) {
-            Slog.w("SQLite exception when querying contacts.", exception);
+            Slog.w(TAG, "SQLite exception when querying contacts.", exception);
         } catch (IllegalArgumentException exception) {
-            Slog.w("Illegal Argument exception when querying contacts.", exception);
+            Slog.w(TAG, "Illegal Argument exception when querying contacts.", exception);
+        } catch (Exception exception) {
+            Slog.e(TAG, "Exception when querying contacts.", exception);
         }
         if (found && lookupKey != null && hasPhoneNumber) {
             return queryPhoneNumber(lookupKey);
@@ -181,6 +183,8 @@
                     mPhoneNumber = cursor.getString(phoneNumIdx);
                 }
             }
+        } catch (Exception exception) {
+            Slog.e(TAG, "Exception when querying contact phone number.", exception);
         }
         return true;
     }
diff --git a/services/people/java/com/android/server/people/data/MmsQueryHelper.java b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
index 39dba9c..414a523 100644
--- a/services/people/java/com/android/server/people/data/MmsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
@@ -100,6 +100,8 @@
                     }
                 }
             }
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception when querying MMS table.", e);
         } finally {
             Binder.defaultBlockingForCurrentThread();
         }
@@ -133,6 +135,8 @@
                     address = cursor.getString(addrIndex);
                 }
             }
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception when querying MMS address table.", e);
         }
         if (!Mms.isPhoneNumber(address)) {
             return null;
diff --git a/services/people/java/com/android/server/people/data/SmsQueryHelper.java b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
index a5eb3a5..f8ff3ab 100644
--- a/services/people/java/com/android/server/people/data/SmsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
@@ -98,6 +98,8 @@
                     }
                 }
             }
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception when querying SMS table.", e);
         } finally {
             Binder.defaultBlockingForCurrentThread();
         }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMenuControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMenuControllerTest.java
new file mode 100644
index 0000000..02dc86b
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMenuControllerTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static com.android.server.inputmethod.InputMethodMenuControllerNew.getMenuItems;
+import static com.android.server.inputmethod.InputMethodMenuControllerNew.getSelectedIndex;
+import static com.android.server.inputmethod.InputMethodSubtypeSwitchingControllerTest.addTestImeSubtypeListItems;
+import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_INDEX;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.inputmethod.Flags;
+
+import com.android.server.inputmethod.InputMethodMenuControllerNew.DividerItem;
+import com.android.server.inputmethod.InputMethodMenuControllerNew.HeaderItem;
+import com.android.server.inputmethod.InputMethodMenuControllerNew.SubtypeItem;
+import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RequiresFlagsEnabled(Flags.FLAG_IME_SWITCHER_REVAMP)
+public class InputMethodMenuControllerTest {
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    /** Verifies that getMenuItems maintains the same order and information from the given items. */
+    @Test
+    public void testGetMenuItems() {
+        final var items = new ArrayList<ImeSubtypeListItem>();
+        addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+                List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "SimpleIme", "SimpleIme",
+                null, true /* supportsSwitchingToNextInputMethod */);
+
+        final var menuItems = getMenuItems(items);
+
+        int itemsIndex = 0;
+
+        for (int i = 0; i < menuItems.size(); i++) {
+            final var menuItem = menuItems.get(i);
+            if (menuItem instanceof SubtypeItem subtypeItem) {
+                final var item = items.get(itemsIndex);
+
+                assertWithMessage("IME name does not match").that(subtypeItem.mImeName)
+                        .isEqualTo(item.mImeName);
+                assertWithMessage("Subtype name does not match").that(subtypeItem.mSubtypeName)
+                        .isEqualTo(item.mSubtypeName);
+                assertWithMessage("InputMethodInfo does not match").that(subtypeItem.mImi)
+                        .isEqualTo(item.mImi);
+                assertWithMessage("Subtype index does not match").that(subtypeItem.mSubtypeIndex)
+                        .isEqualTo(item.mSubtypeIndex);
+
+                itemsIndex++;
+            }
+        }
+
+        assertWithMessage("Items list was not fully traversed").that(itemsIndex)
+                .isEqualTo(items.size());
+    }
+
+    /**
+     * Verifies that getMenuItems does not add a header or divider if all the items belong to
+     * a single input method.
+     */
+    @Test
+    public void testGetMenuItemsNoHeaderOrDividerForSingleInputMethod() {
+        final var items = new ArrayList<ImeSubtypeListItem>();
+        addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+                List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+
+        final var menuItems = getMenuItems(items);
+
+        assertThat(menuItems.stream()
+                .filter(item -> item instanceof HeaderItem || item instanceof DividerItem).toList())
+                .isEmpty();
+    }
+
+    /**
+     * Verifies that getMenuItems only adds headers for item groups with at least two items,
+     * or with a single item with a subtype name.
+     */
+    @Test
+    public void testGetMenuItemsHeaders() {
+        final var items = new ArrayList<ImeSubtypeListItem>();
+        addTestImeSubtypeListItems(items, "DefaultIme", "DefaultIme",
+                null, true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+                List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "ItalianIme", "ItalianIme",
+                List.of("it"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "SimpleIme", "SimpleIme",
+                null, true /* supportsSwitchingToNextInputMethod */);
+
+        final var menuItems = getMenuItems(items);
+
+        assertWithMessage("Must have menu items").that(menuItems).isNotEmpty();
+
+        final var headersAndDividers = menuItems.stream()
+                .filter(item -> item instanceof HeaderItem || item instanceof DividerItem)
+                .toList();
+
+        assertWithMessage("Must have header and divider items").that(headersAndDividers).hasSize(5);
+
+        assertWithMessage("First group has no header")
+                .that(menuItems.getFirst()).isInstanceOf(SubtypeItem.class);
+        assertWithMessage("Group with multiple items has divider")
+                .that(headersAndDividers.get(0)).isInstanceOf(DividerItem.class);
+        assertWithMessage("Group with multiple items has header")
+                .that(headersAndDividers.get(1)).isInstanceOf(HeaderItem.class);
+        assertWithMessage("Group with single item with subtype name has divider")
+                .that(headersAndDividers.get(2)).isInstanceOf(DividerItem.class);
+        assertWithMessage("Group with single item with subtype name has header")
+                .that(headersAndDividers.get(3)).isInstanceOf(HeaderItem.class);
+        assertWithMessage("Group with single item without subtype name has divider only")
+                .that(headersAndDividers.get(4)).isInstanceOf(DividerItem.class);
+    }
+
+    /** Verifies that getMenuItems adds a divider before every header except the first one. */
+    @Test
+    public void testGetMenuItemsDivider() {
+        final var items = new ArrayList<ImeSubtypeListItem>();
+        addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+                List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "ItalianIme", "ItalianIme",
+                List.of("it"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "SimpleIme", "SimpleIme",
+                null, true /* supportsSwitchingToNextInputMethod */);
+
+        final var menuItems = getMenuItems(items);
+
+        assertWithMessage("First item is a header")
+                .that(menuItems.getFirst()).isInstanceOf(HeaderItem.class);
+        assertWithMessage("Last item is a subtype")
+                .that(menuItems.getLast()).isInstanceOf(SubtypeItem.class);
+
+        for (int i = 0; i < menuItems.size(); i++) {
+            final var item = menuItems.get(i);
+            if (item instanceof HeaderItem && i > 0) {
+                final var prevItem = menuItems.get(i - 1);
+                assertWithMessage("The item before a header should be a divider")
+                        .that(prevItem).isInstanceOf(DividerItem.class);
+            } else if (item instanceof DividerItem && i < menuItems.size() - 1) {
+                final var nextItem = menuItems.get(i + 1);
+                assertWithMessage("The item after a divider should be a header or subtype")
+                        .that(nextItem instanceof HeaderItem || nextItem instanceof SubtypeItem)
+                        .isTrue();
+            }
+        }
+    }
+
+    /**
+     * Verifies that getSelectedIndex returns the matching item when the selected subtype is given.
+     */
+    @Test
+    public void testGetSelectedIndexWithSelectedSubtype() {
+        final var items = new ArrayList<ImeSubtypeListItem>();
+        addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+                List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "SimpleIme", "SimpleIme",
+                List.of("it", "jp", "pt"),  true /* supportsSwitchingToNextInputMethod */);
+
+        final var simpleImeId = items.get(2).mImi.getId();
+        final var menuItems = getMenuItems(items);
+
+        final int selectedIndex = getSelectedIndex(menuItems, simpleImeId, 1);
+        // Two headers + one divider + three items
+        assertThat(selectedIndex).isEqualTo(6);
+    }
+
+    /**
+     * Verifies that getSelectedIndex returns the first item of the selected input method,
+     * when no selected subtype is given.
+     */
+    @Test
+    public void testGetSelectedIndexWithoutSelectedSubtype() {
+        final var items = new ArrayList<ImeSubtypeListItem>();
+        addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+                List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "SimpleIme", "SimpleIme",
+                List.of("it", "jp", "pt"),  true /* supportsSwitchingToNextInputMethod */);
+
+        final var simpleImeId = items.get(2).mImi.getId();
+        final var menuItems = getMenuItems(items);
+
+        final int selectedIndex = getSelectedIndex(menuItems, simpleImeId, NOT_A_SUBTYPE_INDEX);
+
+        // Two headers + one divider + two items
+        assertThat(selectedIndex).isEqualTo(5);
+    }
+
+    /**
+     * Verifies that getSelectedIndex will return the item of the selected input method that has
+     * no subtype, when this is the first one reached, regardless of the given selected subtype.
+     */
+    @Test
+    public void getSelectedIndexNoSubtype() {
+        final var items = new ArrayList<ImeSubtypeListItem>();
+        addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+                List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+        addTestImeSubtypeListItems(items, "SimpleIme", "SimpleIme",
+                null,  true /* supportsSwitchingToNextInputMethod */);
+
+        final var simpleImeId = items.get(2).mImi.getId();
+        final var menuItems = getMenuItems(items);
+
+        final int selectedIndex = getSelectedIndex(menuItems, simpleImeId, 1);
+
+        // One header + one divider + two items
+        assertThat(selectedIndex).isEqualTo(4);
+    }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 770451c..a804f24 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -75,7 +75,7 @@
                 .build();
     }
 
-    private static void addTestImeSubtypeListItems(@NonNull List<ImeSubtypeListItem> items,
+    static void addTestImeSubtypeListItems(@NonNull List<ImeSubtypeListItem> items,
             @NonNull String imeName, @NonNull String imeLabel,
             @Nullable List<String> subtypeLocales, boolean supportsSwitchingToNextInputMethod) {
         final ApplicationInfo ai = new ApplicationInfo();
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
index cbca434..1e89359 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
@@ -21,6 +21,7 @@
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
+import android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
 import android.content.pm.PackageManager
 import android.content.pm.verify.domain.DomainSet
 import android.os.Parcel
@@ -33,6 +34,11 @@
 import com.android.server.pm.verify.pkg.VerifierController
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
 import libcore.io.IoUtils
 import org.junit.Before
 import org.junit.Rule
@@ -46,11 +52,6 @@
 import org.mockito.MockitoAnnotations
 import org.xmlpull.v1.XmlPullParser
 import org.xmlpull.v1.XmlPullParserException
-import java.io.File
-import java.io.FileInputStream
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
 
 @Presubmit
 class PackageInstallerSessionTest {
@@ -197,7 +198,8 @@
             /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
             /* stagedSessionErrorMessage */ "some error",
             /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")),
-            /* VerifierController */ mock(VerifierController::class.java)
+            /* VerifierController */ mock(VerifierController::class.java),
+            VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
         )
     }
 
@@ -289,6 +291,7 @@
         assertThat(expected.installerPackageName).isEqualTo(actual.installerPackageName)
         assertThat(expected.isMultiPackage).isEqualTo(actual.isMultiPackage)
         assertThat(expected.isStaged).isEqualTo(actual.isStaged)
+        assertThat(expected.forceVerification).isEqualTo(actual.forceVerification)
     }
 
     private fun assertEquals(
@@ -339,6 +342,7 @@
         assertThat(expected.childSessionIds).asList()
             .containsExactlyElementsIn(actual.childSessionIds.toList())
         assertThat(expected.preVerifiedDomains).isEqualTo(actual.preVerifiedDomains)
+        assertThat(expected.verificationPolicy).isEqualTo(actual.verificationPolicy)
     }
 
     private fun assertInstallSourcesEquivalent(expected: InstallSource, actual: InstallSource) {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java
index fa076db..787fb5a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerificationStatusTrackerTest.java
@@ -62,7 +62,7 @@
                 .thenReturn(TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS)
                 .thenReturn(TEST_REQUEST_START_TIME + TEST_MAX_TIMEOUT_DURATION_MILLIS - 100)
                 .thenReturn(TEST_REQUEST_START_TIME + TEST_MAX_TIMEOUT_DURATION_MILLIS);
-        mTracker = new VerificationStatusTracker(TEST_PACKAGE_NAME, TEST_TIMEOUT_DURATION_MILLIS,
+        mTracker = new VerificationStatusTracker(TEST_TIMEOUT_DURATION_MILLIS,
                 TEST_MAX_TIMEOUT_DURATION_MILLIS, mInjector);
     }
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
index be094b0..2461798 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.pm.verify.pkg;
 
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -92,6 +95,9 @@
     private static final long TEST_TIMEOUT_DURATION_MILLIS = TimeUnit.MINUTES.toMillis(1);
     private static final long TEST_MAX_TIMEOUT_DURATION_MILLIS =
             TimeUnit.MINUTES.toMillis(10);
+    private static final long TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS =
+            TimeUnit.SECONDS.toMillis(10);
+    private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
 
     private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>();
     private final PersistableBundle mTestExtensionParams = new PersistableBundle();
@@ -116,7 +122,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mInjector.isVerifierInstalled(any(Computer.class), anyInt())).thenReturn(true);
+        when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn(
+                TEST_VERIFIER_COMPONENT_NAME.getPackageName());
         when(mInjector.getRemoteService(
                 any(Computer.class), any(Context.class), anyInt(), any(Handler.class)
         )).thenReturn(new Pair<>(mMockServiceConnector, TEST_VERIFIER_COMPONENT_NAME));
@@ -124,6 +131,9 @@
                 TEST_TIMEOUT_DURATION_MILLIS);
         when(mInjector.getMaxVerificationExtendedTimeoutMillis()).thenReturn(
                 TEST_MAX_TIMEOUT_DURATION_MILLIS);
+        when(mInjector.getVerifierConnectionTimeoutMillis()).thenReturn(
+                TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS
+        );
         // Mock time forward as the code continues to check for the current time
         when(mInjector.getCurrentTimeMillis())
                 .thenReturn(TEST_REQUEST_START_TIME)
@@ -150,21 +160,21 @@
 
     @Test
     public void testVerifierNotInstalled() {
-        when(mInjector.isVerifierInstalled(any(Computer.class), anyInt())).thenReturn(false);
+        when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn(null);
         when(mInjector.getRemoteService(
                 any(Computer.class), any(Context.class), anyInt(), any(Handler.class)
         )).thenReturn(null);
-        assertThat(mVerifierController.isVerifierInstalled(mSnapshotSupplier, 0)).isFalse();
+        assertThat(mVerifierController.getVerifierPackageName(mSnapshotSupplier, 0)).isNull();
         assertThat(mVerifierController.bindToVerifierServiceIfNeeded(mSnapshotSupplier, 0))
                 .isFalse();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isFalse();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isFalse();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isFalse();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isFalse();
         verifyZeroInteractions(mSessionCallback);
     }
 
@@ -176,7 +186,7 @@
 
     @Test
     public void testVerifierAvailableButNotConnected() {
-        assertThat(mVerifierController.isVerifierInstalled(mSnapshotSupplier, 0)).isTrue();
+        assertThat(mVerifierController.getVerifierPackageName(mSnapshotSupplier, 0)).isNotNull();
         when(mInjector.getRemoteService(
                 any(Computer.class), any(Context.class), anyInt(), any(Handler.class)
         )).thenReturn(null);
@@ -200,24 +210,24 @@
         ServiceConnector.ServiceLifecycleCallbacks<IVerifierService> callbacks = captor.getValue();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService, times(1)).onVerificationRequired(any(VerificationSession.class));
         callbacks.onBinderDied();
         // Test that nothing crashes if the service connection is lost
-        assertThat(mVerifierController.isVerifierInstalled(mSnapshotSupplier, 0)).isTrue();
+        assertThat(mVerifierController.getVerifierPackageName(mSnapshotSupplier, 0)).isNotNull();
         mVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME);
         mVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME);
         mVerifierController.notifyVerificationTimeout(TEST_ID);
         verifyNoMoreInteractions(mMockService);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         mVerifierController.notifyVerificationTimeout(TEST_ID);
         verify(mMockService, times(1)).onVerificationTimeout(eq(TEST_ID));
     }
@@ -226,8 +236,8 @@
     public void testNotifyPackageNameAvailable() throws Exception {
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         mVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME);
         verify(mMockService).onPackageNameAvailable(eq(TEST_PACKAGE_NAME));
     }
@@ -236,8 +246,8 @@
     public void testNotifyVerificationCancelled() throws Exception {
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         mVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME);
         verify(mMockService).onVerificationCancelled(eq(TEST_PACKAGE_NAME));
     }
@@ -248,8 +258,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         assertThat(session.getId()).isEqualTo(TEST_ID);
@@ -276,8 +286,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         verify(mMockService).onVerificationRetry(captor.capture());
         VerificationSession session = captor.getValue();
         assertThat(session.getId()).isEqualTo(TEST_ID);
@@ -302,8 +312,8 @@
     public void testNotifyVerificationTimeout() throws Exception {
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         mVerifierController.notifyVerificationTimeout(TEST_ID);
         verify(mMockService).onVerificationTimeout(eq(TEST_ID));
     }
@@ -320,8 +330,8 @@
         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
         verify(mSessionCallback, times(1)).onTimeout();
         verify(mInjector, times(2)).getCurrentTimeMillis();
@@ -339,8 +349,8 @@
                 .thenAnswer(i -> true);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
         verify(mSessionCallback, times(1)).onTimeout();
         verify(mInjector, times(2)).getCurrentTimeMillis();
@@ -350,8 +360,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         verify(mMockService).onVerificationRetry(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -367,8 +377,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         session.reportVerificationIncomplete(VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN);
@@ -383,8 +393,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -401,8 +411,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder()
@@ -421,8 +431,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -439,8 +449,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false);
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS;
@@ -456,8 +466,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false);
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS;
@@ -493,10 +503,27 @@
                 .thenReturn(TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS + 1);
         mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false);
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
         verify(mHandler, times(3)).sendMessageAtTime(any(Message.class), anyLong());
         verify(mInjector, times(6)).getCurrentTimeMillis();
         verify(mSessionCallback, times(1)).onTimeout();
     }
+
+    @Test
+    public void testPolicyOverride() throws Exception {
+        ArgumentCaptor<VerificationSession> captor =
+                ArgumentCaptor.forClass(VerificationSession.class);
+        mVerifierController.startVerificationSession(
+                mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
+        verify(mMockService).onVerificationRequired(captor.capture());
+        VerificationSession session = captor.getValue();
+        final int policy = VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+        when(mSessionCallback.setVerificationPolicy(eq(policy))).thenReturn(true);
+        assertThat(session.setVerificationPolicy(policy)).isTrue();
+        assertThat(session.getVerificationPolicy()).isEqualTo(policy);
+        verify(mSessionCallback, times(1)).setVerificationPolicy(eq(policy));
+    }
 }
diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp
index c841643..836f90b 100644
--- a/services/tests/appfunctions/Android.bp
+++ b/services/tests/appfunctions/Android.bp
@@ -36,7 +36,9 @@
         "androidx.test.core",
         "androidx.test.runner",
         "androidx.test.ext.truth",
+        "androidx.core_core-ktx",
         "kotlin-test",
+        "kotlinx_coroutines_test",
         "platform-test-annotations",
         "services.appfunctions",
         "servicestests-core-utils",
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
new file mode 100644
index 0000000..a69e902
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appfunctions
+
+import android.app.appfunctions.flags.Flags
+import android.content.Context
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+class AppFunctionManagerServiceImplTest {
+    @get:Rule
+    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+    private val context: Context
+        get() = ApplicationProvider.getApplicationContext()
+
+    private val serviceImpl = AppFunctionManagerServiceImpl(context)
+
+    @Test
+    fun testGetLockForPackage_samePackage() {
+        val packageName = "com.example.app"
+        val lock1 = serviceImpl.getLockForPackage(packageName)
+        val lock2 = serviceImpl.getLockForPackage(packageName)
+
+        // Assert that the same lock object is returned for the same package name
+        assertThat(lock1).isEqualTo(lock2)
+    }
+
+    @Test
+    fun testGetLockForPackage_differentPackages() {
+        val packageName1 = "com.example.app1"
+        val packageName2 = "com.example.app2"
+        val lock1 = serviceImpl.getLockForPackage(packageName1)
+        val lock2 = serviceImpl.getLockForPackage(packageName2)
+
+        // Assert that different lock objects are returned for different package names
+        assertThat(lock1).isNotEqualTo(lock2)
+    }
+
+    @Ignore("Hard to deterministically trigger the garbage collector.")
+    @Test
+    fun testWeakReference_garbageCollected_differentLockAfterGC() = runTest {
+        // Create a large number of temporary objects to put pressure on the GC
+        val tempObjects = MutableList<Any?>(10000000) { Any() }
+        var callingPackage: String? = "com.example.app"
+        var lock1: Any? = serviceImpl.getLockForPackage(callingPackage)
+        callingPackage = null // Set the key to null
+        val lock1Hash = lock1.hashCode()
+        lock1 = null
+
+        // Create memory pressure
+        repeat(3) {
+            for (i in 1..100) {
+                "a".repeat(10000)
+            }
+            System.gc() // Suggest garbage collection
+            System.runFinalization()
+        }
+        // Get the lock again - it should be a different object now
+        val lock2 = serviceImpl.getLockForPackage("com.example.app")
+        // Assert that the lock objects are different
+        assertThat(lock1Hash).isNotEqualTo(lock2.hashCode())
+    }
+}
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
index bc64e15..96fb453 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
@@ -34,7 +34,6 @@
 import android.util.Log
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.internal.infra.AndroidFuture
-import com.android.server.appfunctions.FutureAppSearchSession.FutureSearchResults
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.atomic.AtomicBoolean
 import org.junit.Test
@@ -392,6 +391,10 @@
                             return AndroidFuture.completedFuture(mutableListOf())
                         }
                     }
+
+                    override fun close() {
+                        Log.d("FakeRuntimeMetadataSearchSession", "Closing session")
+                    }
                 }
             return AndroidFuture.completedFuture(futureSearchResults)
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 3976ea4..2220f43 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -265,7 +265,7 @@
                 mDisplayDeviceConfig.getPowerThrottlingConfigData();
         assertNotNull(powerThrottlingConfigData);
         assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA);
-        assertEquals(15f, powerThrottlingConfigData.customAnimationRateSec, SMALL_DELTA);
+        assertEquals(15f, powerThrottlingConfigData.customAnimationRate, SMALL_DELTA);
         assertEquals(20000, powerThrottlingConfigData.pollingWindowMaxMillis);
         assertEquals(10000, powerThrottlingConfigData.pollingWindowMinMillis);
     }
@@ -1299,7 +1299,7 @@
     private String getPowerThrottlingConfig() {
         return  "<powerThrottlingConfig >\n"
                 +       "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n"
-                +       "<customAnimationRateSec>15</customAnimationRateSec>\n"
+                +       "<customAnimationRate>15</customAnimationRate>\n"
                 +       "<pollingWindowMaxMillis>20000</pollingWindowMaxMillis>\n"
                 +       "<pollingWindowMinMillis>10000</pollingWindowMinMillis>\n"
                 +       "<powerThrottlingMap>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 1a1c8e5..b917af4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -25,6 +25,7 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
 import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
@@ -119,6 +120,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.test.mock.MockContentResolver;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Spline;
 import android.view.ContentRecordingSession;
@@ -137,11 +139,14 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
+import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.internal.util.test.LocalServiceKeeperRule;
 import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.am.BatteryStatsService;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.display.DisplayManagerService.DeviceStateListener;
 import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -152,6 +157,7 @@
 import com.android.server.display.notifications.DisplayNotificationManager;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.lights.LightsManager;
+import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.sensors.SensorManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -375,6 +381,10 @@
     @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
     @Mock DisplayManagerFlags mMockFlags;
 
+    @Mock WindowManagerPolicy mMockedWindowManagerPolicy;
+
+    @Mock IBatteryStats mMockedBatteryStats;
+
     @Rule
     public final ExtendedMockitoRule mExtendedMockitoRule =
             new ExtendedMockitoRule.Builder(this)
@@ -1068,9 +1078,9 @@
                 firstDisplayId);
     }
 
-    /** Tests that the virtual device is created in a device display group. */
+    /** Tests that a trusted virtual display is created in a device display group. */
     @Test
-    public void createVirtualDisplay_addsDisplaysToDeviceDisplayGroups() throws Exception {
+    public void createVirtualDisplay_addsTrustedDisplaysToDeviceDisplayGroups() throws Exception {
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
 
@@ -1081,12 +1091,16 @@
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
         when(virtualDevice.getDeviceId()).thenReturn(1);
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
+
+        when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
         // Create a first virtual display. A display group should be created for this display on the
         // virtual device.
         final VirtualDisplayConfig.Builder builder1 =
                 new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
-                        .setUniqueId("uniqueId --- device display group 1");
-
+                        .setUniqueId("uniqueId --- device display group")
+                        .setFlags(VIRTUAL_DISPLAY_FLAG_TRUSTED);
         int displayId1 =
                 localService.createVirtualDisplay(
                         builder1.build(),
@@ -1097,12 +1111,14 @@
         verify(mMockProjectionService, never()).setContentRecordingSession(any(),
                 nullable(IMediaProjection.class));
         int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
+        assertNotEquals(displayGroupId1, Display.DEFAULT_DISPLAY_GROUP);
 
         // Create a second virtual display. This should be added to the previously created display
         // group.
         final VirtualDisplayConfig.Builder builder2 =
                 new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
-                        .setUniqueId("uniqueId --- device display group 1");
+                        .setUniqueId("uniqueId --- device display group")
+                        .setFlags(VIRTUAL_DISPLAY_FLAG_TRUSTED);
 
         int displayId2 =
                 localService.createVirtualDisplay(
@@ -1121,6 +1137,36 @@
                 displayGroupId2);
     }
 
+    /** Tests that an untrusted virtual display is created in the default display group. */
+    @Test
+    public void createVirtualDisplay_addsUntrustedDisplayToDefaultDisplayGroups() throws Exception {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+
+        registerDefaultDisplays(displayManager);
+        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
+        when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
+        // Create the virtual display. It is untrusted, so it should go into the default group.
+        final VirtualDisplayConfig.Builder builder =
+                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
+                        .setUniqueId("uniqueId --- device display group");
+
+        int displayId =
+                localService.createVirtualDisplay(
+                        builder.build(),
+                        mMockAppToken /* callback */,
+                        virtualDevice /* virtualDeviceToken */,
+                        mock(DisplayWindowPolicyController.class),
+                        PACKAGE_NAME);
+        verify(mMockProjectionService, never()).setContentRecordingSession(any(),
+                nullable(IMediaProjection.class));
+        int displayGroupId = localService.getDisplayInfo(displayId).displayGroupId;
+        assertEquals(displayGroupId, Display.DEFAULT_DISPLAY_GROUP);
+    }
+
     /**
      * Tests that the virtual display is not added to the device display group when
      * VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is set.
@@ -1138,11 +1184,15 @@
         when(virtualDevice.getDeviceId()).thenReturn(1);
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
+        when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
         // Create a first virtual display. A display group should be created for this display on the
         // virtual device.
         final VirtualDisplayConfig.Builder builder1 =
                 new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
-                        .setUniqueId("uniqueId --- device display group");
+                        .setUniqueId("uniqueId --- device display group")
+                        .setFlags(VIRTUAL_DISPLAY_FLAG_TRUSTED);
 
         int displayId1 =
                 localService.createVirtualDisplay(
@@ -1154,12 +1204,14 @@
         verify(mMockProjectionService, never()).setContentRecordingSession(any(),
                 nullable(IMediaProjection.class));
         int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
+        assertNotEquals(displayGroupId1, Display.DEFAULT_DISPLAY_GROUP);
 
         // Create a second virtual display. With the flag VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP,
         // the display should not be added to the previously created display group.
         final VirtualDisplayConfig.Builder builder2 =
                 new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
-                        .setFlags(VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP)
+                        .setFlags(VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP
+                                | VIRTUAL_DISPLAY_FLAG_TRUSTED)
                         .setUniqueId("uniqueId --- own display group");
 
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
@@ -1174,6 +1226,7 @@
         verify(mMockProjectionService, never()).setContentRecordingSession(any(),
                 nullable(IMediaProjection.class));
         int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId;
+        assertNotEquals(displayGroupId2, Display.DEFAULT_DISPLAY_GROUP);
 
         assertNotEquals(
                 "Display 1 should be in the device display group and display 2 in its own display"
@@ -1208,7 +1261,8 @@
         final VirtualDisplayConfig deviceDisplayGroupDisplayConfig =
                 new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
                         .setUniqueId("uniqueId --- device display group 1")
-                        .setFlags(VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED)
+                        .setFlags(VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED
+                                | VIRTUAL_DISPLAY_FLAG_TRUSTED)
                         .build();
 
         int deviceDisplayGroupDisplayId =
@@ -1235,6 +1289,7 @@
                         .setUniqueId("uniqueId --- own display group 1")
                         .setFlags(
                                 VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED
+                                        | VIRTUAL_DISPLAY_FLAG_TRUSTED
                                         | VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP)
                         .build();
 
@@ -1603,6 +1658,70 @@
     }
 
     @Test
+    public void testGetDisplayIdsByGroupsIds() throws Exception {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        // Create display 1
+        FakeDisplayDevice displayDevice1 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1);
+        final int groupId1 = display1.getDisplayInfoLocked().displayGroupId;
+        // Create display 2
+        FakeDisplayDevice displayDevice2 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2);
+        final int groupId2 = display2.getDisplayInfoLocked().displayGroupId;
+        // Both displays should be in the same display group
+        assertEquals(groupId1, groupId2);
+        final int[] displayIds = new int[]{
+                display1.getDisplayIdLocked(), display2.getDisplayIdLocked()};
+        final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>();
+        expectedDisplayGroups.put(groupId1, displayIds);
+
+        final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds();
+
+        for (int i = 0; i < expectedDisplayGroups.size(); i++) {
+            final int groupId = expectedDisplayGroups.keyAt(i);
+            assertTrue(displayGroups.contains(groupId));
+            assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId));
+        }
+    }
+
+    @Test
+    public void testGetDisplayIdsByGroupsIds_multipleDisplayGroups() throws Exception {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        // Create display 1
+        FakeDisplayDevice displayDevice1 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1);
+        final int groupId1 = display1.getDisplayInfoLocked().displayGroupId;
+        // Create display 2
+        FakeDisplayDevice displayDevice2 =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+        LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2);
+        final int groupId2 = display2.getDisplayInfoLocked().displayGroupId;
+        // Both displays should be in different display groups
+        assertNotEquals(groupId1, groupId2);
+        final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>();
+        expectedDisplayGroups.put(groupId1, new int[]{display1.getDisplayIdLocked()});
+        expectedDisplayGroups.put(groupId2, new int[]{display2.getDisplayIdLocked()});
+
+        final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds();
+
+        assertEquals(expectedDisplayGroups.size(), displayGroups.size());
+        for (int i = 0; i < expectedDisplayGroups.size(); i++) {
+            final int groupId = expectedDisplayGroups.keyAt(i);
+            assertTrue(displayGroups.contains(groupId));
+            assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId));
+        }
+    }
+
+    @Test
     public void testCreateVirtualDisplay_isValidProjection_notValid()
             throws RemoteException {
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
@@ -1852,7 +1971,7 @@
 
     /**
      * Tests that specifying VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is allowed when the permission
-     * ADD_TRUSTED_DISPLAY is granted.
+     * ADD_TRUSTED_DISPLAY is granted and that display is not in the default display group.
      */
     @Test
     public void testOwnDisplayGroup_allowCreationWithAddTrustedDisplayPermission()
@@ -1881,6 +2000,9 @@
         DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
         assertNotNull(ddi);
         assertNotEquals(0, ddi.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+
+        int displayGroupId = bs.getDisplayInfo(displayId).displayGroupId;
+        assertNotEquals(displayGroupId, Display.DEFAULT_DISPLAY_GROUP);
     }
 
     /**
@@ -1915,11 +2037,11 @@
     }
 
     /**
-     * Tests that specifying VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is allowed when called with
-     * a virtual device, even if ADD_TRUSTED_DISPLAY is not granted.
+     * Tests that specifying VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is not allowed when called with
+     * a virtual device, if ADD_TRUSTED_DISPLAY is not granted.
      */
     @Test
-    public void testOwnDisplayGroup_allowCreationWithVirtualDevice() throws Exception {
+    public void testOwnDisplayGroup_disallowCreationWithVirtualDevice() throws Exception {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
@@ -1940,16 +2062,16 @@
         when(virtualDevice.getDeviceId()).thenReturn(1);
         when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
-        int displayId = localService.createVirtualDisplay(builder.build(),
-                mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */,
-                mock(DisplayWindowPolicyController.class), PACKAGE_NAME);
-        verify(mMockProjectionService, never()).setContentRecordingSession(any(),
-                nullable(IMediaProjection.class));
-        performTraversalInternal(displayManager);
-        displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
-        DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
-        assertNotNull(ddi);
-        assertNotEquals(0, ddi.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+        try {
+            localService.createVirtualDisplay(builder.build(),
+                    mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */,
+                    mock(DisplayWindowPolicyController.class), PACKAGE_NAME);
+            fail("Creating virtual display with VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP without "
+                    + "ADD_TRUSTED_DISPLAY permission should throw SecurityException even if "
+                    + "called with a virtual device.");
+        } catch (SecurityException e) {
+            // SecurityException is expected
+        }
     }
 
     /**
@@ -2711,6 +2833,78 @@
     }
 
     @Test
+    public void testConnectExternalDisplay_withDisplayManagement_allowsEnableAndDisableDisplay() {
+        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+        when(mMockFlags.isApplyDisplayChangedDuringDisplayAddedEnabled()).thenReturn(true);
+        manageDisplaysPermission(/* granted= */ true);
+        LocalServices.addService(WindowManagerPolicy.class, mMockedWindowManagerPolicy);
+        BatteryStatsService.overrideService(mMockedBatteryStats);
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        DisplayManagerService.BinderService bs = displayManager.new BinderService();
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+        bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+        localService.registerDisplayGroupListener(callback);
+
+        // Create default display device
+        callback.expectsEvent(EVENT_DISPLAY_ADDED);
+        FakeDisplayDevice defaultDisplayDevice =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        callback.waitForExpectedEvent();
+        LogicalDisplay defaultDisplay =
+                logicalDisplayMapper.getDisplayLocked(defaultDisplayDevice, false);
+        callback.clear();
+
+        // Create external display device
+        callback.expectsEvent(EVENT_DISPLAY_ADDED);
+        FakeDisplayDevice displayDevice =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, new float[]{60f},
+                        Display.TYPE_EXTERNAL, callback);
+        callback.waitForExpectedEvent();
+        LogicalDisplay display =
+                logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+        assertThat(display.isEnabledLocked()).isTrue();
+        callback.clear();
+
+        callback.expectsEvent(FakeDisplayDevice.COMMITTED_DISPLAY_STATE_CHANGED);
+        initDisplayPowerController(localService);
+        // Initial power request, should have happened from PowerManagerService.
+        localService.requestPowerState(defaultDisplay.getDisplayInfoLocked().displayGroupId,
+                new DisplayManagerInternal.DisplayPowerRequest(),
+                /*waitForNegativeProximity=*/ false);
+        localService.requestPowerState(display.getDisplayInfoLocked().displayGroupId,
+                new DisplayManagerInternal.DisplayPowerRequest(),
+                /*waitForNegativeProximity=*/ false);
+        displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        callback.waitForExpectedEvent();
+
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+                .isEqualTo(Display.STATE_OFF);
+        assertThat(display.isEnabledLocked()).isFalse();
+        assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_CONNECTED,
+                EVENT_DISPLAY_REMOVED).inOrder();
+
+        int displayId = display.getDisplayIdLocked();
+        boolean enabled = display.isEnabledLocked();
+        assertThat(enabled).isFalse();
+
+        for (int i = 0; i < 9; i++) {
+            callback.expectsEvent(FakeDisplayDevice.COMMITTED_DISPLAY_STATE_CHANGED);
+            enabled = !enabled;
+            Slog.d("DisplayManagerServiceTest", "enabled=" + enabled);
+            displayManager.enableConnectedDisplay(displayId, enabled);
+            callback.waitForExpectedEvent();
+            assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+                    .isEqualTo(enabled ? Display.STATE_ON : Display.STATE_OFF);
+            assertThat(defaultDisplayDevice.getDisplayDeviceInfoLocked().committedState)
+                    .isEqualTo(Display.STATE_ON);
+        }
+        callback.expectsEvent(FakeDisplayDevice.COMMITTED_DISPLAY_STATE_CHANGED);
+        callback.waitForNonExpectedEvent();
+    }
+
+    @Test
     public void testConnectInternalDisplay_withDisplayManagement_shouldConnectAndAddDisplay() {
         when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         manageDisplaysPermission(/* granted= */ true);
@@ -2746,7 +2940,7 @@
         displayManager.setDisplayState(display.getDisplayIdLocked(), Display.STATE_ON);
 
         assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
-                .isEqualTo(Display.STATE_ON);
+                .isEqualTo(Display.STATE_UNKNOWN);
 
         assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(),
                 Display.STATE_OFF)).isTrue();
@@ -2779,7 +2973,7 @@
         var displayId = display.getDisplayIdLocked();
 
         assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
-                .isEqualTo(Display.STATE_ON);
+                .isEqualTo(Display.STATE_UNKNOWN);
 
         assertThrows(SecurityException.class,
                 () -> bs.requestDisplayPower(displayId, Display.STATE_UNKNOWN));
@@ -3737,7 +3931,16 @@
                                                       float[] refreshRates,
                                                       float[] vsyncRates,
                                                       int displayType) {
+        return createFakeDisplayDevice(displayManager, refreshRates, vsyncRates, displayType, null);
+    }
+
+    private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
+                                                      float[] refreshRates,
+                                                      float[] vsyncRates,
+                                                      int displayType,
+                                                      FakeDisplayManagerCallback callback) {
         FakeDisplayDevice displayDevice = new FakeDisplayDevice();
+        displayDevice.setCallback(callback);
         DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
         int width = 100;
         int height = 200;
@@ -3747,6 +3950,7 @@
                     new Display.Mode(i + 1, width, height, refreshRates[i], vsyncRates[i],
                             new float[0], new int[0]);
         }
+        displayDeviceInfo.name = "" + displayType;
         displayDeviceInfo.modeId = 1;
         displayDeviceInfo.type = displayType;
         displayDeviceInfo.renderFrameRate = displayDeviceInfo.supportedModes[0].getRefreshRate();
@@ -3854,6 +4058,19 @@
             }
         }
 
+        void waitForNonExpectedEvent() {
+            waitForNonExpectedEvent(Duration.ofSeconds(1));
+        }
+
+        void waitForNonExpectedEvent(Duration timeout) {
+            try {
+                assertWithMessage("Non Expected '" + mExpectedEvent + "'")
+                        .that(mLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)).isFalse();
+            } catch (InterruptedException ex) {
+                throw new AssertionError("Waiting for expected event interrupted", ex);
+            }
+        }
+
         private void eventSeen(String event) {
             if (event.equals(mExpectedEvent)) {
                 mLatch.countDown();
@@ -3924,8 +4141,10 @@
     }
 
     private class FakeDisplayDevice extends DisplayDevice {
+        static final String COMMITTED_DISPLAY_STATE_CHANGED = "requestDisplayStateLocked";
         private DisplayDeviceInfo mDisplayDeviceInfo;
         private Display.Mode mPreferredMode = new Display.Mode.Builder().build();
+        private FakeDisplayManagerCallback mCallback;
 
         FakeDisplayDevice() {
             super(mMockDisplayAdapter, /* displayToken= */ null, /* uniqueId= */ "", mContext);
@@ -3933,7 +4152,7 @@
 
         public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
             mDisplayDeviceInfo = displayDeviceInfo;
-            mDisplayDeviceInfo.committedState = Display.STATE_ON;
+            mDisplayDeviceInfo.committedState = Display.STATE_UNKNOWN;
         }
 
         @Override
@@ -3970,7 +4189,23 @@
                 final float brightnessState,
                 final float sdrBrightnessState,
                 @Nullable DisplayOffloadSessionImpl displayOffloadSession) {
-            return () -> mDisplayDeviceInfo.committedState = state;
+            return () -> {
+                Slog.d("FakeDisplayDevice", mDisplayDeviceInfo.name
+                        + " new state=" + state);
+                if (state != mDisplayDeviceInfo.committedState) {
+                    Slog.d("FakeDisplayDevice", mDisplayDeviceInfo.name
+                            + " mDisplayDeviceInfo.committedState="
+                            + mDisplayDeviceInfo.committedState + " set to " + state);
+                    mDisplayDeviceInfo.committedState = state;
+                    if (mCallback != null) {
+                        mCallback.eventSeen(COMMITTED_DISPLAY_STATE_CHANGED);
+                    }
+                }
+            };
+        }
+
+        void setCallback(FakeDisplayManagerCallback callback) {
+            this.mCallback = callback;
         }
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 01b2d3e..fdf6b80 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2343,6 +2343,41 @@
         }
     }
 
+    @Test
+    public void stylusUsageStarted_disablesAutomaticBrightnessStrategy() {
+        when(mDisplayManagerFlagsMock.isBlockAutobrightnessChangesOnStylusUsage())
+                .thenReturn(true);
+        when(mDisplayManagerFlagsMock.isRefactorDisplayPowerControllerEnabled())
+                .thenReturn(true);
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        advanceTime(2);
+        clearInvocations(mHolder.automaticBrightnessController);
+        mHolder.dpc.stylusGestureStarted(2000000);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+        verify(mHolder.automaticBrightnessController, times(0))
+                .getAutomaticScreenBrightness(any());
+
+        // Stylus usage timed out, hence autobrightness is now enabled back again
+        advanceTime(6);
+        verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(null);
+
+        // Ideally we should be able to assert against new BrightnessEvent(Display.DEFAULT_DISPLAY),
+        // but because brightnessEvent has the mTime field which refers to the current time,
+        // asserting against that is non-trivial
+        verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(
+                any(BrightnessEvent.class));
+    }
+
     /**
      * Creates a mock and registers it to {@link LocalServices}.
      */
@@ -2406,6 +2441,7 @@
                 .thenReturn(new int[0]);
         when(displayDeviceConfigMock.getDefaultDozeBrightness())
                 .thenReturn(DEFAULT_DOZE_BRIGHTNESS);
+        when(displayDeviceConfigMock.getIdleStylusTimeoutMillis()).thenReturn(5);
 
         when(displayDeviceConfigMock.getBrightnessRampFastDecrease())
                 .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
index f728168..782262d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.display.DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED;
 import static android.view.Display.TYPE_EXTERNAL;
+import static android.view.Display.TYPE_INTERNAL;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -36,6 +37,7 @@
 import android.os.IThermalService;
 import android.os.RemoteException;
 import android.os.Temperature;
+import android.view.Display;
 import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
@@ -97,6 +99,8 @@
     @Mock
     private LogicalDisplay mMockedLogicalDisplay;
     @Mock
+    private LogicalDisplay mMockedDefaultDisplay;
+    @Mock
     private DisplayNotificationManager mMockedDisplayNotificationManager;
     @Mock
     private ExternalDisplayStatsService mMockedExternalDisplayStatsService;
@@ -141,6 +145,15 @@
         when(mMockedLogicalDisplay.getDisplayInfoLocked()).thenReturn(mockedLogicalDisplayInfo);
         when(mMockedLogicalDisplayMapper.getDisplayLocked(EXTERNAL_DISPLAY_ID)).thenReturn(
                 mMockedLogicalDisplay);
+
+        // Initialize default logical display
+        when(mMockedDefaultDisplay.getDisplayIdLocked()).thenReturn(Display.DEFAULT_DISPLAY);
+        when(mMockedDefaultDisplay.isEnabledLocked()).thenReturn(true);
+        final var mockedDefaultDisplayInfo = new DisplayInfo();
+        mockedDefaultDisplayInfo.type = TYPE_INTERNAL;
+        when(mMockedDefaultDisplay.getDisplayInfoLocked()).thenReturn(mockedDefaultDisplayInfo);
+        when(mMockedLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY)).thenReturn(
+                mMockedDefaultDisplay);
     }
 
     @Test
@@ -293,6 +306,52 @@
         verify(mMockedLogicalDisplayMapper, never()).forEachLocked(any());
     }
 
+    @Test
+    public void testMirroringAlwaysConfirmedByUser_flagDisabled() {
+        when(mMockedFlags.isWaitingConfirmationBeforeMirroringEnabled()).thenReturn(false);
+        assertThat(mExternalDisplayPolicy.isDisplayReadyForMirroring(EXTERNAL_DISPLAY_ID)).isTrue();
+    }
+
+    @Test
+    public void testMirroringConfirmed_afterBootForEnabledDisplay() {
+        when(mMockedFlags.isWaitingConfirmationBeforeMirroringEnabled()).thenReturn(true);
+        mExternalDisplayPolicy.onBootCompleted();
+        assertThat(mExternalDisplayPolicy.isDisplayReadyForMirroring(EXTERNAL_DISPLAY_ID))
+                .isTrue();
+    }
+
+    @Test
+    public void testMirroringNotConfirmed_afterBootForDisabledDisplay() {
+        when(mMockedFlags.isWaitingConfirmationBeforeMirroringEnabled()).thenReturn(true);
+        mExternalDisplayPolicy.onBootCompleted();
+        when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(false);
+        assertThat(mExternalDisplayPolicy.isDisplayReadyForMirroring(EXTERNAL_DISPLAY_ID))
+                .isFalse();
+    }
+
+    @Test
+    public void testMirroringNeverConfirmed_forNonExternalDisplays() {
+        when(mMockedFlags.isWaitingConfirmationBeforeMirroringEnabled()).thenReturn(true);
+        mExternalDisplayPolicy.onBootCompleted();
+        assertThat(mExternalDisplayPolicy.isDisplayReadyForMirroring(Display.DEFAULT_DISPLAY))
+                .isFalse();
+    }
+
+    @Test
+    public void testMirroringNeverConfirmed_forNonExistingDisplays() {
+        when(mMockedFlags.isWaitingConfirmationBeforeMirroringEnabled()).thenReturn(true);
+        mExternalDisplayPolicy.onBootCompleted();
+        assertThat(mExternalDisplayPolicy.isDisplayReadyForMirroring(Display.INVALID_DISPLAY))
+                .isFalse();
+    }
+
+    @Test
+    public void testMirroringNeverConfirmed_duringBoot() {
+        when(mMockedFlags.isWaitingConfirmationBeforeMirroringEnabled()).thenReturn(true);
+        assertThat(mExternalDisplayPolicy.isDisplayReadyForMirroring(EXTERNAL_DISPLAY_ID))
+                .isFalse();
+    }
+
     private void setTemperature(final IThermalEventListener thermalEventListener,
             final List<Temperature> temperature) throws RemoteException {
         for (var t : temperature) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 1729ad5..b6da3ae 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -51,6 +51,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -121,6 +122,8 @@
             Set.of(DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE), Collections.emptySet());
     private static final DeviceState DEVICE_STATE_OPEN = createDeviceState(2, "Two",
             Set.of(DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE), Collections.emptySet());
+    private static final DeviceState DEVICE_STATE_EMULATED = createDeviceState(3, "Three",
+            Set.of(DeviceState.PROPERTY_EMULATED_ONLY), Collections.emptySet());
     private static final int FLAG_GO_TO_SLEEP_ON_FOLD = 0;
     private static final int FLAG_GO_TO_SLEEP_FLAG_SOFT_SLEEP = 2;
     private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1;
@@ -207,8 +210,8 @@
         when(mResourcesMock.getIntArray(
                 com.android.internal.R.array.config_deviceStatesOnWhichToSleep))
                 .thenReturn(new int[]{0});
-        when(mSyntheticModeManagerMock.createAppSupportedModes(any(), any())).thenAnswer(
-                AdditionalAnswers.returnsSecondArg());
+        when(mSyntheticModeManagerMock.createAppSupportedModes(any(), any(), anyBoolean()))
+                .thenAnswer(AdditionalAnswers.returnsSecondArg());
 
         when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(false);
         mLooper = new TestLooper();
@@ -686,6 +689,14 @@
     }
 
     @Test
+    public void testDeviceShouldNotBeWokenWhenExitingEmulatedState() {
+        assertFalse(mLogicalDisplayMapper.shouldDeviceBeWoken(DEVICE_STATE_OPEN,
+                DEVICE_STATE_EMULATED,
+                /* isInteractive= */false,
+                /* isBootCompleted= */true));
+    }
+
+    @Test
     public void testDeviceShouldBePutToSleep() {
         assertTrue(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
                 DEVICE_STATE_OPEN,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index 8936f06..b002a1f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -87,7 +88,7 @@
         mDisplayDeviceInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
                 DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 60)};
         when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo);
-        when(mSyntheticModeManager.createAppSupportedModes(any(), any())).thenAnswer(
+        when(mSyntheticModeManager.createAppSupportedModes(any(), any(), anyBoolean())).thenAnswer(
                 AdditionalAnswers.returnsSecondArg());
 
         // Disable binder caches in this process.
@@ -582,7 +583,8 @@
         Display.Mode[] appSupportedModes = new Display.Mode[] {new Display.Mode(OTHER_MODE_ID,
                 DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 45)};
         when(mSyntheticModeManager.createAppSupportedModes(
-                any(), eq(mDisplayDeviceInfo.supportedModes))).thenReturn(appSupportedModes);
+                any(), eq(mDisplayDeviceInfo.supportedModes), anyBoolean()))
+                .thenReturn(appSupportedModes);
 
         mLogicalDisplay.updateLocked(mDeviceRepo, mSyntheticModeManager);
         DisplayInfo info = mLogicalDisplay.getDisplayInfoLocked();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
index 5676a38..6d14065 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -459,7 +459,6 @@
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
         newInjector.setReadStream(bais);
         newDataStore.loadIfNeeded();
-        assertNotNull(newDataStore.getUserPreferredRefreshRate(testDisplayDevice));
         assertEquals(85.3f, mDataStore.getUserPreferredRefreshRate(testDisplayDevice), 01.f);
         assertEquals(85.3f, newDataStore.getUserPreferredRefreshRate(testDisplayDevice), 0.1f);
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index 04b79b4..d93ee84 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -74,7 +74,7 @@
 
     @Test
     public void setModifierDoesntSetIfModifierIsBeyondExtremes() {
-        int extremeModifier = 0x40; // equal to BrightnessReason.MODIFIER_MASK * 2
+        int extremeModifier = 0x80;
 
         // reset modifier
         mBrightnessReason.setModifier(0);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c069875..b3baa5d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -125,7 +125,7 @@
                 DisplayManagerInternal.DisplayOffloadSession.class));
         verify(displayBrightnessStrategy).updateBrightness(
                 eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS,
-                        /* userSetBrightnessChanged= */ false)));
+                        /* userSetBrightnessChanged= */ false, /* isStylusBeingUsed */ false)));
         assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(),
                 displayBrightnessStrategy);
     }
@@ -559,4 +559,11 @@
                 displayDeviceConfig, handler, brightnessMappingStrategy, isDisplayEnabled,
                 leadDisplayId);
     }
+
+    @Test
+    public void setStylusBeingUsed_setsStylusInUseState() {
+        assertFalse(mDisplayBrightnessController.isStylusBeingUsed());
+        mDisplayBrightnessController.setStylusBeingUsed(true);
+        assertTrue(mDisplayBrightnessController.isStylusBeingUsed());
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index a44c517..fe15051 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -68,6 +68,8 @@
 @RunWith(AndroidJUnit4.class)
 public final class DisplayBrightnessStrategySelectorTest {
     private static final boolean DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING = false;
+    private static final boolean STYLUS_IS_NOT_BEING_USED = false;
+    private static final boolean STYLUS_IS_BEING_USED = true;
     private static final int DISPLAY_ID = 1;
 
     @Mock
@@ -196,7 +198,8 @@
                 DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mDozeBrightnessModeStrategy);
     }
 
@@ -212,7 +215,8 @@
                 DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
         assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mDozeBrightnessModeStrategy);
     }
 
@@ -226,7 +230,8 @@
                 DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
         assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mDozeBrightnessModeStrategy);
     }
 
@@ -249,7 +254,8 @@
 
         assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mDozeBrightnessModeStrategy);
     }
 
@@ -259,7 +265,8 @@
                 DisplayManagerInternal.DisplayPowerRequest.class);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_OFF,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mScreenOffBrightnessModeStrategy);
     }
 
@@ -271,7 +278,8 @@
         when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mOverrideBrightnessStrategy);
     }
 
@@ -284,7 +292,8 @@
         when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(0.3f);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mTemporaryBrightnessStrategy);
     }
 
@@ -298,7 +307,8 @@
         when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mBoostBrightnessStrategy);
     }
 
@@ -312,7 +322,8 @@
         when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mInvalidBrightnessStrategy);
     }
 
@@ -323,7 +334,8 @@
         when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mFollowerBrightnessStrategy);
     }
 
@@ -341,7 +353,8 @@
         when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mOffloadBrightnessStrategy);
     }
 
@@ -365,7 +378,8 @@
         when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mAutomaticBrightnessStrategy);
         verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON,
                 true, BrightnessReason.REASON_UNKNOWN,
@@ -373,6 +387,32 @@
                 /* useNormalBrightnessForDoze= */ false, 0.1f, false);
     }
 
+
+    @Test
+    public void selectStrategy_doesNotSelectAutomaticStrategyWhenStylusInUse() {
+        when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
+        when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
+        when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
+        when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
+                true);
+        mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+                mInjector, DISPLAY_ID, mDisplayManagerFlags);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        displayPowerRequest.screenBrightnessOverride = Float.NaN;
+        when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+        when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+        when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(true);
+        when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true);
+        assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+                        new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_BEING_USED)),
+                mAutomaticBrightnessStrategy);
+    }
+
     @Test
     public void selectStrategy_selectsAutomaticFallbackStrategyWhenValid() {
         when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
@@ -389,7 +429,8 @@
         when(mAutoBrightnessFallbackStrategy.isValid()).thenReturn(true);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mAutoBrightnessFallbackStrategy);
     }
 
@@ -407,7 +448,8 @@
         assertNotEquals(mOffloadBrightnessStrategy,
                 mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)));
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)));
     }
 
     @Test
@@ -425,7 +467,8 @@
         when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false);
         assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
                         new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                                0.1f, false, mDisplayOffloadSession)),
+                                0.1f, false, mDisplayOffloadSession,
+                                STYLUS_IS_NOT_BEING_USED)),
                 mFallbackBrightnessStrategy);
     }
 
@@ -440,7 +483,8 @@
 
         mDisplayBrightnessStrategySelector.selectStrategy(
                 new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
-                        0.1f, false, mDisplayOffloadSession));
+                        0.1f, false, mDisplayOffloadSession,
+                        STYLUS_IS_NOT_BEING_USED));
 
         StrategySelectionNotifyRequest strategySelectionNotifyRequest =
                 new StrategySelectionNotifyRequest(displayPowerRequest, Display.STATE_ON,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
index 99dfa73..2a71af0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
@@ -129,7 +129,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mAutoBrightnessFallbackStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index efa8b3e..8a1f860 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -637,7 +637,7 @@
                 .build();
         DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
                 .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
-                        /* userSetBrightnessChanged= */ true));
+                        /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
         assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
     }
 
@@ -686,7 +686,7 @@
                 .build();
         DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
                 .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
-                        /* userSetBrightnessChanged= */ true));
+                        /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
         assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
     }
 
@@ -725,7 +725,7 @@
                 .build();
         DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
                 .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
-                        /* userSetBrightnessChanged= */ true));
+                        /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
         assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
     }
 
@@ -764,7 +764,7 @@
                 .build();
         DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
                 .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
-                        /* userSetBrightnessChanged= */ true));
+                        /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
         assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index 275bb3ef..c03309e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mBoostBrightnessStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index 23e447c..e7f80b0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
@@ -57,7 +57,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mDozeBrightnessModeStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
index c4a5790..dcfa174 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -61,7 +61,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mFallbackBrightnessStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, currentBrightness,
-                                /* userSetBrightnessChanged= */ true));
+                                /* userSetBrightnessChanged= */ true,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index c01f96e..239cdb6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -61,7 +61,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mFollowerBrightnessStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
index 9fb2afa..77302f8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -72,7 +72,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mOffloadBrightnessStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
         assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy
                 .getOffloadScreenBrightness(), 0.0f);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index e8b4c06..cc21af1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mOverrideBrightnessStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index 38709ec..652663e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
@@ -58,7 +58,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mScreenOffBrightnessModeStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index f523b6a..0022cab 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mTemporaryBrightnessStrategy.updateBrightness(
                         new StrategyExecutionRequest(displayPowerRequest, 0.2f,
-                                /* userSetBrightnessChanged= */ false));
+                                /* userSetBrightnessChanged= */ false,
+                                /* isStylusBeingUsed */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
index b2d83d7..9a93fba 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
@@ -43,35 +43,42 @@
     @Test
     fun testAppSupportedModes(@TestParameter testCase: AppSupportedModesTestCase) {
         whenever(mockFlags.isSynthetic60HzModesEnabled).thenReturn(testCase.syntheticModesEnabled)
+        whenever(mockFlags.hasArrSupportFlag()).thenReturn(testCase.hasArrSupport)
         whenever(mockConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
         val syntheticModeManager = SyntheticModeManager(mockFlags)
 
         val result = syntheticModeManager.createAppSupportedModes(
-            mockConfig, testCase.supportedModes)
+            mockConfig, testCase.supportedModes, testCase.hasArrSupport)
 
         assertThat(result).isEqualTo(testCase.expectedAppModes)
     }
 
+    // TODO(b/361433651) Remove vrrSupported once hasArrSupport is rolled out
     enum class AppSupportedModesTestCase(
         val syntheticModesEnabled: Boolean,
         val vrrSupported: Boolean,
+        val hasArrSupport: Boolean,
         val supportedModes: Array<Mode>,
         val expectedAppModes: Array<Mode>
     ) {
-        SYNTHETIC_MODES_NOT_SUPPORTED(false, true, DISPLAY_MODES, DISPLAY_MODES),
-        VRR_NOT_SUPPORTED(true, false, DISPLAY_MODES, DISPLAY_MODES),
-        VRR_SYNTHETIC_NOT_SUPPORTED(false, false, DISPLAY_MODES, DISPLAY_MODES),
-        SINGLE_RESOLUTION_MODES(true, true, DISPLAY_MODES, arrayOf(
+        SYNTHETIC_MODES_NOT_SUPPORTED(false, true, true, DISPLAY_MODES, DISPLAY_MODES),
+        VRR_NOT_SUPPORTED(true, false, false, DISPLAY_MODES, DISPLAY_MODES),
+        VRR_SYNTHETIC_NOT_SUPPORTED(false, false, false, DISPLAY_MODES, DISPLAY_MODES),
+        SINGLE_RESOLUTION_MODES(true, true, true, DISPLAY_MODES, arrayOf(
             Mode(2, 100, 100, 120f),
             Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf())
         )),
-        NO_60HZ_MODES(true, true, arrayOf(Mode(2, 100, 100, 120f)),
+        SINGLE_RESOLUTION_MODES_HASARR(true, false, true, DISPLAY_MODES, arrayOf(
+            Mode(2, 100, 100, 120f),
+            Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf())
+        )),
+        NO_60HZ_MODES(true, true, true, arrayOf(Mode(2, 100, 100, 120f)),
             arrayOf(
                 Mode(2, 100, 100, 120f),
                 Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf())
             )
         ),
-        MULTI_RESOLUTION_MODES(true, true,
+        MULTI_RESOLUTION_MODES(true, true, true,
             arrayOf(
                 Mode(1, 100, 100, 120f),
                 Mode(2, 200, 200, 60f),
@@ -86,7 +93,7 @@
                 Mode(7, 300, 300, 60f, 60f, true, floatArrayOf(), intArrayOf())
             )
         ),
-        WITH_HDR_TYPES(true, true,
+        WITH_HDR_TYPES(true, true, true,
             arrayOf(
                 Mode(1, 100, 100, 120f, 120f, false, floatArrayOf(), intArrayOf(1, 2)),
                 Mode(2, 200, 200, 60f, 120f, false, floatArrayOf(), intArrayOf(3, 4)),
@@ -99,7 +106,7 @@
                 Mode(5, 200, 200, 60f, 60f, true, floatArrayOf(), intArrayOf(5, 6)),
             )
         ),
-        UNACHIEVABLE_60HZ(true, true,
+        UNACHIEVABLE_60HZ(true, true, true,
             arrayOf(
                 Mode(1, 100, 100, 90f),
             ),
@@ -107,7 +114,7 @@
                 Mode(1, 100, 100, 90f),
             )
         ),
-        MULTI_RESOLUTION_MODES_WITH_UNACHIEVABLE_60HZ(true, true,
+        MULTI_RESOLUTION_MODES_WITH_UNACHIEVABLE_60HZ(true, true, true,
             arrayOf(
                 Mode(1, 100, 100, 120f),
                 Mode(2, 200, 200, 90f),
@@ -118,7 +125,7 @@
                 Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf()),
             )
         ),
-        LOWER_THAN_60HZ_MODES(true, true,
+        LOWER_THAN_60HZ_MODES(true, true, true,
             arrayOf(
                 Mode(1, 100, 100, 30f),
                 Mode(2, 100, 100, 45f),
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index 1211456..439243e 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -82,6 +82,11 @@
     private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET =
             createAudioDeviceInfo(
                     AudioSystem.DEVICE_OUT_WIRED_HEADSET, "name_wired_hs", /* address= */ null);
+    private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS =
+            createAudioDeviceInfo(
+                    AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+                    "name_wired_hs_with_address",
+                    /* address= */ "card=1;device=0");
     private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP =
             createAudioDeviceInfo(
                     AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "name_a2dp", /* address= */ "12:34:45");
@@ -304,6 +309,55 @@
         assertThat(selectedRoute.getName().toString()).isEqualTo(FAKE_ROUTE_NAME);
     }
 
+    @Test
+    public void getAvailableRoutes_whenAddressIsPopulatedForNonBluetoothDevice_usesCorrectName() {
+        addAvailableAudioDeviceInfo(
+                /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS,
+                /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS,
+                FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP);
+
+        List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes();
+        assertThat(availableRoutes.size()).isEqualTo(3);
+
+        assertThat(
+                        getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET)
+                                .getName()
+                                .toString())
+                .isEqualTo(
+                        FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS
+                                .getProductName()
+                                .toString());
+
+        assertThat(
+                        getAvailableRouteWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)
+                                .getName()
+                                .toString())
+                .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP.getProductName().toString());
+    }
+
+    @Test
+    public void
+            getAvailableRoutes_whenAddressIsNotPopulatedForNonBluetoothDevice_usesCorrectName() {
+        addAvailableAudioDeviceInfo(
+                /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET,
+                /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+
+        List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes();
+        assertThat(availableRoutes.size()).isEqualTo(2);
+
+        assertThat(
+                        getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
+                                .getName()
+                                .toString())
+                .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER.getProductName().toString());
+
+        assertThat(
+                        getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET)
+                                .getName()
+                                .toString())
+                .isEqualTo(FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET.getProductName().toString());
+    }
+
     // Internal methods.
 
     @NonNull
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/alarm/OWNERS
index 6f207fb1..6eb986b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/OWNERS
@@ -1 +1 @@
-include /apex/jobscheduler/OWNERS
+include /apex/jobscheduler/ALARM_OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 6ccc037..2a825f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -107,6 +107,7 @@
 import android.os.IProgressListener;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -1309,12 +1310,13 @@
         intent.putExtra("EXTRA_INTENT0", extraIntent);
 
         intent.collectExtraIntentKeys();
-        mAms.addCreatorToken(intent);
+        mAms.addCreatorToken(intent, TEST_PACKAGE);
 
         ActivityManagerService.IntentCreatorToken token =
                 (ActivityManagerService.IntentCreatorToken) extraIntent.getCreatorToken();
         assertThat(token).isNotNull();
         assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+        assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
     }
 
     @Test
@@ -1330,7 +1332,7 @@
         fillinIntent.collectExtraIntentKeys();
         intent.fillIn(fillinIntent, FILL_IN_ACTION);
 
-        mAms.addCreatorToken(fillinIntent);
+        mAms.addCreatorToken(fillinIntent, TEST_PACKAGE);
 
         fillinExtraIntent = intent.getParcelableExtra("FILLIN_EXTRA_INTENT0", Intent.class);
 
@@ -1338,6 +1340,49 @@
                 (ActivityManagerService.IntentCreatorToken) fillinExtraIntent.getCreatorToken();
         assertThat(token).isNotNull();
         assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+        assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT)
+    public void testCheckCreatorToken() {
+        Intent intent = new Intent();
+        Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
+        intent.putExtra("EXTRA_INTENT", extraIntent);
+
+        intent.collectExtraIntentKeys();
+
+        // mimic client hack and sneak in an extra intent without going thru collectExtraIntentKeys.
+        Intent extraIntent2 = new Intent("EXTRA_INTENT_ACTION2");
+        intent.putExtra("EXTRA_INTENT2", extraIntent2);
+
+        // mock parceling on the client side, unparcling on the system server side, then
+        // addCreatorToken on system server side.
+        final Parcel parcel = Parcel.obtain();
+        intent.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        Intent newIntent = new Intent();
+        newIntent.readFromParcel(parcel);
+        intent = newIntent;
+        mAms.addCreatorToken(intent, TEST_PACKAGE);
+        // entering the target app's process.
+        intent.checkCreatorToken();
+
+        Intent extraIntent3 = new Intent("EXTRA_INTENT_ACTION3");
+        intent.putExtra("EXTRA_INTENT3", extraIntent3);
+
+        extraIntent = intent.getParcelableExtra("EXTRA_INTENT", Intent.class);
+        extraIntent2 = intent.getParcelableExtra("EXTRA_INTENT2", Intent.class);
+        extraIntent3 = intent.getParcelableExtra("EXTRA_INTENT3", Intent.class);
+
+        assertThat(extraIntent.getExtendedFlags()
+                & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
+        // sneaked in intent should have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set.
+        assertThat(extraIntent2.getExtendedFlags()
+                & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isNotEqualTo(0);
+        // local created intent should not have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set.
+        assertThat(extraIntent3.getExtendedFlags()
+                & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
     }
 
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 2107406..250c2f9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -168,9 +168,11 @@
             + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
     private static int sFirstUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
     private static int sFirstNonUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 20;
-    private static int sUiTierSize = 5;
+    private static int sUiTierSize = 10;
 
     private Context mContext;
+    private ProcessStateController mProcessStateController;
+    private ActiveUids mActiveUids;
     private PackageManagerInternal mPackageManagerInternal;
     private ActivityManagerService mService;
     private OomAdjusterInjector mInjector = new OomAdjusterInjector();
@@ -229,15 +231,19 @@
         doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
         doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
         setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
+
         doNothing().when(pr).enqueueProcessChangeItemLocked(anyInt(), anyInt(), anyInt(),
                 anyInt());
         doNothing().when(pr).enqueueProcessChangeItemLocked(anyInt(), anyInt(), anyInt(),
                 anyBoolean());
-        mService.mOomAdjuster = mService.mConstants.ENABLE_NEW_OOMADJ
-                ? new OomAdjusterModernImpl(mService, mService.mProcessList,
-                        new ActiveUids(mService, false), mInjector)
-                : new OomAdjuster(mService, mService.mProcessList, new ActiveUids(mService, false),
-                        mInjector);
+        mActiveUids = new ActiveUids(mService, false);
+        mProcessStateController = new ProcessStateController.Builder(mService,
+                mService.mProcessList, mActiveUids)
+                .useModernOomAdjuster(mService.mConstants.ENABLE_NEW_OOMADJ)
+                .setOomAdjusterInjector(mInjector)
+                .build();
+        mService.mProcessStateController = mProcessStateController;
+        mService.mOomAdjuster = mService.mProcessStateController.getOomAdjuster();
         mService.mOomAdjuster.mAdjSeq = 10000;
         mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
         mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
@@ -246,8 +252,8 @@
     @SuppressWarnings("GuardedBy")
     @After
     public void tearDown() {
-        mService.mOomAdjuster.resetInternal();
-        mService.mOomAdjuster.mActiveUids.clear();
+        mProcessStateController.getOomAdjuster().resetInternal();
+        mActiveUids.clear();
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         mInjector.reset();
     }
@@ -293,7 +299,7 @@
     private void updateOomAdj(ProcessRecord... apps) {
         if (apps.length == 0) {
             updateProcessRecordNodes(mService.mProcessList.getLruProcessesLOSP());
-            mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+            mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
         } else {
             updateProcessRecordNodes(Arrays.asList(apps));
             if (apps.length == 1) {
@@ -301,10 +307,10 @@
                 if (!mService.mProcessList.getLruProcessesLOSP().contains(app)) {
                     mService.mProcessList.getLruProcessesLOSP().add(app);
                 }
-                mService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE);
+                mProcessStateController.runUpdate(apps[0], OOM_ADJ_REASON_NONE);
             } else {
                 setProcessesToLru(apps);
-                mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+                mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
                 mService.mProcessList.getLruProcessesLOSP().clear();
             }
         }
@@ -318,9 +324,9 @@
     private void updateOomAdjPending(ProcessRecord... apps) {
         setProcessesToLru(apps);
         for (ProcessRecord app : apps) {
-            mService.mOomAdjuster.enqueueOomAdjTargetLocked(app);
+            mProcessStateController.enqueueUpdateTarget(app);
         }
-        mService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE);
+        mProcessStateController.runPendingUpdate(OOM_ADJ_REASON_NONE);
         mService.mProcessList.getLruProcessesLOSP().clear();
     }
 
@@ -341,11 +347,10 @@
     public void testUpdateOomAdj_DoOne_Persistent_TopUi_Sleeping() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        app.mState.setHasTopUi(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        mProcessStateController.setMaxAdj(app, PERSISTENT_PROC_ADJ);
+        mProcessStateController.setHasTopUi(app, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         updateOomAdj(app);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ,
                 SCHED_GROUP_RESTRICTED);
@@ -357,9 +362,9 @@
     public void testUpdateOomAdj_DoOne_Persistent_TopUi_Awake() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        app.mState.setHasTopUi(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setMaxAdj(app, PERSISTENT_PROC_ADJ);
+        mProcessStateController.setHasTopUi(app, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ,
@@ -371,9 +376,9 @@
     public void testUpdateOomAdj_DoOne_Persistent_TopApp() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(app, PERSISTENT_PROC_ADJ);
         doReturn(app).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doReturn(null).when(mService).getTopApp();
 
@@ -388,7 +393,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(app).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doReturn(null).when(mService).getTopApp();
 
@@ -401,8 +406,8 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState();
-        app.mState.setRunningRemoteAnimation(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setRunningRemoteAnimation(app, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
 
@@ -415,7 +420,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doCallRealMethod().when(app).getActiveInstrumentation();
 
@@ -431,7 +436,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(true).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class),
                 any(int[].class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doReturn(false).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class),
                 any(int[].class));
@@ -466,8 +471,8 @@
     public void testUpdateOomAdj_DoOne_ExecutingService() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mServices.startExecutingService(mock(ServiceRecord.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.startExecutingService(app.mServices, mock(ServiceRecord.class));
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -480,11 +485,11 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState();
         doReturn(app).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         updateOomAdj(app);
         doReturn(null).when(mService).getTopApp();
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ,
                 SCHED_GROUP_BACKGROUND);
@@ -498,7 +503,7 @@
         app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ);
         app.mState.setCurAdj(CACHED_APP_MIN_ADJ);
         doReturn(null).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
@@ -516,7 +521,7 @@
         doReturn(true).when(wpc).hasActivities();
         doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
                 .when(wpc).getActivityStateFlags();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT);
@@ -555,7 +560,7 @@
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).hasRecentTasks();
         app.mState.setLastTopTime(SystemClock.uptimeMillis());
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doCallRealMethod().when(wpc).hasRecentTasks();
 
@@ -567,9 +572,9 @@
     public void testUpdateOomAdj_DoOne_FgServiceLocation() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION,
-                /* hasNoneType=*/false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(app.mServices, true,
+                ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION, /* hasNoneType=*/false);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -582,8 +587,9 @@
     public void testUpdateOomAdj_DoOne_FgService() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(app.mServices, true, 0, /* hasNoneType=*/
+                true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -599,20 +605,20 @@
 
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
-        s.startRequested = true;
+        mProcessStateController.setStartRequested(s, true);
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
-        s.setShortFgsInfo(SystemClock.uptimeMillis());
+        mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis());
 
         // SHORT_SERVICE FGS will get IMP_FG and a slightly different recent-adjustment.
         {
             ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                     MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-            app.mServices.startService(s);
-            app.mServices.setHasForegroundServices(true,
+            mProcessStateController.startService(app.mServices, s);
+            mProcessStateController.setHasForegroundServices(app.mServices, true,
                     FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
             app.mState.setLastTopTime(SystemClock.uptimeMillis());
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
             updateOomAdj(app);
 
@@ -625,9 +631,9 @@
         {
             ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                     MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-            app.mServices.setHasForegroundServices(true,
+            mProcessStateController.setHasForegroundServices(app.mServices, true,
                     FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
-            app.mServices.startService(s);
+            mProcessStateController.startService(app.mServices, s);
             app.mState.setLastTopTime(SystemClock.uptimeMillis()
                     - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
             mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -642,21 +648,21 @@
         // SHORT_SERVICE, timed out already.
         s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
-        s.startRequested = true;
+        mProcessStateController.setStartRequested(s, true);
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
-        s.setShortFgsInfo(SystemClock.uptimeMillis()
+        mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis()
                 - mService.mConstants.mShortFgsTimeoutDuration
                 - mService.mConstants.mShortFgsProcStateExtraWaitDuration);
         {
             ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                     MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-            app.mServices.setHasForegroundServices(true,
+            mProcessStateController.setHasForegroundServices(app.mServices, true,
                     FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
-            app.mServices.startService(s);
+            mProcessStateController.startService(app.mServices, s);
             app.mState.setLastTopTime(SystemClock.uptimeMillis()
                     - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
             updateOomAdj(app);
 
@@ -671,8 +677,8 @@
     public void testUpdateOomAdj_DoOne_OverlayUi() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mState.setHasOverlayUi(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasOverlayUi(app, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ,
@@ -684,9 +690,10 @@
     public void testUpdateOomAdj_DoOne_PerceptibleRecent_FgService() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(app.mServices, true, 0, /* hasNoneType=*/
+                true);
         app.mState.setLastTopTime(SystemClock.uptimeMillis());
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE,
@@ -699,7 +706,7 @@
         verify(mService.mHandler).sendEmptyMessageAtTime(
                 eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
-        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        mProcessStateController.runFollowUpUpdate();
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT, "fg-service");
@@ -722,9 +729,9 @@
             // Simulate the system starting and binding to a service in the app.
             ServiceRecord s = bindService(app, system,
                     null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
-            s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime;
+            mProcessStateController.setLastTopAlmostPerceptibleBindRequest(s, nowUptime);
             s.getConnections().clear();
-            app.mServices.updateHasTopStartedAlmostPerceptibleServices();
+            mProcessStateController.updateHasTopStartedAlmostPerceptibleServices(app.mServices);
             mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(app);
 
@@ -736,7 +743,7 @@
             verify(mService.mHandler).sendEmptyMessageAtTime(
                     eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
             mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
-            mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+            mProcessStateController.runFollowUpUpdate();
 
             final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
                     ? sFirstUiCachedAdj : sFirstCachedAdj;
@@ -758,15 +765,15 @@
             // Simulate the system starting and binding to a service in the app.
             ServiceRecord s = bindService(app, system,
                     null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class));
-            s.lastTopAlmostPerceptibleBindRequestUptimeMs =
-                    nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
-            app.mServices.updateHasTopStartedAlmostPerceptibleServices();
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mProcessStateController.setLastTopAlmostPerceptibleBindRequest(s,
+                    nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs);
+            mProcessStateController.updateHasTopStartedAlmostPerceptibleServices(app.mServices);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(app);
 
             assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
 
-            mService.mOomAdjuster.resetInternal();
+            mProcessStateController.getOomAdjuster().resetInternal();
         }
 
         // Out of grace period and no valid binding so no adjustment.
@@ -780,16 +787,16 @@
             // Simulate the system starting and binding to a service in the app.
             ServiceRecord s = bindService(app, system,
                     null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
-            s.lastTopAlmostPerceptibleBindRequestUptimeMs =
-                    nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
+            mProcessStateController.setLastTopAlmostPerceptibleBindRequest(s,
+                    nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs);
             s.getConnections().clear();
-            app.mServices.updateHasTopStartedAlmostPerceptibleServices();
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mProcessStateController.updateHasTopStartedAlmostPerceptibleServices(app.mServices);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(app);
 
             assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
 
-            mService.mOomAdjuster.resetInternal();
+            mProcessStateController.getOomAdjuster().resetInternal();
         }
     }
 
@@ -800,12 +807,12 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true));
-        system.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        system.mState.setHasTopUi(true);
+        mProcessStateController.setMaxAdj(system, PERSISTENT_PROC_ADJ);
+        mProcessStateController.setHasTopUi(system, true);
         // Simulate the system starting and binding to a service in the app.
         ServiceRecord s = bindService(app, system,
                 null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(system, app);
 
         assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND,
@@ -817,8 +824,8 @@
     public void testUpdateOomAdj_DoOne_Toast() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mState.setForcingToImportant(new Object());
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setForcingToImportant(app, new Object());
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -832,7 +839,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isHeavyWeightProcess();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doReturn(false).when(wpc).isHeavyWeightProcess();
 
@@ -847,7 +854,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -861,7 +868,7 @@
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isPreviousProcess();
         doReturn(true).when(wpc).hasActivities();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
@@ -873,7 +880,7 @@
         verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
                 followUpTimeCaptor.capture());
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
-        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        mProcessStateController.runFollowUpUpdate();
 
         int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
                 ? sFirstUiCachedAdj : CACHED_APP_MIN_ADJ;
@@ -896,9 +903,9 @@
             doReturn(true).when(wpc).isPreviousProcess();
             doReturn(true).when(wpc).hasActivities();
         }
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         setProcessesToLru(apps);
-        mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+        mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
 
         for (int i = 0; i < numberOfApps; i++) {
             assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
@@ -914,7 +921,7 @@
             mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
         }
 
-        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        mProcessStateController.runFollowUpUpdate();
 
         for (int i = 0; i < numberOfApps; i++) {
             final int mruIndex = numberOfApps - i - 1;
@@ -938,10 +945,8 @@
     public void testUpdateOomAdj_DoOne_Backup() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
-        backupTarget.app = app;
-        doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setBackupTarget(app);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doReturn(null).when(mService.mBackupTargets).get(anyInt());
 
@@ -954,8 +959,8 @@
     public void testUpdateOomAdj_DoOne_ClientActivities() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mServices.setHasClientActivities(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasClientActivities(app.mServices, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState());
@@ -966,8 +971,8 @@
     public void testUpdateOomAdj_DoOne_TreatLikeActivity() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        app.mServices.setTreatLikeActivity(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setTreatLikeActivity(app.mServices, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
@@ -981,10 +986,10 @@
         app.mState.setServiceB(true);
         ServiceRecord s = mock(ServiceRecord.class);
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
-        s.startRequested = true;
-        s.lastActivity = SystemClock.uptimeMillis();
-        app.mServices.startService(s);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        mProcessStateController.startService(app.mServices, s);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND);
@@ -995,8 +1000,8 @@
     public void testUpdateOomAdj_DoOne_MaxAdj() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setMaxAdj(app, PERCEPTIBLE_LOW_APP_ADJ);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ,
@@ -1011,7 +1016,7 @@
         app.mState.setCurRawAdj(SERVICE_ADJ);
         app.mState.setCurAdj(SERVICE_ADJ);
         doReturn(null).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj());
@@ -1025,10 +1030,10 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ServiceRecord s = mock(ServiceRecord.class);
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
-        s.startRequested = true;
-        s.lastActivity = SystemClock.uptimeMillis();
-        app.mServices.startService(s);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        mProcessStateController.startService(app.mServices, s);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -1043,8 +1048,8 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY,
                 mock(IBinder.class));
-        s.startRequested = true;
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(client).when(mService).getTopApp();
         updateOomAdj(client, app);
@@ -1062,10 +1067,10 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        client.mServices.setTreatLikeActivity(true);
+        mProcessStateController.setTreatLikeActivity(client.mServices, true);
         bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY
                 | Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
@@ -1086,7 +1091,7 @@
                 mock(ActivityServiceConnectionsHolder.class));
         doReturn(client).when(mService).getTopApp();
         doReturn(true).when(cr.activity).isActivityVisible();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -1099,7 +1104,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         bindService(app, app, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
@@ -1114,9 +1119,9 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        client.mServices.setTreatLikeActivity(true);
+        mProcessStateController.setTreatLikeActivity(client.mServices, true);
         bindService(app, client, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState());
@@ -1137,7 +1142,7 @@
                 mock(IBinder.class));
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(client).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
         doReturn(null).when(mService).getTopApp();
 
@@ -1152,9 +1157,9 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
-        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        client.mState.setHasTopUi(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+        mProcessStateController.setHasTopUi(client, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -1170,8 +1175,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class));
-        client.mServices.startExecutingService(mock(ServiceRecord.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.startExecutingService(client.mServices, mock(ServiceRecord.class));
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -1188,7 +1193,7 @@
         bindService(app, client, null, null, 0, mock(IBinder.class));
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(client).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
         doReturn(null).when(mService).getTopApp();
 
@@ -1203,8 +1208,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
-        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState());
@@ -1222,9 +1227,9 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
         client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         updateOomAdj(client, app);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
                 SCHED_GROUP_RESTRICTED);
@@ -1240,8 +1245,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
-        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState());
@@ -1256,8 +1261,9 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, 0, mock(IBinder.class));
-        client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client.mServices, true,
+                0, /* hasNoneType=*/true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, client.mState.getSetProcState());
@@ -1279,16 +1285,16 @@
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
-        s.startRequested = true;
-        s.isForeground = true;
-        s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
-        s.setShortFgsInfo(SystemClock.uptimeMillis());
-        client.mServices.startService(s);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setIsForegroundService(s, true);
+        mProcessStateController.setForegroundServiceType(s, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE);
+        mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis());
+        mProcessStateController.startService(client.mServices, s);
         client.mState.setLastTopTime(SystemClock.uptimeMillis());
 
-        client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
-                /* hasNoneType=*/false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client.mServices, true,
+                FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1310,16 +1316,16 @@
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
-        s.startRequested = true;
-        s.isForeground = true;
-        s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
-        s.setShortFgsInfo(SystemClock.uptimeMillis());
-        app2.mServices.startService(s);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setIsForegroundService(s, true);
+        mProcessStateController.setForegroundServiceType(s, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE);
+        mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis());
+        mProcessStateController.startService(app2.mServices, s);
         app2.mState.setLastTopTime(SystemClock.uptimeMillis());
 
-        app2.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
-                /* hasNoneType=*/false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(app2.mServices, true,
+                FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app2);
 
         // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1331,7 +1337,7 @@
         // Persistent process
         ProcessRecord pers = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        pers.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(pers, PERSISTENT_PROC_ADJ);
 
         // app1, which is bound by pers (which makes it BFGS)
         ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
@@ -1358,18 +1364,16 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
-        BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
-        backupTarget.app = client;
-        doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setBackupTarget(client);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
-        doReturn(null).when(mService.mBackupTargets).get(anyInt());
+        mProcessStateController.stopBackupTarget(UserHandle.getUserId(MOCKAPP2_UID));
 
         assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj());
         assertNoBfsl(app);
 
-        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
         updateOomAdj(client, app);
 
         assertEquals(PERSISTENT_SERVICE_ADJ, app.mState.getSetAdj());
@@ -1384,8 +1388,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
-        client.mState.setRunningRemoteAnimation(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setRunningRemoteAnimation(client, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj());
@@ -1399,8 +1403,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
-        client.mState.setRunningRemoteAnimation(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setRunningRemoteAnimation(client, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1414,8 +1418,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, 0, mock(IBinder.class));
-        client.mState.setHasOverlayUi(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasOverlayUi(client, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1432,13 +1436,13 @@
             bindService(app, client, null, null,
                     Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
                     mock(IBinder.class));
-            client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
 
             assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
 
-            mService.mOomAdjuster.resetInternal();
+            mProcessStateController.getOomAdjuster().resetInternal();
         }
 
         {
@@ -1451,14 +1455,14 @@
             bindService(app, client, null, null,
                     Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
                     mock(IBinder.class));
-            client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
             doReturn(false).when(wpc).isHeavyWeightProcess();
 
             assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
 
-            mService.mOomAdjuster.resetInternal();
+            mProcessStateController.getOomAdjuster().resetInternal();
         }
 
         {
@@ -1469,13 +1473,13 @@
             bindService(app, client, null, null,
                     Context.BIND_ALMOST_PERCEPTIBLE,
                     mock(IBinder.class));
-            client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
 
             assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
 
-            mService.mOomAdjuster.resetInternal();
+            mProcessStateController.getOomAdjuster().resetInternal();
         }
 
         {
@@ -1488,14 +1492,14 @@
             bindService(app, client, null, null,
                     Context.BIND_ALMOST_PERCEPTIBLE,
                     mock(IBinder.class));
-            client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+            setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
             doReturn(false).when(wpc).isHeavyWeightProcess();
 
             assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
 
-            mService.mOomAdjuster.resetInternal();
+            mProcessStateController.getOomAdjuster().resetInternal();
         }
     }
 
@@ -1507,8 +1511,8 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, 0, mock(IBinder.class));
-        client.mState.setRunningRemoteAnimation(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setRunningRemoteAnimation(client, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1523,8 +1527,8 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND,
                 mock(IBinder.class));
-        client.mState.setHasOverlayUi(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasOverlayUi(client, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState());
@@ -1536,7 +1540,8 @@
     public void testUpdateOomAdj_DoOne_Provider_Self() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        bindProvider(app, app, null, null, false);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(app, cpr);
         updateOomAdj(app);
 
         final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
@@ -1551,9 +1556,10 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        bindProvider(app, client, null, null, false);
-        client.mServices.setTreatLikeActivity(true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(client, cpr);
+        mProcessStateController.setTreatLikeActivity(client.mServices, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client);
 
         final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
@@ -1568,10 +1574,11 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        bindProvider(app, client, null, null, false);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(client, cpr);
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(client).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
         doReturn(null).when(mService).getTopApp();
 
@@ -1585,9 +1592,10 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        bindProvider(app, client, null, null, false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client.mServices, true, 0, true);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(client, cpr);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1607,17 +1615,18 @@
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
         ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
-        s.startRequested = true;
+        mProcessStateController.setStartRequested(s, true);
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
-        s.setShortFgsInfo(SystemClock.uptimeMillis());
-        client.mServices.startService(s);
+        mProcessStateController.setShortFgsInfo(s, SystemClock.uptimeMillis());
+        mProcessStateController.startService(client.mServices, s);
         client.mState.setLastTopTime(SystemClock.uptimeMillis());
 
-        client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
-                /* hasNoneType=*/false);
-        bindProvider(app, client, null, null, false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client.mServices, true,
+                FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, false);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(client, cpr);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1638,8 +1647,9 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        bindProvider(app, client, null, null, true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, true);
+        bindProvider(client, cpr);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ,
@@ -1651,9 +1661,24 @@
     public void testUpdateOomAdj_DoOne_Provider_Retention() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
-        updateOomAdj(app);
+        ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        final String providerName = "aProvider";
+        // Go through the motions of binding a provider
+        final ContentProviderRecord cpr = createContentProviderRecord(app, providerName, false);
+        final ContentProviderConnection conn = bindProvider(client, cpr);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(client).when(mService).getTopApp();
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdj(client, app);
+
+        assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
+
+        unbindProvider(client, cpr, conn);
+        mProcessStateController.removePublishedProvider(app, providerName);
+        final long lastProviderTime = SystemClock.uptimeMillis();
+        mProcessStateController.setLastProviderTime(app, SystemClock.uptimeMillis());
+        updateOomAdj(client, app);
 
         assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
                 SCHED_GROUP_BACKGROUND, "recent-provider");
@@ -1663,8 +1688,10 @@
         final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
         verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
                 followUpTimeCaptor.capture());
+
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
-        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        setProcessesToLru(client, app);
+        mProcessStateController.runFollowUpUpdate();
 
         final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
                 ? sFirstNonUiCachedAdj : sFirstCachedAdj;
@@ -1688,7 +1715,7 @@
         bindService(client, client2, null, null, 0, mock(IBinder.class));
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(client2).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
         doReturn(null).when(mService).getTopApp();
 
@@ -1707,8 +1734,8 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app, client2, null, null, 0, mock(IBinder.class));
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1727,8 +1754,8 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, null, 0, mock(IBinder.class));
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1747,13 +1774,13 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, null, 0, mock(IBinder.class));
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
         bindService(client2, app, null, null, 0, mock(IBinder.class));
 
         // Note: We add processes to LRU but still call updateOomAdjLocked() with a specific
         // processes.
         setProcessesToLru(app, client, client2);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1766,8 +1793,8 @@
         assertBfsl(client);
         assertBfsl(client2);
 
-        client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client2.mServices, false, 0, false);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client2);
 
         assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState());
@@ -1790,8 +1817,8 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client2, client, null, null, 0, mock(IBinder.class));
-        client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1817,8 +1844,8 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, null, 0, mock(IBinder.class));
         bindService(client2, client, null, null, 0, mock(IBinder.class));
-        client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1851,8 +1878,8 @@
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
         bindService(client3, client4, null, null, 0, mock(IBinder.class));
         bindService(client4, client3, null, null, 0, mock(IBinder.class));
-        client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(client.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3, client4);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1883,13 +1910,13 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, null, 0, mock(IBinder.class));
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
         bindService(client2, app, null, null, 0, mock(IBinder.class));
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.mState.setForcingToImportant(new Object());
+        mProcessStateController.setForcingToImportant(client3, new Object());
         bindService(app, client3, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1913,9 +1940,9 @@
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.mState.setForcingToImportant(new Object());
+        mProcessStateController.setForcingToImportant(client3, new Object());
         bindService(app, client3, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1940,9 +1967,9 @@
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
         ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        client4.mState.setForcingToImportant(new Object());
+        mProcessStateController.setForcingToImportant(client4, new Object());
         bindService(app, client4, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3, client4);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1965,13 +1992,13 @@
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.mState.setForcingToImportant(new Object());
+        mProcessStateController.setForcingToImportant(client3, new Object());
         bindService(app, client3, null, null, 0, mock(IBinder.class));
         ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(client4.mServices, true, 0, true);
         bindService(app, client4, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3, client4);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1992,12 +2019,12 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app, client2, null, null, 0, mock(IBinder.class));
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.mState.setForcingToImportant(new Object());
+        mProcessStateController.setForcingToImportant(client3, new Object());
         bindService(app, client3, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, client3, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2015,9 +2042,10 @@
         bindService(app, client, null, null, 0, mock(IBinder.class));
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
-        bindProvider(client, client2, null, null, false);
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        final ContentProviderRecord cpr = createContentProviderRecord(client, null, false);
+        bindProvider(client2, cpr);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2035,10 +2063,11 @@
         bindService(app, client, null, null, 0, mock(IBinder.class));
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
-        bindProvider(client, client2, null, null, false);
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        final ContentProviderRecord cpr = createContentProviderRecord(client, null, false);
+        bindProvider(client2, cpr);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
         bindService(client2, app, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2053,12 +2082,14 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        bindProvider(app, client, null, null, false);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(client, cpr);
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
-        bindProvider(client, client2, null, null, false);
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        final ContentProviderRecord cpr2 = createContentProviderRecord(client, null, false);
+        bindProvider(client2, cpr2);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2073,13 +2104,16 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        bindProvider(app, client, null, null, false);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(client, cpr);
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
-        bindProvider(client, client2, null, null, false);
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        bindProvider(client2, app, null, null, false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        final ContentProviderRecord cpr2 = createContentProviderRecord(client, null, false);
+        bindProvider(client2, cpr2);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
+        final ContentProviderRecord cpr3 = createContentProviderRecord(client2, null, false);
+        bindProvider(app, cpr3);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2102,10 +2136,10 @@
                 mock(IBinder.class));
         bindService(app2, client2, null, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                 mock(IBinder.class));
-        client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setMaxAdj(client1, PERSISTENT_PROC_ADJ);
+        mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true);
 
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client1, client2, app1, app2);
 
         assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -2126,7 +2160,7 @@
         assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT);
 
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         updateOomAdj(client1, client2, app1, app2);
         assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ,
                 SCHED_GROUP_TOP_APP);
@@ -2136,7 +2170,7 @@
 
         bindService(client2, app1, null, null, 0, mock(IBinder.class));
         bindService(app1, client2, null, null, 0, mock(IBinder.class));
-        client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false);
+        mProcessStateController.setHasForegroundServices(client2.mServices, false, 0, false);
         updateOomAdj(app1, client1, client2);
         assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ,
                 SCHED_GROUP_TOP_APP);
@@ -2153,8 +2187,8 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         final ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        client2.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(client1, PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(client2, PERSISTENT_PROC_ADJ);
 
         final ServiceRecord s1 = bindService(app1, client1, null, null,
                 Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, mock(IBinder.class));
@@ -2178,10 +2212,10 @@
         s2.getConnections().clear();
         client1.mServices.removeAllConnections();
         client2.mServices.removeAllConnections();
-        client1.mState.setMaxAdj(UNKNOWN_ADJ);
-        client2.mState.setMaxAdj(UNKNOWN_ADJ);
-        client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        client2.mState.setHasOverlayUi(true);
+        mProcessStateController.setMaxAdj(client1, UNKNOWN_ADJ);
+        mProcessStateController.setMaxAdj(client2, UNKNOWN_ADJ);
+        mProcessStateController.setHasForegroundServices(client1.mServices, true, 0, true);
+        mProcessStateController.setHasOverlayUi(client2, true);
 
         bindService(app1, client1, null, s1, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
                 mock(IBinder.class));
@@ -2196,10 +2230,10 @@
         assertProcStates(app2, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT);
 
-        client2.mState.setHasOverlayUi(false);
+        mProcessStateController.setHasOverlayUi(client2, false);
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(client2).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         updateOomAdj(client2, app2);
         assertProcStates(app2, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
@@ -2213,10 +2247,10 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         final ProcessRecord client1 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
-        client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(client1, PERSISTENT_PROC_ADJ);
 
-        app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(app1.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
 
@@ -2234,10 +2268,10 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         final ProcessRecord client1 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
-        client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(client1, PERSISTENT_PROC_ADJ);
 
-        app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(app1.mServices, true, 0, true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE,
                 mock(IBinder.class));
@@ -2254,10 +2288,10 @@
     public void testUpdateOomAdj_DoOne_PendingFinishAttach() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.setPendingFinishAttach(true);
+        mProcessStateController.setPendingFinishAttach(app, true);
         app.mState.setHasForegroundActivities(false);
 
-        mService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+        mProcessStateController.setAttachingProcessStatesLSP(app);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
@@ -2269,11 +2303,11 @@
     public void testUpdateOomAdj_DoOne_TopApp_PendingFinishAttach() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.setPendingFinishAttach(true);
+        mProcessStateController.setPendingFinishAttach(app, true);
         app.mState.setHasForegroundActivities(true);
         doReturn(app).when(mService).getTopApp();
 
-        mService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+        mProcessStateController.setAttachingProcessStatesLSP(app);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ,
@@ -2303,40 +2337,40 @@
         client1.setUidRecord(clientUidRecord);
         client2.setUidRecord(clientUidRecord);
 
-        client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        client2.mState.setForcingToImportant(new Object());
+        mProcessStateController.setHasForegroundServices(client1.mServices, true, 0, true);
+        mProcessStateController.setForcingToImportant(client2, new Object());
         setProcessesToLru(app1, app2, app3, client1, client2);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         final ComponentName cn1 = ComponentName.unflattenFromString(
                 MOCKAPP_PACKAGENAME + "/.TestService");
         final ServiceRecord s1 = bindService(app1, client1, null, null, 0, mock(IBinder.class));
         setFieldValue(ServiceRecord.class, s1, "name", cn1);
-        s1.startRequested = true;
+        mProcessStateController.setStartRequested(s1, true);
 
         final ComponentName cn2 = ComponentName.unflattenFromString(
                 MOCKAPP2_PACKAGENAME + "/.TestService");
         final ServiceRecord s2 = bindService(app2, client2, null, null, 0, mock(IBinder.class));
         setFieldValue(ServiceRecord.class, s2, "name", cn2);
-        s2.startRequested = true;
+        mProcessStateController.setStartRequested(s2, true);
 
         final ComponentName cn3 = ComponentName.unflattenFromString(
                 MOCKAPP5_PACKAGENAME + "/.TestService");
         final ServiceRecord s3 = bindService(app3, client1, null, null, 0, mock(IBinder.class));
         setFieldValue(ServiceRecord.class, s3, "name", cn3);
-        s3.startRequested = true;
+        mProcessStateController.setStartRequested(s3, true);
 
         final ComponentName cn4 = ComponentName.unflattenFromString(
                 MOCKAPP3_PACKAGENAME + "/.TestService");
         final ServiceRecord c2s = makeServiceRecord(client2);
         setFieldValue(ServiceRecord.class, c2s, "name", cn4);
-        c2s.startRequested = true;
+        mProcessStateController.setStartRequested(c2s, true);
 
         try {
-            mService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord);
-            mService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
-            mService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
-            mService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord);
+            mActiveUids.put(MOCKAPP_UID, app1UidRecord);
+            mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
+            mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
+            mActiveUids.put(MOCKAPP3_UID, clientUidRecord);
 
             setServiceMap(s1, MOCKAPP_UID, cn1);
             setServiceMap(s2, MOCKAPP2_UID, cn2);
@@ -2354,8 +2388,8 @@
             assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app2.mState.getSetProcState());
             assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, client2.mState.getSetProcState());
 
-            client1.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false);
-            client2.mState.setForcingToImportant(null);
+            mProcessStateController.setHasForegroundServices(client1.mServices, false, 0, false);
+            mProcessStateController.setForcingToImportant(client2, null);
             app1UidRecord.reset();
             app2UidRecord.reset();
             app3UidRecord.reset();
@@ -2379,7 +2413,7 @@
                     .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
                             anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
             mService.mServices.mServiceMap.clear();
-            mService.mOomAdjuster.mActiveUids.clear();
+            mActiveUids.clear();
         }
     }
 
@@ -2388,12 +2422,13 @@
     public void testUpdateOomAdj_DoAll_Unbound() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.mState.setForcingToImportant(new Object());
         ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
 
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setForcingToImportant(app, new Object());
+        mProcessStateController.setHasForegroundServices(app2.mServices, true, 0, /* hasNoneType=*/
+                true);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -2408,12 +2443,14 @@
     public void testUpdateOomAdj_DoAll_BoundFgService() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
-        app.mState.setForcingToImportant(new Object());
         ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+
+        mProcessStateController.setForcingToImportant(app, new Object());
+        mProcessStateController.setHasForegroundServices(app2.mServices, true, 0, /* hasNoneType=*/
+                true);
         bindService(app, app2, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2435,9 +2472,9 @@
         ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app2, app3, null, null, 0, mock(IBinder.class));
-        app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(app3.mServices, true, 0, true);
         bindService(app3, app, null, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2, app3);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2476,13 +2513,13 @@
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.mState.setHasOverlayUi(true);
+        mProcessStateController.setHasOverlayUi(app4, true);
         bindService(app, app4, null, s, 0, mock(IBinder.class));
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(app5.mServices, true, 0, true);
         bindService(app, app5, null, s, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2, app3, app4, app5);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2518,13 +2555,13 @@
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.mState.setHasOverlayUi(true);
+        mProcessStateController.setHasOverlayUi(app4, true);
         bindService(app, app4, null, s, 0, mock(IBinder.class));
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(app5.mServices, true, 0, true);
         bindService(app, app5, null, s, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app5, app4, app3, app2, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2560,13 +2597,13 @@
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.mState.setHasOverlayUi(true);
+        mProcessStateController.setHasOverlayUi(app4, true);
         bindService(app, app4, null, s, 0, mock(IBinder.class));
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
+        mProcessStateController.setHasForegroundServices(app5.mServices, true, 0, true);
         bindService(app, app5, null, s, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app3, app4, app2, app, app5);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2603,10 +2640,10 @@
                 mock(IBinder.class));
         ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        mProcessStateController.setMaxAdj(client3, PERSISTENT_PROC_ADJ);
         bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES,
                 mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3);
 
         final int expected = PROCESS_CAPABILITY_ALL & ~PROCESS_CAPABILITY_BFSL;
@@ -2622,22 +2659,25 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
-        ContentProviderRecord cr = bindProvider(app, app2, null, null, false);
+        final ContentProviderRecord cpr = createContentProviderRecord(app, null, false);
+        bindProvider(app2, cpr);
         ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
-        bindProvider(app2, app3, null, null, false);
-        bindProvider(app3, app, null, null, false);
+        final ContentProviderRecord cpr2 = createContentProviderRecord(app2, null, false);
+        bindProvider(app3, cpr2);
+        final ContentProviderRecord cpr3 = createContentProviderRecord(app3, null, false);
+        bindProvider(app, cpr3);
         WindowProcessController wpc = app3.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
         ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
-        app4.mState.setHasOverlayUi(true);
-        bindProvider(app, app4, cr, null, false);
+        mProcessStateController.setHasOverlayUi(app4, true);
+        bindProvider(app4, cpr);
         ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        bindProvider(app, app5, cr, null, false);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setHasForegroundServices(app5.mServices, true, 0, true);
+        bindProvider(app5, cpr);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2, app3, app4, app5);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2666,22 +2706,22 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         long now = SystemClock.uptimeMillis();
         ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class));
-        s.startRequested = true;
-        s.lastActivity = now;
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, now);
         s = bindService(app2, app, null, null, 0, mock(IBinder.class));
-        s.startRequested = true;
-        s.lastActivity = now;
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, now);
         ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         s = mock(ServiceRecord.class);
-        s.app = app3;
+        mProcessStateController.setHostProcess(s, app3);
         setFieldValue(ServiceRecord.class, s, "connections",
                 new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
-        app3.mServices.startService(s);
+        mProcessStateController.startService(app3.mServices, s);
         doCallRealMethod().when(s).getConnections();
-        s.startRequested = true;
-        s.lastActivity = now;
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, now);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         mService.mOomAdjuster.mNumServiceProcs = 3;
         updateOomAdj(app3, app2, app);
 
@@ -2702,8 +2742,8 @@
 
         // cachedAdj1 and cachedAdj2 will be read if USE_TIERED_CACHED_ADJ is disabled. Otherwise,
         // sFirstUiCachedAdj and sFirstNonUiCachedAdj are used instead.
-        final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+        final int cachedAdj1 = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS;
+        final int cachedAdj2 = cachedAdj1 + CACHED_APP_IMPORTANCE_LEVELS * 2;
         doReturn(userOwner).when(mService.mUserController).getCurrentUserId();
 
         final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
@@ -2723,11 +2763,11 @@
         ServiceRecord s = spy(new ServiceRecord(mService, cn, cn, null, 0, null,
                 si, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
-        s.startRequested = true;
-        s.lastActivity = now;
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, now);
 
-        app.mServices.startService(s);
-        app.mState.setHasShownUi(true);
+        mProcessStateController.startService(app.mServices, s);
+        mProcessStateController.setHasShownUi(app, true);
 
         final ServiceInfo si2 = mock(ServiceInfo.class);
         si2.applicationInfo = mock(ApplicationInfo.class);
@@ -2735,13 +2775,14 @@
         ServiceRecord s2 = spy(new ServiceRecord(mService, cn2, cn2, null, 0, null,
                 si2, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections();
-        s2.startRequested = true;
-        s2.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+        mProcessStateController.setStartRequested(s2, true);
+        mProcessStateController.setServiceLastActivityTime(s2,
+                now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1);
 
-        app2.mServices.startService(s2);
-        app2.mState.setHasShownUi(false);
+        mProcessStateController.startService(app2.mServices, s2);
+        mProcessStateController.setHasShownUi(app2, false);
 
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
 
         assertProcStates(app, PROCESS_STATE_SERVICE,
@@ -2754,7 +2795,7 @@
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
         app.mState.setSetAdj(UNKNOWN_ADJ);
-        app.mState.setHasShownUi(false);
+        mProcessStateController.setHasShownUi(app, false);
         updateOomAdj();
 
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
@@ -2763,27 +2804,28 @@
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
         app.mState.setSetAdj(UNKNOWN_ADJ);
-        s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+        mProcessStateController.setServiceLastActivityTime(s,
+                now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1);
         updateOomAdj();
 
         assertProcStates(app, PROCESS_STATE_SERVICE,
                 mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
                 SCHED_GROUP_BACKGROUND, "cch-started-services", true);
 
-        app.mServices.stopService(s);
+        mProcessStateController.stopService(app.mServices, s);
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
         app.mState.setSetAdj(UNKNOWN_ADJ);
-        app.mState.setHasShownUi(true);
+        mProcessStateController.setHasShownUi(app, true);
         mService.mConstants.KEEP_WARMING_SERVICES.add(cn);
         mService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
         s = spy(new ServiceRecord(mService, cn, cn, null, 0, null,
                 si, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
-        s.startRequested = true;
-        s.lastActivity = now;
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, now);
 
-        app.mServices.startService(s);
+        mProcessStateController.startService(app.mServices, s);
         updateOomAdj();
 
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
@@ -2795,8 +2837,9 @@
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
         app.mState.setSetAdj(UNKNOWN_ADJ);
-        app.mState.setHasShownUi(false);
-        s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+        mProcessStateController.setHasShownUi(app, false);
+        mProcessStateController.setServiceLastActivityTime(s,
+                now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1);
         updateOomAdj();
 
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
@@ -2825,7 +2868,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, true));
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(app).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -2847,7 +2890,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(app).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -2873,14 +2916,14 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         long now = SystemClock.uptimeMillis();
         ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class));
-        s.startRequested = true;
-        s.lastActivity = now;
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, now);
         s = bindService(app2, app3, null, null, 0, mock(IBinder.class));
-        s.lastActivity = now;
+        mProcessStateController.setServiceLastActivityTime(s, now);
         s = bindService(app3, app2, null, null, 0, mock(IBinder.class));
-        s.lastActivity = now;
+        mProcessStateController.setServiceLastActivityTime(s, now);
 
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         mService.mOomAdjuster.mNumServiceProcs = 3;
         updateOomAdj(app, app2, app3);
 
@@ -2898,14 +2941,14 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
         doReturn(app).when(mService).getTopApp();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
 
         // Start binding to a service that isn't running yet.
         ServiceRecord sr = makeServiceRecord(app);
-        sr.app = null;
+        mProcessStateController.setHostProcess(sr, null);
         bindService(null, app, null, sr, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
 
         // Since sr.app is null, this service cannot be in the same process as the
@@ -2924,13 +2967,13 @@
 
         setProcessesToLru(app);
         ServiceRecord s = makeServiceRecord(app);
-        s.startRequested = true;
-        s.lastActivity = SystemClock.uptimeMillis();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
-        app.mServices.stopService(s);
+        mProcessStateController.stopService(app.mServices, s);
         updateOomAdj();
         // isolated process should be killed immediately after service stop.
         verify(app).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
@@ -2944,13 +2987,13 @@
                 MOCKAPP_ISOLATED_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
 
         ServiceRecord s = makeServiceRecord(app);
-        s.startRequested = true;
-        s.lastActivity = SystemClock.uptimeMillis();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdjPending(app);
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
-        app.mServices.stopService(s);
+        mProcessStateController.stopService(app.mServices, s);
         updateOomAdjPending(app);
         // isolated process should be killed immediately after service stop.
         verify(app).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
@@ -2966,13 +3009,13 @@
 
         setProcessesToLru(app);
         ServiceRecord s = makeServiceRecord(app);
-        s.startRequested = true;
-        s.lastActivity = SystemClock.uptimeMillis();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
-        app.mServices.stopService(s);
+        mProcessStateController.stopService(app.mServices, s);
         updateOomAdj();
         // isolated process with entry point should not be killed
         verify(app, never()).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
@@ -2987,13 +3030,13 @@
         app.setIsolatedEntryPoint("test");
 
         ServiceRecord s = makeServiceRecord(app);
-        s.startRequested = true;
-        s.lastActivity = SystemClock.uptimeMillis();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdjPending(app);
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
-        app.mServices.stopService(s);
+        mProcessStateController.stopService(app.mServices, s);
         updateOomAdjPending(app);
         // isolated process with entry point should not be killed
         verify(app, never()).killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
@@ -3014,10 +3057,10 @@
 
         setProcessesToLru(sandboxService, client, attributedClient);
 
-        client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        attributedClient.mServices.setHasForegroundServices(true, 0, true);
+        mProcessStateController.setMaxAdj(client, PERSISTENT_PROC_ADJ);
+        mProcessStateController.setHasForegroundServices(attributedClient.mServices, true, 0, true);
         bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class));
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
         assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ,
                 SCHED_GROUP_DEFAULT);
@@ -3038,13 +3081,13 @@
 
         // App1 binds to app2 and gets temp allowlisted.
         bindService(app2, app, null, null, 0, mock(IBinder.class));
-        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
+        mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
 
         assertEquals(true, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app.mOptRecord.shouldNotFreeze());
         assertEquals(true, app2.mOptRecord.shouldNotFreeze());
 
-        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
+        mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(false, app.mOptRecord.shouldNotFreeze());
         assertEquals(false, app2.mOptRecord.shouldNotFreeze());
@@ -3064,8 +3107,8 @@
         // App1 and app2 both bind to app3 and get temp allowlisted.
         bindService(app3, app, null, null, 0, mock(IBinder.class));
         bindService(app3, app2, null, null, 0, mock(IBinder.class));
-        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
-        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true);
+        mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
+        mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true);
 
         assertEquals(true, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app2.getUidRecord().isSetAllowListed());
@@ -3074,7 +3117,7 @@
         assertEquals(true, app3.mOptRecord.shouldNotFreeze());
 
         // Remove app1 from allowlist.
-        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
+        mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app2.getUidRecord().isSetAllowListed());
         assertEquals(false, app.mOptRecord.shouldNotFreeze());
@@ -3082,7 +3125,7 @@
         assertEquals(true, app3.mOptRecord.shouldNotFreeze());
 
         // Now remove app2 from allowlist.
-        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
+        mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(false, app2.getUidRecord().isSetAllowListed());
         assertEquals(false, app.mOptRecord.shouldNotFreeze());
@@ -3098,9 +3141,9 @@
 
         setProcessesToLru(app);
         ServiceRecord s = makeServiceRecord(app);
-        s.startRequested = true;
-        s.lastActivity = SystemClock.uptimeMillis();
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mProcessStateController.setStartRequested(s, true);
+        mProcessStateController.setServiceLastActivityTime(s, SystemClock.uptimeMillis());
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
                 "started-services");
@@ -3111,7 +3154,7 @@
         verify(mService.mHandler).sendEmptyMessageAtTime(
                 eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
-        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        mProcessStateController.runFollowUpUpdate();
 
         final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
                 ? sFirstNonUiCachedAdj : sFirstCachedAdj;
@@ -3131,9 +3174,9 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         app1.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
         app2.mProviders.setLastProviderTime(SystemClock.uptimeMillis() + 2000);
-        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         setProcessesToLru(app1, app2);
-        mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+        mProcessStateController.runFullUpdate(OOM_ADJ_REASON_NONE);
 
         assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
                 SCHED_GROUP_BACKGROUND, "recent-provider");
@@ -3146,7 +3189,7 @@
         verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
                 eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
-        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        mProcessStateController.runFollowUpUpdate();
 
         final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
                 ? sFirstNonUiCachedAdj : sFirstCachedAdj;
@@ -3156,7 +3199,7 @@
         verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
                 eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
-        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        mProcessStateController.runFollowUpUpdate();
         assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
                 "cch-empty");
     }
@@ -3169,12 +3212,12 @@
 
     private ServiceRecord makeServiceRecord(ProcessRecord app) {
         final ServiceRecord record = mock(ServiceRecord.class);
-        record.app = app;
+        mProcessStateController.setHostProcess(record, app);
         setFieldValue(ServiceRecord.class, record, "connections",
                 new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
         doCallRealMethod().when(record).getConnections();
         setFieldValue(ServiceRecord.class, record, "packageName", app.info.packageName);
-        app.mServices.startService(record);
+        mProcessStateController.startService(app.mServices, record);
         record.appInfo = app.info;
         setFieldValue(ServiceRecord.class, record, "bindings", new ArrayMap<>());
         setFieldValue(ServiceRecord.class, record, "pendingStarts", new ArrayList<>());
@@ -3213,27 +3256,55 @@
         doCallRealMethod().when(record).addConnection(any(IBinder.class),
                 any(ConnectionRecord.class));
         record.addConnection(binder, cr);
-        client.mServices.addConnection(cr);
+        mProcessStateController.addConnection(client.mServices, cr);
         binding.connections.add(cr);
         doNothing().when(cr).trackProcState(anyInt(), anyInt());
         return record;
     }
 
-    private ContentProviderRecord bindProvider(ProcessRecord publisher, ProcessRecord client,
-            ContentProviderRecord record, String name, boolean hasExternalProviders) {
-        if (record == null) {
-            record = mock(ContentProviderRecord.class);
-            publisher.mProviders.installProvider(name, record);
-            record.proc = publisher;
-            setFieldValue(ContentProviderRecord.class, record, "connections",
-                    new ArrayList<ContentProviderConnection>());
-            doReturn(hasExternalProviders).when(record).hasExternalProcessHandles();
+    private void setWakefulness(int state) {
+        if (Flags.pushGlobalStateToOomadjuster()) {
+            mProcessStateController.setWakefulness(state);
+        } else {
+            mService.mWakefulness.set(state);
         }
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private void setBackupTarget(ProcessRecord app) {
+        if (Flags.pushGlobalStateToOomadjuster()) {
+            mProcessStateController.setBackupTarget(app, app.userId);
+        } else {
+            BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
+            backupTarget.app = app;
+            doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
+        }
+    }
+
+    private ContentProviderRecord createContentProviderRecord(ProcessRecord publisher, String name,
+            boolean hasExternalProviders) {
+        ContentProviderRecord record = mock(ContentProviderRecord.class);
+        mProcessStateController.addPublishedProvider(publisher, name, record);
+        record.proc = publisher;
+        setFieldValue(ContentProviderRecord.class, record, "connections",
+                new ArrayList<ContentProviderConnection>());
+        doReturn(hasExternalProviders).when(record).hasExternalProcessHandles();
+        return record;
+    }
+
+    private ContentProviderConnection bindProvider(ProcessRecord client,
+            ContentProviderRecord record) {
         ContentProviderConnection conn = spy(new ContentProviderConnection(record, client,
                 client.info.packageName, UserHandle.getUserId(client.uid)));
         record.connections.add(conn);
-        client.mProviders.addProviderConnection(conn);
-        return record;
+        mProcessStateController.addProviderConnection(client, conn);
+        return conn;
+    }
+
+    private void unbindProvider(ProcessRecord client, ContentProviderRecord record,
+            ContentProviderConnection conn) {
+        record.connections.remove(conn);
+        mProcessStateController.removeProviderConnection(client, conn);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -3405,10 +3476,10 @@
             }
             providers.setLastProviderTime(mLastProviderTime);
 
-            UidRecord uidRec = mService.mOomAdjuster.mActiveUids.get(mUid);
+            UidRecord uidRec = mActiveUids.get(mUid);
             if (uidRec == null) {
                 uidRec = new UidRecord(mUid, mService);
-                mService.mOomAdjuster.mActiveUids.put(mUid, uidRec);
+                mActiveUids.put(mUid, uidRec);
             }
             uidRec.addProcess(app);
             app.setUidRecord(uidRec);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
index 1ff4a27..59302ee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -50,7 +50,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
@@ -169,6 +168,7 @@
         realAtm.initialize(null, null, mContext.getMainLooper());
         realAms.mActivityTaskManager = spy(realAtm);
         realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+        realAms.mProcessStateController = spy(realAms.mProcessStateController);
         realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
         realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer);
         realAms.mPackageManagerInt = mPackageManagerInt;
@@ -242,14 +242,14 @@
                 USER_SYSTEM              // userId
         ));
 
-        verify(mAms.mOomAdjuster, bindMode).updateOomAdjPendingTargetsLocked(anyInt());
-        clearInvocations(mAms.mOomAdjuster);
+        verify(mAms.mProcessStateController, bindMode).runPendingUpdate(anyInt());
+        clearInvocations(mAms.mProcessStateController);
 
         // Unbind the service.
         mAms.unbindService(serviceConnection);
 
-        verify(mAms.mOomAdjuster, unbindMode).updateOomAdjPendingTargetsLocked(anyInt());
-        clearInvocations(mAms.mOomAdjuster);
+        verify(mAms.mProcessStateController, unbindMode).runPendingUpdate(anyInt());
+        clearInvocations(mAms.mProcessStateController);
 
         removeProcessRecord(app);
     }
@@ -496,8 +496,8 @@
                 USER_SYSTEM            // userId
         ));
 
-        verify(mAms.mOomAdjuster, bindMode).updateOomAdjPendingTargetsLocked(anyInt());
-        clearInvocations(mAms.mOomAdjuster);
+        verify(mAms.mProcessStateController, bindMode).runPendingUpdate(anyInt());
+        clearInvocations(mAms.mProcessStateController);
 
         if (clientApp.isFreezable()) {
             verify(mAms.mOomAdjuster.mCachedAppOptimizer,
@@ -509,8 +509,8 @@
         // Unbind the service.
         mAms.unbindService(serviceConnection);
 
-        verify(mAms.mOomAdjuster, unbindMode).updateOomAdjPendingTargetsLocked(anyInt());
-        clearInvocations(mAms.mOomAdjuster);
+        verify(mAms.mProcessStateController, unbindMode).runPendingUpdate(anyInt());
+        clearInvocations(mAms.mProcessStateController);
 
         removeProcessRecord(clientApp);
         removeProcessRecord(serviceApp);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
index 1731590..026e72f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -31,12 +31,14 @@
 import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
 import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
 import static android.app.AppOpsManager.UID_STATE_TOP;
+import static android.permission.flags.Flags.delayUidStateChangesFromCapabilityUpdates;
 
 import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -325,6 +327,10 @@
                 .backgroundState()
                 .update();
 
+        assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+        assertEquals(MODE_IGNORED,
+                mIntf.evalMode(UID, OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO, MODE_FOREGROUND));
+
         procStateBuilder(UID)
                 .backgroundState()
                 .microphoneCapability()
@@ -342,10 +348,23 @@
                 .microphoneCapability()
                 .update();
 
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+        assertEquals(MODE_ALLOWED,
+                mIntf.evalMode(UID, OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO, MODE_FOREGROUND));
+
         procStateBuilder(UID)
                 .backgroundState()
                 .update();
 
+        if (delayUidStateChangesFromCapabilityUpdates()) {
+            mClock.advanceTime(mConstants.BG_STATE_SETTLE_TIME - 1);
+            assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+            assertEquals(MODE_ALLOWED,
+                    mIntf.evalMode(UID, OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
+                            MODE_FOREGROUND));
+
+            mClock.advanceTime(1);
+        }
         assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
         assertEquals(MODE_IGNORED,
                 mIntf.evalMode(UID, OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO, MODE_FOREGROUND));
@@ -357,6 +376,8 @@
                 .backgroundState()
                 .update();
 
+        assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+
         procStateBuilder(UID)
                 .backgroundState()
                 .cameraCapability()
@@ -372,10 +393,18 @@
                 .cameraCapability()
                 .update();
 
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+
         procStateBuilder(UID)
                 .backgroundState()
                 .update();
 
+        if (delayUidStateChangesFromCapabilityUpdates()) {
+            mClock.advanceTime(mConstants.BG_STATE_SETTLE_TIME - 1);
+            assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+
+            mClock.advanceTime(1);
+        }
         assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
     }
 
@@ -385,6 +414,9 @@
                 .backgroundState()
                 .update();
 
+        assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+        assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+
         procStateBuilder(UID)
                 .backgroundState()
                 .locationCapability()
@@ -401,15 +433,55 @@
                 .locationCapability()
                 .update();
 
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+
         procStateBuilder(UID)
                 .backgroundState()
                 .update();
 
+        if (delayUidStateChangesFromCapabilityUpdates()) {
+            mClock.advanceTime(mConstants.BG_STATE_SETTLE_TIME - 1);
+            assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+            assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+
+            mClock.advanceTime(1);
+        }
         assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
         assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
     }
 
     @Test
+    public void testProcStateChangesAndStaysUnrestrictedAndCapabilityRemoved() {
+        assumeTrue(delayUidStateChangesFromCapabilityUpdates());
+
+        procStateBuilder(UID)
+                .topState()
+                .microphoneCapability()
+                .cameraCapability()
+                .locationCapability()
+                .update();
+
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+
+        procStateBuilder(UID)
+                .foregroundState()
+                .update();
+
+        mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME - 1);
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+        assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+
+        mClock.advanceTime(1);
+        assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+        assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+        assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+    }
+
+    @Test
     public void testVisibleAppWidget() {
         procStateBuilder(UID)
                 .backgroundState()
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/OWNERS b/services/tests/mockingservicestests/src/com/android/server/job/OWNERS
index 6f207fb1..c8345f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/job/OWNERS
@@ -1 +1 @@
-include /apex/jobscheduler/OWNERS
+include /apex/jobscheduler/JOB_OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 584fd62..5c718d9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -76,9 +76,12 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
 import android.util.SparseBooleanArray;
@@ -89,12 +92,12 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 import com.android.server.PowerAllowlistInternal;
+import com.android.server.job.Flags;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobStore;
 import com.android.server.job.controllers.QuotaController.ExecutionStats;
 import com.android.server.job.controllers.QuotaController.QcConstants;
-import com.android.server.job.controllers.QuotaController.QuotaBump;
 import com.android.server.job.controllers.QuotaController.ShrinkableDebits;
 import com.android.server.job.controllers.QuotaController.TimedEvent;
 import com.android.server.job.controllers.QuotaController.TimingSession;
@@ -131,11 +134,11 @@
     private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     private static final int SOURCE_USER_ID = 0;
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private QuotaController mQuotaController;
     private QuotaController.QcConstants mQcConstants;
     private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
     private int mSourceUid;
-    private AppStandbyInternal.AppIdleStateChangeListener mAppIdleStateChangeListener;
     private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener;
     private IUidObserver mUidObserver;
     private UsageStatsManagerInternal.UsageEventListener mUsageEventListener;
@@ -190,8 +193,7 @@
         when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
         doReturn(mActivityMangerInternal)
                 .when(() -> LocalServices.getService(ActivityManagerInternal.class));
-        final AppStandbyInternal appStandbyInternal = mock(AppStandbyInternal.class);
-        doReturn(appStandbyInternal)
+        doReturn(mock(AppStandbyInternal.class))
                 .when(() -> LocalServices.getService(AppStandbyInternal.class));
         doReturn(mock(BatteryManagerInternal.class))
                 .when(() -> LocalServices.getService(BatteryManagerInternal.class));
@@ -238,8 +240,6 @@
 
         // Initialize real objects.
         // Capture the listeners.
-        ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> aiscListenerCaptor =
-                ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
         ArgumentCaptor<IUidObserver> uidObserverCaptor =
                 ArgumentCaptor.forClass(IUidObserver.class);
         ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor =
@@ -249,8 +249,6 @@
         mQuotaController = new QuotaController(mJobSchedulerService,
                 mock(BackgroundJobsController.class), mock(ConnectivityController.class));
 
-        verify(appStandbyInternal).addListener(aiscListenerCaptor.capture());
-        mAppIdleStateChangeListener = aiscListenerCaptor.getValue();
         verify(mPowerAllowlistInternal)
                 .registerTempAllowlistChangeListener(taChangeCaptor.capture());
         mTempAllowlistListener = taChangeCaptor.getValue();
@@ -303,6 +301,12 @@
         }
     }
 
+    private int getProcessStateQuotaFreeThreshold() {
+        synchronized (mQuotaController.mLock) {
+            return mQuotaController.getProcessStateQuotaFreeThreshold();
+        }
+    }
+
     private void setProcessState(int procState) {
         setProcessState(procState, mSourceUid);
     }
@@ -315,7 +319,7 @@
             final boolean contained = foregroundUids.get(uid);
             mUidObserver.onUidStateChanged(uid, procState, 0,
                     ActivityManager.PROCESS_CAPABILITY_NONE);
-            if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            if (procState <= getProcessStateQuotaFreeThreshold()) {
                 if (!contained) {
                     verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1))
                             .put(eq(uid), eq(true));
@@ -481,14 +485,12 @@
                 now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
         TimingSession two = createTimingSession(
                 now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
-        QuotaBump bump1 = new QuotaBump(now - 2 * HOUR_IN_MILLIS);
         TimingSession thr = createTimingSession(
                 now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
         // Overlaps 24 hour boundary.
         TimingSession fou = createTimingSession(
                 now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1);
         // Way past the 24 hour boundary.
-        QuotaBump bump2 = new QuotaBump(now - 24 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
         TimingSession fiv = createTimingSession(
                 now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4);
         List<TimedEvent> expectedRegular = new ArrayList<>();
@@ -496,16 +498,13 @@
         // Added in correct (chronological) order.
         expectedRegular.add(fou);
         expectedRegular.add(thr);
-        expectedRegular.add(bump1);
         expectedRegular.add(two);
         expectedRegular.add(one);
         expectedEJ.add(fou);
         expectedEJ.add(one);
         mQuotaController.saveTimingSession(0, "com.android.test", fiv, false);
-        mQuotaController.getTimingSessions(0, "com.android.test").add(bump2);
         mQuotaController.saveTimingSession(0, "com.android.test", fou, false);
         mQuotaController.saveTimingSession(0, "com.android.test", thr, false);
-        mQuotaController.getTimingSessions(0, "com.android.test").add(bump1);
         mQuotaController.saveTimingSession(0, "com.android.test", two, false);
         mQuotaController.saveTimingSession(0, "com.android.test", one, false);
         mQuotaController.saveTimingSession(0, "com.android.test", fiv, true);
@@ -935,22 +934,26 @@
      * Tests that getExecutionStatsLocked returns the correct stats.
      */
     @Test
-    public void testGetExecutionStatsLocked_Values() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetExecutionStatsLocked_Values_LegacyDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(0, "com.android.test",
-                createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - HOUR_IN_MILLIS),
+                        10 * MINUTE_IN_MILLIS, 5), false);
         mQuotaController.saveTimingSession(0, "com.android.test",
-                createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
+                        10 * MINUTE_IN_MILLIS, 5), false);
         mQuotaController.saveTimingSession(0, "com.android.test",
-                createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS,
+                        10 * MINUTE_IN_MILLIS, 5), false);
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
 
         ExecutionStats expectedStats = new ExecutionStats();
 
         // Active
-        expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
-        expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
         expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
         expectedStats.expirationTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
@@ -965,7 +968,7 @@
         }
 
         // Working
-        expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
         expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
         expectedStats.expirationTimeElapsed = now;
@@ -982,7 +985,7 @@
         }
 
         // Frequent
-        expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
         expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
         expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
@@ -1000,7 +1003,7 @@
         }
 
         // Rare
-        expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS;
         expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
         expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
         expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
@@ -1017,11 +1020,112 @@
         }
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Exempted
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+        expectedStats.expirationTimeElapsed = now + 14 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 5;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 1;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            EXEMPTED_INDEX));
+        }
+
+        // Active
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        // There is only one session in the past active bucket window, the empty time for this
+        // window is the bucket window size - duration of the session.
+        expectedStats.expirationTimeElapsed = now + 24 * MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            ACTIVE_INDEX));
+        }
+
+        // Working
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 10;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 2;
+        expectedStats.inQuotaTimeElapsed = now + 2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            WORKING_INDEX));
+        }
+
+        // Frequent
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 15;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 3;
+        expectedStats.inQuotaTimeElapsed = now + 10 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(
+                            0, "com.android.test", FREQUENT_INDEX));
+        }
+
+        // Rare
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 20;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 4;
+        expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            RARE_INDEX));
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
      */
     @Test
-    public void testGetExecutionStatsLocked_Values_BeginningOfTime() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetExecutionStatsLocked_Values_BeginningOfTime_LegacyDefaultBucketWindowSizes() {
         // Set time to 3 minutes after boot.
         advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
         advanceElapsedClock(3 * MINUTE_IN_MILLIS);
@@ -1044,7 +1148,8 @@
         expectedStats.sessionCountInWindow = 1;
         synchronized (mQuotaController.mLock) {
             assertEquals(expectedStats,
-                    mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            ACTIVE_INDEX));
         }
 
         // Working
@@ -1054,7 +1159,8 @@
         expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
         synchronized (mQuotaController.mLock) {
             assertEquals(expectedStats,
-                    mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            WORKING_INDEX));
         }
 
         // Frequent
@@ -1075,7 +1181,83 @@
         expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
         synchronized (mQuotaController.mLock) {
             assertEquals(expectedStats,
-                    mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            RARE_INDEX));
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() {
+        // Set time to 3 minutes after boot.
+        advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+        advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Exempted
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 2;
+        expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 2;
+        expectedStats.sessionCountInWindow = 1;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            EXEMPTED_INDEX));
+        }
+
+        // Active
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        expectedStats.expirationTimeElapsed = 20 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            ACTIVE_INDEX));
+        }
+
+        // Working
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
+        expectedStats.expirationTimeElapsed = 4 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            WORKING_INDEX));
+        }
+
+        // Frequent
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
+        expectedStats.expirationTimeElapsed = 12 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(
+                            0, "com.android.test", FREQUENT_INDEX));
+        }
+
+        // Rare
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
+        expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            RARE_INDEX));
         }
     }
 
@@ -1083,7 +1265,8 @@
      * Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
      */
     @Test
-    public void testGetExecutionStatsLocked_CoalescingSessions() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetExecutionStatsLocked_CoalescingSessions_LegacyDefaultBucketWindowSizes() {
         for (int i = 0; i < 10; ++i) {
             mQuotaController.saveTimingSession(0, "com.android.test",
                     createTimingSession(
@@ -1100,12 +1283,14 @@
                 advanceElapsedClock(54 * SECOND_IN_MILLIS);
                 mQuotaController.saveTimingSession(0, "com.android.test",
                         createTimingSession(
-                                JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+                                JobSchedulerService.sElapsedRealtimeClock.millis(),
+                                500, 1), false);
                 advanceElapsedClock(500);
                 advanceElapsedClock(400);
                 mQuotaController.saveTimingSession(0, "com.android.test",
                         createTimingSession(
-                                JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+                                JobSchedulerService.sElapsedRealtimeClock.millis(),
+                                100, 1), false);
                 advanceElapsedClock(100);
                 advanceElapsedClock(5 * SECOND_IN_MILLIS);
             }
@@ -1231,6 +1416,164 @@
         }
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() {
+        for (int i = 0; i < 20; ++i) {
+            mQuotaController.saveTimingSession(0, "com.android.test",
+                    createTimingSession(
+                            JobSchedulerService.sElapsedRealtimeClock.millis(),
+                            5 * MINUTE_IN_MILLIS, 5), false);
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            for (int j = 0; j < 5; ++j) {
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(),
+                                MINUTE_IN_MILLIS, 2), false);
+                advanceElapsedClock(MINUTE_IN_MILLIS);
+                advanceElapsedClock(54 * SECOND_IN_MILLIS);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+                advanceElapsedClock(500);
+                advanceElapsedClock(400);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+                advanceElapsedClock(100);
+                advanceElapsedClock(5 * SECOND_IN_MILLIS);
+            }
+            advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(64, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(192, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(320, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced
+            assertEquals(44, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced
+            assertEquals(132, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced
+            assertEquals(220, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(44, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(132, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(220, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                5 * SECOND_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced
+            assertEquals(28, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced
+            assertEquals(84, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced
+            assertEquals(140, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                MINUTE_IN_MILLIS);
+
+        // Only two TimingSessions there for every hour.
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(8, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(24, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(40, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                5 * MINUTE_IN_MILLIS);
+
+        // Only one TimingSessions there for every hour
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                15 * MINUTE_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+        // between an hour and 15 minutes.
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
      */
@@ -1245,7 +1588,8 @@
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
         mQuotaController.saveTimingSession(0, "com.android.test",
-                createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS,
+                        10 * MINUTE_IN_MILLIS, 5), false);
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
         final ExecutionStats originalStatsActive;
@@ -1371,7 +1715,7 @@
         }
 
         setDischarging();
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
@@ -1455,7 +1799,8 @@
     }
 
     @Test
-    public void testGetMaxJobExecutionTimeLocked_EJ() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetMaxJobExecutionTimeLocked_EJ_LegacyDefaultEJLimits() {
         final long timeUsedMs = 3 * MINUTE_IN_MILLIS;
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
@@ -1473,7 +1818,7 @@
         }
 
         setDischarging();
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
@@ -1505,7 +1850,7 @@
                 createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS,
                         timeUsedMs, 5), true);
 
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
@@ -1531,11 +1876,91 @@
         }
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetMaxJobExecutionTimeLocked_EJ_NewDefaultEJLimits() {
+        final long timeUsedMs = 3 * MINUTE_IN_MILLIS;
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
+                        timeUsedMs, 5), true);
+        JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0);
+        setStandbyBucket(RARE_INDEX, job);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+        }
+
+        setCharging();
+        synchronized (mQuotaController.mLock) {
+            assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
+
+        setDischarging();
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
+
+        // Top-started job
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(job);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
+
+        // Test used quota rolling out of window.
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+        }
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS,
+                        timeUsedMs, 5), true);
+
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        synchronized (mQuotaController.mLock) {
+            // max of 50% WORKING limit and remaining quota
+            assertEquals(10 * MINUTE_IN_MILLIS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
+
+        // Top-started job
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+            mQuotaController.prepareForExecutionLocked(job);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
+    }
+
     /**
      * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size.
      */
     @Test
-    public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
@@ -1559,27 +1984,56 @@
         }
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
+                false);
+
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+                20 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
+        // window size = allowed time, so jobs can essentially run non-stop until they reach the
+        // max execution time.
+        setStandbyBucket(EXEMPTED_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(10 * MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
     /**
      * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
      * window.
      */
     @Test
-    public void testGetTimeUntilQuotaConsumedLocked_BucketWindow() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_LegacyDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Close to RARE boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS),
                         30 * SECOND_IN_MILLIS, 5), false);
         // Far away from FREQUENT boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS -  HOUR_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
         // Overlap WORKING_SET boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS),
                         3 * MINUTE_IN_MILLIS, 5), false);
         // Close to ACTIVE boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS -  MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
 
         setStandbyBucket(RARE_INDEX);
         synchronized (mQuotaController.mLock) {
@@ -1624,6 +2078,69 @@
         }
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Close to RARE boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS),
+                        30 * SECOND_IN_MILLIS, 5), false);
+        // Far away from FREQUENT boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS -  HOUR_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
+        // Overlap WORKING_SET boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
+        // Close to ACTIVE boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS -  MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
+
+        setStandbyBucket(RARE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(30 * SECOND_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+
+        setStandbyBucket(FREQUENT_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+
+        setStandbyBucket(WORKING_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(5 * MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(7 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+
+        // ACTIVE window != allowed time.
+        setStandbyBucket(ACTIVE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(7 * MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(10 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
     /**
      * Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
      */
@@ -1651,7 +2168,7 @@
         // Close to boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(now - (24 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS),
-                        4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5), false);
+                        mQcConstants.MAX_EXECUTION_TIME_MS - 5 * MINUTE_IN_MILLIS, 5), false);
 
         setStandbyBucket(WORKING_INDEX);
         synchronized (mQuotaController.mLock) {
@@ -1667,7 +2184,8 @@
         // Far from boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5),
+                        now - (20 * HOUR_IN_MILLIS),
+                        mQcConstants.MAX_EXECUTION_TIME_MS - 3 * MINUTE_IN_MILLIS, 5),
                 false);
 
         setStandbyBucket(WORKING_INDEX);
@@ -1694,11 +2212,12 @@
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
                         now - (24 * HOUR_IN_MILLIS + 11 * MINUTE_IN_MILLIS),
-                        4 * HOUR_IN_MILLIS,
+                        mQcConstants.MAX_EXECUTION_TIME_MS,
                         5), false);
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5),
+                        now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS + MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5),
                 false);
 
         synchronized (mQuotaController.mLock) {
@@ -1722,11 +2241,12 @@
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
                         now - (20 * HOUR_IN_MILLIS),
-                        3 * HOUR_IN_MILLIS + 48 * MINUTE_IN_MILLIS,
+                        mQcConstants.MAX_EXECUTION_TIME_MS - 12 * MINUTE_IN_MILLIS,
                         5), false);
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5),
+                        now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS + MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5),
                 false);
 
         synchronized (mQuotaController.mLock) {
@@ -1745,7 +2265,8 @@
      * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size.
      */
     @Test
-    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(now - (24 * HOUR_IN_MILLIS),
@@ -1771,186 +2292,170 @@
         }
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (24 * HOUR_IN_MILLIS),
+                        mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5),
+                false);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5),
+                false);
+
+        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+                20 * MINUTE_IN_MILLIS);
+        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
+        // window size != allowed time.
+        setStandbyBucket(EXEMPTED_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
     /**
      * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
      * window and the session is rolling out of the window.
      */
     @Test
-    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow() {
+    @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_LegacyDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (24 * HOUR_IN_MILLIS),
-                        10 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false);
         setStandbyBucket(RARE_INDEX);
         synchronized (mQuotaController.mLock) {
             assertEquals(0,
                     mQuotaController.getRemainingExecutionTimeLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(10 * MINUTE_IN_MILLIS,
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS,
                     mQuotaController.getTimeUntilQuotaConsumedLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
         }
 
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (8 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false);
         setStandbyBucket(FREQUENT_INDEX);
         synchronized (mQuotaController.mLock) {
             assertEquals(0,
                     mQuotaController.getRemainingExecutionTimeLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(10 * MINUTE_IN_MILLIS,
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
                     mQuotaController.getTimeUntilQuotaConsumedLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
         }
 
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (2 * HOUR_IN_MILLIS),
-                        10 * MINUTE_IN_MILLIS, 5), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false);
         setStandbyBucket(WORKING_INDEX);
         synchronized (mQuotaController.mLock) {
             assertEquals(0,
                     mQuotaController.getRemainingExecutionTimeLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(10 * MINUTE_IN_MILLIS,
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS,
                     mQuotaController.getTimeUntilQuotaConsumedLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
         }
 
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5), false);
+        // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
+        // max execution time.
+        setStandbyBucket(ACTIVE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS
+                            - (mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS
+                                    + mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS
+                                    + mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS),
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_NewDefaultBucketWindowSizes() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false);
+        setStandbyBucket(RARE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false);
+        setStandbyBucket(FREQUENT_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false);
+        setStandbyBucket(WORKING_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(0,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5),
                 false);
-        // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
-        // max execution time.
+        // ACTIVE window != allowed time.
         setStandbyBucket(ACTIVE_INDEX);
         synchronized (mQuotaController.mLock) {
             assertEquals(0,
                     mQuotaController.getRemainingExecutionTimeLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
-    /**
-     * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
-     * window and there are valid QuotaBumps in the history.
-     */
-    @Test
-    public void testGetTimeUntilQuotaConsumedLocked_QuotaBump() {
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        // Close to RARE boundary.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
-                        30 * SECOND_IN_MILLIS, 5), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS));
-        // Far away from FREQUENT boundary.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
-        // Overlap WORKING_SET boundary.
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 2 * HOUR_IN_MILLIS));
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
-                        3 * MINUTE_IN_MILLIS, 5), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS));
-        // Close to ACTIVE boundary.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
-
-        setStandbyBucket(RARE_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(3 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(4 * MINUTE_IN_MILLIS,
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
                     mQuotaController.getTimeUntilQuotaConsumedLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
         }
 
-        setStandbyBucket(FREQUENT_INDEX);
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_EXEMPTED_MS,
+                        mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 5),
+                false);
+        // EXEMPTED window != allowed time
+        setStandbyBucket(EXEMPTED_INDEX);
         synchronized (mQuotaController.mLock) {
-            assertEquals(4 * MINUTE_IN_MILLIS,
+            assertEquals(0,
                     mQuotaController.getRemainingExecutionTimeLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(4 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-
-        setStandbyBucket(WORKING_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(8 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(10 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-
-        // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
-        // max execution time.
-        setStandbyBucket(ACTIVE_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(10 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
-    /**
-     * Test getTimeUntilQuotaConsumedLocked when there are valid QuotaBumps in recent history that
-     * provide enough additional quota to bridge gaps between sessions.
-     */
-    @Test
-    public void testGetTimeUntilQuotaConsumedLocked_QuotaBump_CrucialBumps() {
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (25 * HOUR_IN_MILLIS),
-                        30 * MINUTE_IN_MILLIS, 25), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS));
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS));
-        // Without the valid quota bumps, the app would only 3 minutes until the quota was consumed.
-        // The quota bumps provide enough quota to bridge the gap between the two earliest sessions.
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (8 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (8 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS),
-                        2 * MINUTE_IN_MILLIS, 5), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
-                        3 * MINUTE_IN_MILLIS, 1), false);
-        mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
-                .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS));
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (9 * MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 1), false);
-
-        setStandbyBucket(FREQUENT_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(2 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(7 * MINUTE_IN_MILLIS,
+            assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
                     mQuotaController.getTimeUntilQuotaConsumedLocked(
                             SOURCE_USER_ID, SOURCE_PACKAGE));
         }
@@ -2302,270 +2807,6 @@
         }
     }
 
-    @Test
-    public void testIsWithinQuotaLocked_WithQuotaBump_Duration() {
-        setDischarging();
-        int standbyBucket = WORKING_INDEX;
-        setStandbyBucket(standbyBucket);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
-                5 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
-        long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(
-                        now - (HOUR_IN_MILLIS - 2 * MINUTE_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1),
-                false);
-        final ExecutionStats stats;
-        synchronized (mQuotaController.mLock) {
-            stats = mQuotaController.getExecutionStatsLocked(
-                    SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
-            mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 1);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(5 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        advanceElapsedClock(HOUR_IN_MILLIS);
-
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        // Emulate a quota bump while some jobs are executing
-        JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 1);
-        JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 2);
-
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job1, null);
-            mQuotaController.prepareForExecutionLocked(job1);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-            mQuotaController.maybeStartTrackingJobLocked(job2, null);
-            mQuotaController.prepareForExecutionLocked(job2);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null);
-            mQuotaController.maybeStopTrackingJobLocked(job2, null);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        // Phase out the first session
-        advanceElapsedClock(5 * MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-
-        // Phase out the first quota bump
-        advanceElapsedClock(7 * HOUR_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
-        }
-    }
-
-    @Test
-    public void testIsWithinQuotaLocked_WithQuotaBump_JobCount() {
-        setDischarging();
-        int standbyBucket = WORKING_INDEX;
-        setStandbyBucket(standbyBucket);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 1);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
-        long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 10), false);
-        final ExecutionStats stats;
-        synchronized (mQuotaController.mLock) {
-            stats = mQuotaController.getExecutionStatsLocked(
-                    SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
-            mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 10);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(10, stats.jobCountLimit);
-        }
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(11, stats.jobCountLimit);
-        }
-
-        advanceElapsedClock(HOUR_IN_MILLIS);
-
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(11, stats.jobCountLimit);
-        }
-
-        // Emulate a quota bump while some jobs are executing
-        JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1);
-        JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2);
-
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job1, null);
-            mQuotaController.prepareForExecutionLocked(job1);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(12, stats.jobCountLimit);
-            mQuotaController.maybeStartTrackingJobLocked(job2, null);
-            mQuotaController.prepareForExecutionLocked(job2);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null);
-            mQuotaController.maybeStopTrackingJobLocked(job2, null);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(12, stats.jobCountLimit);
-        }
-
-        // Phase out the first session
-        advanceElapsedClock(3 * MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(12, stats.jobCountLimit);
-        }
-
-        // Phase out the first quota bump
-        advanceElapsedClock(7 * HOUR_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(11, stats.jobCountLimit);
-        }
-    }
-
-    @Test
-    public void testIsWithinQuotaLocked_WithQuotaBump_SessionCount() {
-        setDischarging();
-        int standbyBucket = WORKING_INDEX;
-        setStandbyBucket(standbyBucket);
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 2);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 1);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
-        long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
-        final ExecutionStats stats;
-        synchronized (mQuotaController.mLock) {
-            stats = mQuotaController.getExecutionStatsLocked(
-                    SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(2, stats.sessionCountLimit);
-        }
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(3, stats.sessionCountLimit);
-        }
-
-        advanceElapsedClock(HOUR_IN_MILLIS);
-
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(3, stats.sessionCountLimit);
-        }
-
-        // Emulate a quota bump while some jobs are executing
-        JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1);
-        JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2);
-
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job1, null);
-            mQuotaController.prepareForExecutionLocked(job1);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job1, null);
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(4, stats.sessionCountLimit);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStartTrackingJobLocked(job2, null);
-            mQuotaController.prepareForExecutionLocked(job2);
-        }
-
-        advanceElapsedClock(MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            mQuotaController.maybeStopTrackingJobLocked(job2, null);
-            assertFalse(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(4, stats.sessionCountLimit);
-        }
-
-        // Phase out the first session
-        advanceElapsedClock(2 * MINUTE_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(4, stats.sessionCountLimit);
-        }
-
-        // Phase out the first quota bump
-        advanceElapsedClock(7 * HOUR_IN_MILLIS);
-        synchronized (mQuotaController.mLock) {
-            assertTrue(mQuotaController
-                    .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
-            assertEquals(3, stats.sessionCountLimit);
-        }
-    }
 
     @Test
     public void testIsWithinEJQuotaLocked_NeverApp() {
@@ -2928,12 +3169,12 @@
                 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
-        final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
+        final long end = now - (mQcConstants.WINDOW_SIZE_WORKING_MS - 5 * MINUTE_IN_MILLIS);
         // Counting backwards, the quota will come back one minute before the end.
-        final long expectedAlarmTime =
-                end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
+        final long expectedAlarmTime = end - MINUTE_IN_MILLIS + mQcConstants.WINDOW_SIZE_WORKING_MS
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1), false);
+                new TimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, end, 1), false);
         synchronized (mQuotaController.mLock) {
             mQuotaController.maybeScheduleStartAlarmLocked(
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
@@ -3000,7 +3241,8 @@
         // Test with timing sessions out of window.
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS
+                        - 2 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
         synchronized (mQuotaController.mLock) {
             mQuotaController.maybeScheduleStartAlarmLocked(
                     SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
@@ -3009,8 +3251,9 @@
                 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
-        final long start = now - (6 * HOUR_IN_MILLIS);
-        final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
+        final long start = now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - 2 * HOUR_IN_MILLIS);
+        final long expectedAlarmTime = start + mQcConstants.WINDOW_SIZE_FREQUENT_MS
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false);
         synchronized (mQuotaController.mLock) {
@@ -3084,7 +3327,8 @@
         // Test with timing sessions out of window.
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS
+                                - 2 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
         synchronized (mQuotaController.mLock) {
             mQuotaController.maybeScheduleStartAlarmLocked(
                     SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket);
@@ -3093,8 +3337,9 @@
                 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions in window but still in quota.
-        final long start = now - (6 * HOUR_IN_MILLIS);
-        final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
+        final long start = now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - 2 * HOUR_IN_MILLIS);
+        final long expectedAlarmTime = start + mQcConstants.WINDOW_SIZE_FREQUENT_MS
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false);
         synchronized (mQuotaController.mLock) {
@@ -3266,7 +3511,7 @@
 
         // And down from there.
         final long expectedWorkingAlarmTime =
-                outOfQuotaTime + (2 * HOUR_IN_MILLIS)
+                outOfQuotaTime + mQcConstants.WINDOW_SIZE_WORKING_MS
                         + mQcConstants.IN_QUOTA_BUFFER_MS;
         setStandbyBucket(WORKING_INDEX, jobStatus);
         synchronized (mQuotaController.mLock) {
@@ -3278,7 +3523,7 @@
                 eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedFrequentAlarmTime =
-                outOfQuotaTime + (8 * HOUR_IN_MILLIS)
+                outOfQuotaTime + mQcConstants.WINDOW_SIZE_FREQUENT_MS
                         + mQcConstants.IN_QUOTA_BUFFER_MS;
         setStandbyBucket(FREQUENT_INDEX, jobStatus);
         synchronized (mQuotaController.mLock) {
@@ -3290,7 +3535,7 @@
                 eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedRareAlarmTime =
-                outOfQuotaTime + (24 * HOUR_IN_MILLIS)
+                outOfQuotaTime + mQcConstants.WINDOW_SIZE_RARE_MS
                         + mQcConstants.IN_QUOTA_BUFFER_MS;
         setStandbyBucket(RARE_INDEX, jobStatus);
         synchronized (mQuotaController.mLock) {
@@ -3450,7 +3695,7 @@
         }
 
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        // Working set window size is 2 hours.
+        // Working set window size is configured with QcConstants.WINDOW_SIZE_WORKING_MS.
         final int standbyBucket = WORKING_INDEX;
         final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2;
         final long remainingTimeMs =
@@ -3459,13 +3704,14 @@
         // Session straddles edge of bucket window. Only the contribution should be counted towards
         // the quota.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS),
-                        3 * MINUTE_IN_MILLIS + contributionMs, 3), false);
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS
+                        - 3 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS + contributionMs,
+                        3), false);
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2), false);
         // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
         // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
-        final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS
+        final long expectedAlarmTime = now - HOUR_IN_MILLIS + mQcConstants.WINDOW_SIZE_WORKING_MS
                 + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs);
         synchronized (mQuotaController.mLock) {
             mQuotaController.maybeScheduleStartAlarmLocked(
@@ -3569,12 +3815,6 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS,
                 84 * SECOND_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
-                93 * SECOND_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 92);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 91);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 90 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 89);
 
         assertEquals(8 * MINUTE_IN_MILLIS,
                 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3632,11 +3872,6 @@
         assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
         assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
         assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
-        assertEquals(93 * SECOND_IN_MILLIS, mQuotaController.getQuotaBumpAdditionDurationMs());
-        assertEquals(92, mQuotaController.getQuotaBumpAdditionJobCount());
-        assertEquals(91, mQuotaController.getQuotaBumpAdditionSessionCount());
-        assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
-        assertEquals(89, mQuotaController.getQuotaBumpLimit());
     }
 
     @Test
@@ -3689,11 +3924,6 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, -1);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, -1);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, -1);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 59 * MINUTE_IN_MILLIS);
-        setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, -1);
 
         assertEquals(MINUTE_IN_MILLIS,
                 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3744,11 +3974,6 @@
         assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs());
         assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs());
         assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs());
-        assertEquals(0, mQuotaController.getQuotaBumpAdditionDurationMs());
-        assertEquals(0, mQuotaController.getQuotaBumpAdditionJobCount());
-        assertEquals(0, mQuotaController.getQuotaBumpAdditionSessionCount());
-        assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
-        assertEquals(0, mQuotaController.getQuotaBumpLimit());
 
         // Invalid configurations.
         // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
@@ -3806,7 +4031,6 @@
         setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS);
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS);
 
         assertEquals(24 * HOUR_IN_MILLIS,
                 mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3847,7 +4071,6 @@
         assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
         assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
         assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
-        assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
     }
 
     /** Tests that TimingSessions aren't saved when the device is charging. */
@@ -4126,7 +4349,7 @@
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         // Change to a state that should still be considered foreground.
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
         synchronized (mQuotaController.mLock) {
             mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
@@ -4134,6 +4357,36 @@
         assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
+    /** Tests that Timers count FOREGROUND_SERVICE jobs. */
+    @Test
+    @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS)
+    public void testTimerTracking_Fgs() {
+        setDischarging();
+
+        JobStatus jobStatus = createJobStatus("testTimerTracking_Fgs", 1);
+        setProcessState(ActivityManager.PROCESS_STATE_BOUND_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+        }
+
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobStatus);
+        }
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        // Change to FOREGROUND_SERVICE state that should count.
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(jobStatus, null);
+        }
+        List<TimingSession> expected = new ArrayList<>();
+        expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+    }
+
     /**
      * Tests that Timers properly track sessions when switching between foreground and background
      * states.
@@ -4180,7 +4433,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobFg3);
         }
@@ -4213,7 +4466,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobFg3);
         }
@@ -4262,7 +4515,7 @@
         }
         assertEquals(0, stats.jobCountInRateLimitingWindow);
 
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobFg1);
         }
@@ -4412,7 +4665,7 @@
             mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobFg1);
         }
@@ -4625,7 +4878,7 @@
 
         // App still in foreground so everything should be in quota.
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
         assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
         assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -4697,7 +4950,7 @@
         // The package only has one second to run, but this session is at the edge of the rolling
         // window, so as the package "reaches its quota" it will have more to keep running.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - 2 * HOUR_IN_MILLIS,
+                createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS,
                         10 * SECOND_IN_MILLIS - remainingTimeMs, 1), false);
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(now - HOUR_IN_MILLIS,
@@ -5901,7 +6154,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobFg3);
         }
@@ -5935,7 +6188,7 @@
         }
         advanceElapsedClock(10 * SECOND_IN_MILLIS);
         expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobFg3);
         }
@@ -6056,7 +6309,7 @@
             mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
         }
         advanceElapsedClock(5 * SECOND_IN_MILLIS);
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         synchronized (mQuotaController.mLock) {
             mQuotaController.prepareForExecutionLocked(jobFg1);
         }
@@ -6534,7 +6787,7 @@
 
         // App still in foreground so everything should be in quota.
         advanceElapsedClock(20 * SECOND_IN_MILLIS);
-        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        setProcessState(getProcessStateQuotaFreeThreshold());
         assertTrue(jobTop2.isExpeditedQuotaApproved());
         assertTrue(jobFg.isExpeditedQuotaApproved());
         assertTrue(jobBg.isExpeditedQuotaApproved());
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 9ba2724..625dbe6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -18,6 +18,7 @@
 
 import static android.media.AudioAttributes.USAGE_ALARM;
 
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -91,6 +92,7 @@
     }
     @Test
     public void testAlarmOnBackgroundUser_foregroundUserNotified() throws RemoteException {
+        assumeTrue(UserManager.supportsMultipleUsers());
         AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
         UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
         final int fgUserId = mSpiedContext.getUserId();
@@ -100,7 +102,7 @@
                 /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
                 AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
         clearInvocations(mNotificationManager);
-        mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+        mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
         verify(mNotificationManager)
                 .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
                         eq(afi.getClientUid()), any(Notification.class),
@@ -116,7 +118,7 @@
                 /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
                 AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
         clearInvocations(mNotificationManager);
-        mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+        mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
         verifyZeroInteractions(mNotificationManager);
     }
 
@@ -131,7 +133,7 @@
                 AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0,
                 Build.VERSION.SDK_INT);
         clearInvocations(mNotificationManager);
-        mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+        mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
         verifyZeroInteractions(mNotificationManager);
     }
 
@@ -169,7 +171,7 @@
         doReturn(focusStack).when(mockAudioPolicy).getFocusStack();
         mBackgroundUserSoundNotifier.mFocusControlAudioPolicy = mockAudioPolicy;
 
-        mBackgroundUserSoundNotifier.muteAlarmSounds(mSpiedContext);
+        mBackgroundUserSoundNotifier.muteAlarmSounds(bgUserUid);
 
         verify(apc1.getPlayerProxy()).stop();
         verify(mockAudioPolicy).sendFocusLossAndUpdate(afi);
@@ -178,6 +180,7 @@
 
     @Test
     public void testOnAudioFocusGrant_alarmOnBackgroundUser_notifiesForegroundUser() {
+        assumeTrue(UserManager.supportsMultipleUsers());
         final int fgUserId = mSpiedContext.getUserId();
         UserInfo bgUser = createUser("Background User",  UserManager.USER_TYPE_FULL_SECONDARY, 0);
         int bgUserUid = bgUser.id * 100000;
@@ -205,7 +208,7 @@
                 .when(mUserManager).getUserSwitchability(any());
 
         Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
-                mSpiedContext);
+                mSpiedContext, 101000);
 
         assertEquals("Alarm for BgUser", notification.extras.getString(
                 Notification.EXTRA_TITLE));
@@ -232,7 +235,7 @@
                 .when(mUserManager).getUserSwitchability(any());
 
         Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
-                mSpiedContext);
+                mSpiedContext, 101000);
 
         assertEquals(1, notification.actions.length);
         assertEquals(mSpiedContext.getString(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index e131a98..de029e0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -185,7 +185,8 @@
         verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
         verify(broadcastHelper, never()).sendPackageBroadcast(eq(
                 Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
-                nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+                nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable(),
+                nullable())
 
         distractingPackageHelper.removeDistractingPackageRestrictions(pms.snapshotComputer(),
                 arrayOfNulls(0), TEST_USER_ID)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 39acd8d..124c41e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -72,7 +72,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Predicate;
 
@@ -486,7 +485,7 @@
             FakeStagedSession session = new FakeStagedSession(239);
             session.setIsApex(true);
             // Call and verify
-            Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+            var result = mStagingManager.getStagedApexInfos(session);
             assertThat(result).isEmpty();
         }
         // Invalid session: destroyed
@@ -496,7 +495,7 @@
             session.setIsApex(true);
             session.setDestroyed(true);
             // Call and verify
-            Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+            var result = mStagingManager.getStagedApexInfos(session);
             assertThat(result).isEmpty();
         }
     }
@@ -520,8 +519,8 @@
         when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
 
         // Call and verify
-        Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(validSession);
-        assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0]);
+        List<ApexInfo> result = mStagingManager.getStagedApexInfos(validSession);
+        assertThat(result).containsExactly(fakeApexInfos[0]);
 
         ArgumentCaptor<ApexSessionParams> argumentCaptor =
                 ArgumentCaptor.forClass(ApexSessionParams.class);
@@ -544,9 +543,8 @@
         when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
 
         // Call and verify
-        Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession);
-        assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0],
-                fakeApexInfos[1].moduleName, fakeApexInfos[1]);
+        List<ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession);
+        assertThat(result).containsExactly(fakeApexInfos[0], fakeApexInfos[1]);
 
         ArgumentCaptor<ApexSessionParams> argumentCaptor =
                 ArgumentCaptor.forClass(ApexSessionParams.class);
@@ -557,7 +555,7 @@
     }
 
     @Test
-    public void getStagedApexModuleNames_returnsStagedApexModules() throws Exception {
+    public void getStagedApexInfos_returnsStagedApexModules() throws Exception {
         FakeStagedSession validSession1 = new FakeStagedSession(239);
         validSession1.setIsApex(true);
         validSession1.setSessionReady();
@@ -575,8 +573,8 @@
 
         mockApexManagerGetStagedApexInfoWithSessionId();
 
-        List<String> result = mStagingManager.getStagedApexModuleNames();
-        assertThat(result).containsExactly("239", "123", "124");
+        List<StagedApexInfo> result = mStagingManager.getStagedApexInfos();
+        assertThat(result).containsExactly((Object[]) fakeStagedApexInfos("239", "123", "124"));
         verify(mApexManager, times(2)).getStagedApexInfos(any());
     }
 
@@ -605,26 +603,12 @@
         });
     }
 
-    @Test
-    public void getStagedApexInfo() throws Exception {
-        FakeStagedSession validSession1 = new FakeStagedSession(239);
-        validSession1.setIsApex(true);
-        validSession1.setSessionReady();
-        mStagingManager.createSession(validSession1);
-        ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
-        when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
-
-        // Verify null is returned if module name is not found
-        StagedApexInfo result = mStagingManager.getStagedApexInfo("not found");
-        assertThat(result).isNull();
-        verify(mApexManager, times(1)).getStagedApexInfos(any());
-        // Otherwise, the correct object is returned
-        result = mStagingManager.getStagedApexInfo("module1");
-        assertThat(result.moduleName).isEqualTo(fakeApexInfos[0].moduleName);
-        assertThat(result.diskImagePath).isEqualTo(fakeApexInfos[0].modulePath);
-        assertThat(result.versionCode).isEqualTo(fakeApexInfos[0].versionCode);
-        assertThat(result.versionName).isEqualTo(fakeApexInfos[0].versionName);
-        verify(mApexManager, times(2)).getStagedApexInfos(any());
+    private StagedApexInfo[] fakeStagedApexInfos(String... moduleNames) {
+        return Arrays.stream(moduleNames).map(moduleName -> {
+            StagedApexInfo info = new StagedApexInfo();
+            info.moduleName = moduleName;
+            return info;
+        }).toArray(StagedApexInfo[]::new);
     }
 
     @Test
@@ -646,8 +630,8 @@
             ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
                     ApexStagedEvent.class);
             verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
-            assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
-                    new String[]{"239"});
+            assertThat(argumentCaptor.getValue().stagedApexInfos).isEqualTo(
+                    fakeStagedApexInfos("239"));
         }
 
         // Create another staged session and verify observers are notified of union
@@ -662,8 +646,8 @@
             ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
                     ApexStagedEvent.class);
             verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
-            assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
-                    new String[]{"239", "240"});
+            assertThat(argumentCaptor.getValue().stagedApexInfos).isEqualTo(
+                    fakeStagedApexInfos("239", "240"));
         }
 
         // Finally, verify that once unregistered, observer is not notified
@@ -699,7 +683,7 @@
         ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
                 ApexStagedEvent.class);
         verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
-        assertThat(argumentCaptor.getValue().stagedApexModuleNames).hasLength(0);
+        assertThat(argumentCaptor.getValue().stagedApexInfos).hasLength(0);
     }
 
     @Test
@@ -757,7 +741,8 @@
                 /* stagedSessionErrorCode */ PackageManager.INSTALL_UNKNOWN,
                 /* stagedSessionErrorMessage */ "no error",
                 /* preVerifiedDomains */ null,
-                /* verifierController */ null);
+                /* verifierController */ null,
+                /* verificationPolicy */ PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED);
 
         StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
         doReturn(packageName).when(stagedSession).getPackageName();
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 9983fb4..c099517 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -506,6 +506,7 @@
     }
 
     @Test
+    @Ignore("b/372942682")
     public void testWallpaperManagerCallbackInRightOrder() throws RemoteException {
         WallpaperData wallpaper = new WallpaperData(USER_SYSTEM, FLAG_SYSTEM);
         wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.RED),
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 639ae30..58489f3 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -81,7 +81,6 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -581,8 +580,6 @@
         HintManagerService service = createService();
         IBinder token = new Binder();
         int threadCount = 2;
-
-        // session 1 has 2 non-isolated tids
         long sessionPtr1 = 111;
         CountDownLatch stopLatch1 = new CountDownLatch(1);
         int[] tids1 = createThreads(threadCount, stopLatch1);
@@ -618,7 +615,6 @@
         IBinder token = new Binder();
         int threadCount = 2;
 
-        // session 1 has 2 non-isolated tids
         long sessionPtr1 = 111;
         CountDownLatch stopLatch1 = new CountDownLatch(1);
         int[] tids1 = createThreads(threadCount, stopLatch1);
@@ -630,67 +626,23 @@
                         SessionTag.OTHER, new SessionConfig());
         assertNotNull(session1);
 
-        // session 2 has 2 non-isolated tids and 2 isolated tids
-        long sessionPtr2 = 222;
-        CountDownLatch stopLatch2 = new CountDownLatch(1);
-        // negative value used for test only to avoid conflicting with any real thread that exists
-        int isoProc1 = -100;
-        int isoProc2 = 99999999;
-        when(mAmInternalMock.getIsolatedProcesses(eq(UID))).thenReturn(List.of(0));
-        int[] tids2 = createThreads(threadCount, stopLatch2);
-        int[] tids2WithIsolated = Arrays.copyOf(tids2, tids2.length + 2);
-        tids2WithIsolated[threadCount] = isoProc1;
-        tids2WithIsolated[threadCount + 1] = isoProc2;
-        int[] expectedTids2 = Arrays.copyOf(tids2, tids2.length + 1);
-        expectedTids2[tids2.length] = isoProc1;
-        when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
-                eq(tids2WithIsolated), eq(DEFAULT_TARGET_DURATION), anyInt(),
-                any(SessionConfig.class))).thenReturn(sessionPtr2);
-        AppHintSession session2 = (AppHintSession) service.getBinderServiceInstance()
-                .createHintSessionWithConfig(token, tids2WithIsolated,
-                        DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
-        assertNotNull(session2);
-
-        // trigger clean up through UID state change by making the process foreground->background
-        // this will remove the one unexpected isolated tid from session 2
-        service.mUidObserver.onUidStateChanged(UID,
-                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
-        service.mUidObserver.onUidStateChanged(UID,
-                ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
-                CLEAN_UP_UID_DELAY_MILLIS));
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
-        // the new TIDs pending list should be updated
-        assertArrayEquals(expectedTids2, session2.getTidsInternal());
-        reset(mNativeWrapperMock);
-
-        // this should resume and update the threads so those never-existed invalid isolated
-        // processes should be cleaned up
-        service.mUidObserver.onUidStateChanged(UID,
-                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
-        // wait for the async uid state change to trigger resume and setThreads
-        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
-        verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), eq(expectedTids2));
-        reset(mNativeWrapperMock);
-
         // let all session 1 threads to exit and the cleanup should force pause the session 1
         stopLatch1.countDown();
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
         service.mUidObserver.onUidStateChanged(UID,
+                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+        service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
         verifyAllHintsEnabled(session1, false);
-        verifyAllHintsEnabled(session2, false);
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
+        assertArrayEquals(tids1, session1.getTidsInternal());
         verifyAllHintsEnabled(session1, false);
-        verifyAllHintsEnabled(session2, true);
         reset(mNativeWrapperMock);
 
         // in foreground, set new tids for session 1 then it should be resumed and all hints allowed
@@ -704,8 +656,6 @@
 
         // let all session 1 and 2 non isolated threads to exit
         stopLatch1.countDown();
-        stopLatch2.countDown();
-        expectedTids2 = new int[]{isoProc1};
         LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
         service.mUidObserver.onUidStateChanged(UID,
                 ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
@@ -713,14 +663,11 @@
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
-        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
         // in background, set threads for session 1 then it should not be force paused next time
         session1.setThreads(SESSION_TIDS_A);
         // the new TIDs pending list should be updated
         assertArrayEquals(SESSION_TIDS_A, session1.getTidsInternal());
-        assertArrayEquals(expectedTids2, session2.getTidsInternal());
         verifyAllHintsEnabled(session1, false);
-        verifyAllHintsEnabled(session2, false);
         reset(mNativeWrapperMock);
 
         service.mUidObserver.onUidStateChanged(UID,
@@ -729,10 +676,7 @@
                 CLEAN_UP_UID_DELAY_MILLIS));
         verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr1),
                 eq(SESSION_TIDS_A));
-        verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2),
-                eq(expectedTids2));
         verifyAllHintsEnabled(session1, true);
-        verifyAllHintsEnabled(session2, true);
     }
 
     private void verifyAllHintsEnabled(AppHintSession session, boolean verifyEnabled) {
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index a1db182..1c7fc63 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -54,6 +54,7 @@
 import android.provider.Settings;
 import android.testing.TestableContext;
 import android.util.IntArray;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.DisplayAddress;
@@ -384,6 +385,32 @@
     }
 
     @Test
+    public void testOnGroupRemoved_perDisplayWakeByTouchEnabled() {
+        createNotifier();
+        // GIVEN per-display wake by touch is enabled and one display group has been defined
+        when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(true);
+        final int groupId = 313;
+        final int displayId1 = 3113;
+        final int displayId2 = 4114;
+        final int[] displays = new int[]{displayId1, displayId2};
+        when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displays));
+        when(mDisplayManagerInternal.getDisplayIdsForGroup(groupId)).thenReturn(displays);
+        mNotifier.onGroupWakefulnessChangeStarted(
+                groupId, WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_TAP, /* eventTime= */ 1000);
+        final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray();
+        expectedDisplayInteractivities.put(displayId1, true);
+        expectedDisplayInteractivities.put(displayId2, true);
+        verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities);
+
+        // WHEN display group is removed
+        when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(new SparseArray<>());
+        mNotifier.onGroupRemoved(groupId);
+
+        // THEN native input manager is informed that displays in that group no longer exist
+        verify(mInputManagerInternal).setDisplayInteractivities(new SparseBooleanArray());
+    }
+
+    @Test
     public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException {
         when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
         createNotifier();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index 2f65a18..177f30a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -54,6 +54,8 @@
 import android.os.Parcel;
 import android.os.WakeLockStats;
 import android.os.WorkSource;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 import android.view.Display;
@@ -66,6 +68,7 @@
 import com.android.internal.os.KernelSingleUidTimeReader;
 import com.android.internal.os.LongArrayMultiStateCounter;
 import com.android.internal.os.PowerProfile;
+import com.android.server.power.feature.flags.Flags;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.truth.LongSubject;
@@ -86,11 +89,16 @@
 @RunWith(AndroidJUnit4.class)
 @SuppressWarnings("GuardedBy")
 public class BatteryStatsImplTest {
-    @Rule
+    @Rule(order = 0)
     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
             .setProvideMainThread(true)
+            .setSystemPropertyImmutable("persist.sys.com.android.server.power.feature.flags."
+                + "framework_wakelock_info-override", null)
             .build();
 
+    @Rule(order = 1)
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private KernelCpuUidFreqTimeReader mKernelUidCpuFreqTimeReader;
     @Mock
@@ -572,6 +580,7 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO)
     public void testGetWakeLockStats() {
         mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 2ccb642..69579d6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -91,9 +91,14 @@
 public class BatteryStatsNoteTest {
 
     @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
+    public final RavenwoodRule mRavenwood =
+            new RavenwoodRule.Builder()
+                    .setProvideMainThread(true)
+                    .setSystemPropertyImmutable(
+                            "persist.sys.com.android.server.power.feature.flags."
+                                    + "framework_wakelock_info-override",
+                            null)
+                    .build();
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
@@ -192,6 +197,7 @@
      * Test BatteryStatsImpl.Uid.noteStartWakeLocked.
      */
     @Test
+    @EnableFlags(com.android.server.power.feature.flags.Flags.FLAG_FRAMEWORK_WAKELOCK_INFO)
     public void testNoteStartWakeLocked() throws Exception {
         final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
         MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -222,6 +228,7 @@
      * Test BatteryStatsImpl.Uid.noteStartWakeLocked for an isolated uid.
      */
     @Test
+    @EnableFlags(com.android.server.power.feature.flags.Flags.FLAG_FRAMEWORK_WAKELOCK_INFO)
     public void testNoteStartWakeLocked_isolatedUid() throws Exception {
         final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
         PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
@@ -264,6 +271,7 @@
      * isolated uid is removed from batterystats before the wakelock has been stopped.
      */
     @Test
+    @EnableFlags(com.android.server.power.feature.flags.Flags.FLAG_FRAMEWORK_WAKELOCK_INFO)
     public void testNoteStartWakeLocked_isolatedUidRace() throws Exception {
         final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
         PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java
index 5b7762d..9645e90 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java
@@ -23,12 +23,15 @@
 import android.os.Process;
 import android.os.UidBatteryConsumer;
 import android.os.WorkSource;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.PowerProfile;
+import com.android.server.power.feature.flags.Flags;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -38,20 +41,29 @@
 @SmallTest
 public class WakelockPowerCalculatorTest {
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
+    public final RavenwoodRule mRavenwood =
+            new RavenwoodRule.Builder()
+                    .setProvideMainThread(true)
+                    .setSystemPropertyImmutable(
+                            "persist.sys.com.android.server.power.feature.flags."
+                                    + "framework_wakelock_info-override",
+                            null)
+                    .build();
+
+    @Rule(order = 1)
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_PID = 3145;
 
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePower(PowerProfile.POWER_CPU_IDLE, 360.0);
+    @Rule(order = 2)
+    public final BatteryUsageStatsRule mStatsRule =
+            new BatteryUsageStatsRule().setAveragePower(PowerProfile.POWER_CPU_IDLE, 360.0);
 
     @Test
+    @EnableFlags(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO)
     public void testTimerBasedModel() {
         mStatsRule.getUidStats(Process.ROOT_UID);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
index 8f33498..0d5d277 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java
@@ -24,10 +24,13 @@
 
 import android.content.Context;
 import android.os.Process;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.ravenwood.RavenwoodConfig;
 import android.platform.test.ravenwood.RavenwoodConfig.Config;
 
 import com.android.internal.os.PowerStats;
+import com.android.server.power.feature.flags.Flags;
 import com.android.server.power.stats.format.WakelockPowerStatsLayout;
 
 import org.junit.Before;
@@ -37,11 +40,19 @@
 public class WakelockPowerStatsCollectorTest {
 
     @Config
-    public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
-            .setProvideMainThread(true)
-            .build();
+    public static final RavenwoodConfig sConfig =
+            new RavenwoodConfig.Builder()
+                    .setProvideMainThread(true)
+                    .setSystemPropertyImmutable(
+                            "persist.sys.com.android.server.power.feature.flags."
+                                    + "framework_wakelock_info-override",
+                            null)
+                    .build();
 
-    @Rule
+    @Rule(order = 0)
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
 
     private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
@@ -65,6 +76,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO)
     public void collectStats() {
         PowerStatsCollector powerStatsCollector = mBatteryStats.getPowerStatsCollector(
                 POWER_COMPONENT_WAKELOCK);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java
new file mode 100644
index 0000000..1fe3f58
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockStatsFrameworkEventsTest.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.nano.OsProtoEnums;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WakelockStatsFrameworkEventsTest {
+    private WakelockStatsFrameworkEvents mEvents;
+
+    @Before
+    public void setup() {
+        mEvents = new WakelockStatsFrameworkEvents();
+    }
+
+    private static final int UID_1 = 1;
+    private static final int UID_2 = 2;
+
+    private static final String TAG_1 = "TAG1";
+    private static final String TAG_2 = "TAG2";
+
+    private static final int WAKELOCK_TYPE_1 = OsProtoEnums.PARTIAL_WAKE_LOCK;
+    private static final int WAKELOCK_TYPE_2 = OsProtoEnums.DOZE_WAKE_LOCK;
+
+    private static final long TS_1 = 1000;
+    private static final long TS_2 = 2000;
+    private static final long TS_3 = 3000;
+    private static final long TS_4 = 4000;
+    private static final long TS_5 = 5000;
+
+    // Mirrors com.android.os.framework.FrameworkWakelockInfo proto.
+    private static class WakelockInfo {
+        public int uid;
+        public String tag;
+        public int type;
+        public long uptimeMillis;
+        public long completedCount;
+
+        WakelockInfo(int uid, String tag, int type, long uptimeMillis, long completedCount) {
+            this.uid = uid;
+            this.tag = tag;
+            this.type = type;
+            this.uptimeMillis = uptimeMillis;
+            this.completedCount = completedCount;
+        }
+    }
+
+    // Assumes that mEvents is empty.
+    @SuppressWarnings("GuardedBy")
+    private void makeMetricsAlmostOverflow() throws Exception {
+        for (int i = 0; i < mEvents.SUMMARY_THRESHOLD - 1; i++) {
+            String tag = "forceOverflow" + i;
+            mEvents.noteStartWakeLock(UID_1, tag, WAKELOCK_TYPE_1, TS_1);
+            mEvents.noteStopWakeLock(UID_1, tag, WAKELOCK_TYPE_1, TS_2);
+        }
+
+        assertFalse("not overflow", mEvents.inOverflow());
+        ArrayList<WakelockInfo> info = pullResults(TS_4);
+        WakelockInfo notOverflowInfo =
+                info.stream()
+                        .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG))
+                        .findFirst()
+                        .orElse(null);
+
+        assertEquals("not overflow", notOverflowInfo, null);
+
+        // Add one more to hit an overflow state.
+        String lastTag = "forceOverflowLast";
+        mEvents.noteStartWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2, TS_1);
+        mEvents.noteStopWakeLock(UID_1, lastTag, WAKELOCK_TYPE_2, TS_2);
+
+        assertTrue("overflow", mEvents.inOverflow());
+        info = pullResults(TS_4);
+
+        WakelockInfo tag1Info =
+                info.stream().filter(i -> i.tag.equals(lastTag)).findFirst().orElse(null);
+
+        assertTrue("lastTag found", tag1Info != null);
+        assertEquals("uid", UID_1, tag1Info.uid);
+        assertEquals("tag", lastTag, tag1Info.tag);
+        assertEquals("type", WAKELOCK_TYPE_2, tag1Info.type);
+        assertEquals("duration", TS_2 - TS_1, tag1Info.uptimeMillis);
+        assertEquals("count", 1, tag1Info.completedCount);
+    }
+
+    // Assumes that mEvents is empty.
+    @SuppressWarnings("GuardedBy")
+    private void makeMetricsAlmostHardCap() throws Exception {
+        for (int i = 0; i < mEvents.MAX_WAKELOCK_DIMENSIONS - 1; i++) {
+            mEvents.noteStartWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1, TS_1);
+            mEvents.noteStopWakeLock(i /* uid */, TAG_1, WAKELOCK_TYPE_1, TS_2);
+        }
+
+        assertFalse("not hard capped", mEvents.inHardCap());
+        ArrayList<WakelockInfo> info = pullResults(TS_4);
+        WakelockInfo notOverflowInfo =
+                info.stream()
+                        .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG))
+                        .findFirst()
+                        .orElse(null);
+
+        assertEquals("not overflow", notOverflowInfo, null);
+
+        // Add one more to hit an hardcap state.
+        int hardCapUid = mEvents.MAX_WAKELOCK_DIMENSIONS;
+        mEvents.noteStartWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2, TS_1);
+        mEvents.noteStopWakeLock(hardCapUid, TAG_2, WAKELOCK_TYPE_2, TS_2);
+
+        assertTrue("hard capped", mEvents.inHardCap());
+        info = pullResults(TS_4);
+
+        WakelockInfo tag2Info =
+                info.stream().filter(i -> i.uid == hardCapUid).findFirst().orElse(null);
+
+        assertTrue("hardCapUid found", tag2Info != null);
+        assertEquals("uid", hardCapUid, tag2Info.uid);
+        assertEquals("tag", mEvents.OVERFLOW_TAG, tag2Info.tag);
+        assertEquals("type", mEvents.OVERFLOW_LEVEL, tag2Info.type);
+        assertEquals("duration", TS_2 - TS_1, tag2Info.uptimeMillis);
+        assertEquals("count", 1, tag2Info.completedCount);
+    }
+
+    private ArrayList<WakelockInfo> pullResults(long timestamp) {
+        ArrayList<WakelockInfo> results = new ArrayList<>();
+        WakelockStatsFrameworkEvents.EventLogger logger =
+                new WakelockStatsFrameworkEvents.EventLogger() {
+                    public void logResult(
+                            int uid,
+                            String tag,
+                            int wakeLockLevel,
+                            long uptimeMillis,
+                            long completedCount) {
+                        WakelockInfo info =
+                                new WakelockInfo(
+                                        uid, tag, wakeLockLevel, uptimeMillis, completedCount);
+                        results.add(info);
+                    }
+                };
+        mEvents.pullFrameworkWakelockInfoAtoms(timestamp, logger);
+        return results;
+    }
+
+    @Test
+    public void singleWakelock() {
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_3);
+
+        assertEquals("size", 1, info.size());
+        assertEquals("uid", UID_1, info.get(0).uid);
+        assertEquals("tag", TAG_1, info.get(0).tag);
+        assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type);
+        assertEquals("duration", TS_2 - TS_1, info.get(0).uptimeMillis);
+        assertEquals("count", 1, info.get(0).completedCount);
+    }
+
+    @Test
+    public void wakelockOpen() throws Exception {
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_3);
+
+        assertEquals("size", 1, info.size());
+        assertEquals("uid", UID_1, info.get(0).uid);
+        assertEquals("tag", TAG_1, info.get(0).tag);
+        assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type);
+        assertEquals("duration", TS_3 - TS_1, info.get(0).uptimeMillis);
+        assertEquals("count", 0, info.get(0).completedCount);
+    }
+
+    @Test
+    public void wakelockOpenOverlap() throws Exception {
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1);
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_3);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_4);
+
+        assertEquals("size", 1, info.size());
+        assertEquals("uid", UID_1, info.get(0).uid);
+        assertEquals("tag", TAG_1, info.get(0).tag);
+        assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type);
+        assertEquals("duration", TS_4 - TS_1, info.get(0).uptimeMillis);
+        assertEquals("count", 0, info.get(0).completedCount);
+    }
+
+    @Test
+    public void testOverflow() throws Exception {
+        makeMetricsAlmostOverflow();
+
+        // This one gets tagged as an overflow.
+        mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1);
+        mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_2);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_4);
+        WakelockInfo overflowInfo =
+                info.stream()
+                        .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG))
+                        .findFirst()
+                        .orElse(null);
+
+        assertEquals("uid", UID_1, overflowInfo.uid);
+        assertEquals("type", mEvents.OVERFLOW_LEVEL, overflowInfo.type);
+        assertEquals("duration", TS_2 - TS_1, overflowInfo.uptimeMillis);
+        assertEquals("count", 1, overflowInfo.completedCount);
+    }
+
+    @Test
+    public void testOverflowOpen() throws Exception {
+        makeMetricsAlmostOverflow();
+
+        // This is the open wakelock that overflows.
+        mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_4);
+        WakelockInfo overflowInfo =
+                info.stream()
+                        .filter(i -> i.tag.equals(mEvents.OVERFLOW_TAG))
+                        .findFirst()
+                        .orElse(null);
+
+        assertEquals("uid", UID_1, overflowInfo.uid);
+        assertEquals("type", mEvents.OVERFLOW_LEVEL, overflowInfo.type);
+        assertEquals("duration", (TS_4 - TS_1), overflowInfo.uptimeMillis);
+        assertEquals("count", 0, overflowInfo.completedCount);
+    }
+
+    @Test
+    public void testHardCap() throws Exception {
+        makeMetricsAlmostHardCap();
+
+        // This one gets tagged as a hard cap.
+        mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1);
+        mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_2);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_4);
+        WakelockInfo hardCapInfo =
+                info.stream()
+                        .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG))
+                        .findFirst()
+                        .orElse(null);
+
+        assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.uid);
+        assertEquals("type", mEvents.OVERFLOW_LEVEL, hardCapInfo.type);
+        assertEquals("duration", TS_2 - TS_1, hardCapInfo.uptimeMillis);
+        assertEquals("count", 1, hardCapInfo.completedCount);
+    }
+
+    @Test
+    public void testHardCapOpen() throws Exception {
+        makeMetricsAlmostHardCap();
+
+        // This is the open wakelock that overflows.
+        mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_2, TS_1);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_4);
+        WakelockInfo hardCapInfo =
+                info.stream()
+                        .filter(i -> i.tag.equals(mEvents.HARD_CAP_TAG))
+                        .findFirst()
+                        .orElse(null);
+
+        assertEquals("uid", mEvents.HARD_CAP_UID, hardCapInfo.uid);
+        assertEquals("type", mEvents.OVERFLOW_LEVEL, hardCapInfo.type);
+        assertEquals("duration", (TS_4 - TS_1), hardCapInfo.uptimeMillis);
+        assertEquals("count", 0, hardCapInfo.completedCount);
+    }
+
+    @Test
+    public void overlappingWakelocks() throws Exception {
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1);
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_2);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_3);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_5);
+
+        assertEquals("size", 1, info.size());
+        assertEquals("uid", UID_1, info.get(0).uid);
+        assertEquals("tag", TAG_1, info.get(0).tag);
+        assertEquals("type", WAKELOCK_TYPE_1, info.get(0).type);
+        assertEquals("duration", TS_4 - TS_1, info.get(0).uptimeMillis);
+        assertEquals("count", 1, info.get(0).completedCount);
+    }
+
+    @Test
+    public void diffUid() throws Exception {
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1);
+        mEvents.noteStartWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1, TS_2);
+        mEvents.noteStopWakeLock(UID_2, TAG_1, WAKELOCK_TYPE_1, TS_3);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_5);
+        assertEquals("size", 2, info.size());
+
+        WakelockInfo uid1Info = info.stream().filter(i -> i.uid == UID_1).findFirst().orElse(null);
+
+        assertTrue("UID_1 found", uid1Info != null);
+        assertEquals("uid", UID_1, uid1Info.uid);
+        assertEquals("tag", TAG_1, uid1Info.tag);
+        assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type);
+        assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis);
+        assertEquals("count", 1, uid1Info.completedCount);
+
+        WakelockInfo uid2Info = info.stream().filter(i -> i.uid == UID_2).findFirst().orElse(null);
+        assertTrue("UID_2 found", uid2Info != null);
+        assertEquals("uid", UID_2, uid2Info.uid);
+        assertEquals("tag", TAG_1, uid2Info.tag);
+        assertEquals("type", WAKELOCK_TYPE_1, uid2Info.type);
+        assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis);
+        assertEquals("count", 1, uid2Info.completedCount);
+    }
+
+    @Test
+    public void diffTag() throws Exception {
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1);
+        mEvents.noteStartWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1, TS_2);
+        mEvents.noteStopWakeLock(UID_1, TAG_2, WAKELOCK_TYPE_1, TS_3);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_5);
+        assertEquals("size", 2, info.size());
+
+        WakelockInfo uid1Info =
+                info.stream().filter(i -> i.tag.equals(TAG_1)).findFirst().orElse(null);
+
+        assertTrue("TAG_1 found", uid1Info != null);
+        assertEquals("uid", UID_1, uid1Info.uid);
+        assertEquals("tag", TAG_1, uid1Info.tag);
+        assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type);
+        assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis);
+        assertEquals("count", 1, uid1Info.completedCount);
+
+        WakelockInfo uid2Info =
+                info.stream().filter(i -> i.tag.equals(TAG_2)).findFirst().orElse(null);
+        assertTrue("TAG_2 found", uid2Info != null);
+        assertEquals("uid", UID_1, uid2Info.uid);
+        assertEquals("tag", TAG_2, uid2Info.tag);
+        assertEquals("type", WAKELOCK_TYPE_1, uid2Info.type);
+        assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis);
+        assertEquals("count", 1, uid2Info.completedCount);
+    }
+
+    @Test
+    public void diffType() throws Exception {
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_1);
+        mEvents.noteStartWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2, TS_2);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_2, TS_3);
+        mEvents.noteStopWakeLock(UID_1, TAG_1, WAKELOCK_TYPE_1, TS_4);
+
+        ArrayList<WakelockInfo> info = pullResults(TS_5);
+        assertEquals("size", 2, info.size());
+
+        WakelockInfo uid1Info =
+                info.stream().filter(i -> i.type == WAKELOCK_TYPE_1).findFirst().orElse(null);
+
+        assertTrue("WAKELOCK_TYPE_1 found", uid1Info != null);
+        assertEquals("uid", UID_1, uid1Info.uid);
+        assertEquals("tag", TAG_1, uid1Info.tag);
+        assertEquals("type", WAKELOCK_TYPE_1, uid1Info.type);
+        assertEquals("duration", TS_4 - TS_1, uid1Info.uptimeMillis);
+        assertEquals("count", 1, uid1Info.completedCount);
+
+        WakelockInfo uid2Info =
+                info.stream().filter(i -> i.type == WAKELOCK_TYPE_2).findFirst().orElse(null);
+        assertTrue("WAKELOCK_TYPE_2 found", uid2Info != null);
+        assertEquals("uid", UID_1, uid2Info.uid);
+        assertEquals("tag", TAG_1, uid2Info.tag);
+        assertEquals("type", WAKELOCK_TYPE_2, uid2Info.type);
+        assertEquals("duration", TS_3 - TS_2, uid2Info.uptimeMillis);
+        assertEquals("count", 1, uid2Info.completedCount);
+    }
+}
diff --git a/services/tests/security/forensic/Android.bp b/services/tests/security/forensic/Android.bp
new file mode 100644
index 0000000..adc4904
--- /dev/null
+++ b/services/tests/security/forensic/Android.bp
@@ -0,0 +1,41 @@
+package {
+    default_team: "trendy_team_platform_security",
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "ForensicServiceTests",
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.rules",
+        "androidx.test.runner",
+        "compatibility-device-util-axt",
+        "frameworks-base-testutils",
+        "junit",
+        "platform-test-annotations",
+        "services.core",
+        "truth",
+    ],
+
+    platform_apis: true,
+
+    test_suites: [
+        "device-tests",
+        "automotive-tests",
+    ],
+
+    certificate: "platform",
+    dxflags: ["--multi-dex"],
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/core/java/android/service/wallpaper/AndroidManifest.xml b/services/tests/security/forensic/AndroidManifest.xml
similarity index 60%
copy from core/java/android/service/wallpaper/AndroidManifest.xml
copy to services/tests/security/forensic/AndroidManifest.xml
index f1bdb76..40feb19 100644
--- a/core/java/android/service/wallpaper/AndroidManifest.xml
+++ b/services/tests/security/forensic/AndroidManifest.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2024 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -16,7 +15,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.service.wallpaper">
+     package="com.android.server.security.forensic.tests">
 
-    <uses-sdk android:minSdkVersion="29" />
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.server.security.forensic.tests"
+         android:label="Frameworks Forensic Services Tests"/>
 </manifest>
diff --git a/services/tests/security/forensic/AndroidTest.xml b/services/tests/security/forensic/AndroidTest.xml
new file mode 100644
index 0000000..bbe2e9c
--- /dev/null
+++ b/services/tests/security/forensic/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks Forensic Service tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="ForensicServiceTests.apk"/>
+        <option name="install-arg" value="-t" />
+    </target_preparer>
+
+    <option name="test-tag" value="ForensicServiceTests" />
+    <test class="com.android.tradefed.testtype.InstrumentationTest" >
+        <option name="package" value="com.android.server.security.forensic.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/security/forensic/OWNERS b/services/tests/security/forensic/OWNERS
new file mode 100644
index 0000000..80c9afb9
--- /dev/null
+++ b/services/tests/security/forensic/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+file:platform/frameworks/base:main:/core/java/android/security/forensic/OWNERS
diff --git a/services/tests/security/forensic/TEST_MAPPING b/services/tests/security/forensic/TEST_MAPPING
new file mode 100644
index 0000000..bd8b2ab
--- /dev/null
+++ b/services/tests/security/forensic/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "ForensicServiceTests"
+    }
+  ]
+}
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
new file mode 100644
index 0000000..7aa2e0f
--- /dev/null
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.forensic;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.security.forensic.IForensicServiceCommandCallback;
+import android.security.forensic.IForensicServiceStateCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class ForensicServiceTest {
+    private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
+    private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
+    private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+    private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
+
+    private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
+    private static final int ERROR_PERMISSION_DENIED =
+            IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
+    private static final int ERROR_INVALID_STATE_TRANSITION =
+            IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
+    private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
+            IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+    private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
+            IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
+
+    @Mock
+    private Context mContextSpy;
+
+    private ForensicService mForensicService;
+    private TestLooper mTestLooper;
+    private Looper mLooper;
+
+    @SuppressLint("VisibleForTests")
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mTestLooper = new TestLooper();
+        mLooper = mTestLooper.getLooper();
+        mForensicService = new ForensicService(new MockInjector(mContextSpy));
+        mForensicService.onStart();
+    }
+
+    @Test
+    public void testMonitorState_Invisible() throws RemoteException {
+        StateCallback scb = new StateCallback();
+        assertEquals(STATE_UNKNOWN, scb.mState);
+        mForensicService.getBinderService().monitorState(scb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb.mState);
+    }
+
+    @Test
+    public void testMonitorState_Invisible_TwoMonitors() throws RemoteException {
+        StateCallback scb1 = new StateCallback();
+        assertEquals(STATE_UNKNOWN, scb1.mState);
+        mForensicService.getBinderService().monitorState(scb1);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+
+        StateCallback scb2 = new StateCallback();
+        assertEquals(STATE_UNKNOWN, scb2.mState);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+    }
+
+    @Test
+    public void testMakeVisible_FromInvisible() throws RemoteException {
+        StateCallback scb = new StateCallback();
+        assertEquals(STATE_UNKNOWN, scb.mState);
+        mForensicService.getBinderService().monitorState(scb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeVisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testMakeVisible_FromInvisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_INVISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeVisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_VISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeVisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testMakeVisible_FromEnabled_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_ENABLED);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_ENABLED, scb1.mState);
+        assertEquals(STATE_ENABLED, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeVisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_ENABLED, scb1.mState);
+        assertEquals(STATE_ENABLED, scb2.mState);
+        assertNotNull(ccb.mErrorCode);
+        assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+    }
+
+    @Test
+    public void testMakeInvisible_FromInvisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_INVISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeInvisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testMakeInvisible_FromVisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_VISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeInvisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testMakeInvisible_FromEnabled_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_ENABLED);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_ENABLED, scb1.mState);
+        assertEquals(STATE_ENABLED, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().makeInvisible(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+
+    @Test
+    public void testEnable_FromInvisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_INVISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().enable(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+        assertNotNull(ccb.mErrorCode);
+        assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+    }
+
+    @Test
+    public void testEnable_FromVisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_VISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().enable(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_ENABLED, scb1.mState);
+        assertEquals(STATE_ENABLED, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testEnable_FromEnabled_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_ENABLED);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_ENABLED, scb1.mState);
+        assertEquals(STATE_ENABLED, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().enable(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_ENABLED, scb1.mState);
+        assertEquals(STATE_ENABLED, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testDisable_FromInvisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_INVISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().disable(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_INVISIBLE, scb1.mState);
+        assertEquals(STATE_INVISIBLE, scb2.mState);
+        assertNotNull(ccb.mErrorCode);
+        assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+    }
+
+    @Test
+    public void testDisable_FromVisible_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_VISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().disable(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    @Test
+    public void testDisable_FromEnabled_TwoMonitors() throws RemoteException {
+        mForensicService.setState(STATE_ENABLED);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_ENABLED, scb1.mState);
+        assertEquals(STATE_ENABLED, scb2.mState);
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().disable(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+        assertNull(ccb.mErrorCode);
+    }
+
+    private class MockInjector implements ForensicService.Injector {
+        private final Context mContext;
+
+        MockInjector(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+
+
+        @Override
+        public Looper getLooper() {
+            return mLooper;
+        }
+
+    }
+
+    private static class StateCallback extends IForensicServiceStateCallback.Stub {
+        int mState = STATE_UNKNOWN;
+
+        @Override
+        public void onStateChange(int state) throws RemoteException {
+            mState = state;
+        }
+    }
+
+    private static class CommandCallback extends IForensicServiceCommandCallback.Stub {
+        Integer mErrorCode = null;
+
+        public void reset() {
+            mErrorCode = null;
+        }
+
+        @Override
+        public void onSuccess() throws RemoteException {
+
+        }
+
+        @Override
+        public void onFailure(int errorCode) throws RemoteException {
+            mErrorCode = errorCode;
+        }
+    }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 6ede334..359755a 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -67,6 +67,7 @@
         "androidx.test.ext.junit",
         "cts-wm-util",
         "platform-compat-test-rules",
+        "platform-parametric-runner-lib",
         "mockito-target-minus-junit4",
         "mockito-kotlin2",
         "platform-test-annotations",
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index d49bc43..d8a9400 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,4 +1,4 @@
-per-file *Alarm* = file:/apex/jobscheduler/OWNERS
+per-file *Alarm* = file:/apex/jobscheduler/ALARM_OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
 per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
index c76392b..5134737 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
@@ -236,6 +236,27 @@
     }
 
     @Test
+    fun whenScrollToggleOn_ScrollRightKeyIsPressed_scrollEventIsSent() {
+        // There should be some delay between the downTime of the key event and calling onKeyEvent
+        val downTime = clock.now() - KEYBOARD_POST_EVENT_DELAY_MILLIS
+        val keyCodeScrollToggle = MouseKeysInterceptor.MouseKeyEvent.SCROLL_TOGGLE.keyCodeValue
+        val keyCodeScroll = MouseKeysInterceptor.MouseKeyEvent.RIGHT_MOVE_OR_SCROLL.keyCodeValue
+
+        val scrollToggleDownEvent = KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+            keyCodeScrollToggle, 0, 0, DEVICE_ID, 0)
+        val scrollDownEvent = KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+            keyCodeScroll, 0, 0, DEVICE_ID, 0)
+
+        mouseKeysInterceptor.onKeyEvent(scrollToggleDownEvent, 0)
+        mouseKeysInterceptor.onKeyEvent(scrollDownEvent, 0)
+        testLooper.dispatchAll()
+
+        // Verify the sendScrollEvent method is called once and capture the arguments
+        verifyScrollEvents(arrayOf<Float>(-MouseKeysInterceptor.MOUSE_SCROLL_STEP),
+	    arrayOf<Float>(0f))
+    }
+
+    @Test
     fun whenScrollToggleOff_DirectionalUpKeyIsPressed_RelativeEventIsSent() {
         // There should be some delay between the downTime of the key event and calling onKeyEvent
         val downTime = clock.now() - KEYBOARD_POST_EVENT_DELAY_MILLIS
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index b745e6a..e5831b3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -1417,7 +1417,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testMouseMoveEventsDoNotMoveMagnifierViewport() {
         runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
     }
@@ -1471,55 +1471,55 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() {
         runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testStylusHoverMoveEventsDoNotMoveMagnifierViewport() {
         runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testMouseHoverMoveEventsMoveMagnifierViewport() {
         runHoverMovesViewportTest(InputDevice.SOURCE_MOUSE);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testStylusHoverMoveEventsMoveMagnifierViewport() {
         runHoverMovesViewportTest(InputDevice.SOURCE_STYLUS);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testMouseDownEventsDoNotMoveMagnifierViewport() {
         runDownDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testStylusDownEventsDoNotMoveMagnifierViewport() {
         runDownDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testMouseUpEventsDoNotMoveMagnifierViewport() {
         runUpDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testStylusUpEventsDoNotMoveMagnifierViewport() {
         runUpDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void testMouseMoveEventsMoveMagnifierViewport() {
         final EventCaptor eventCaptor = new EventCaptor();
         mMgh.setNext(eventCaptor);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 6aa8a32..06ebe6e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -62,6 +62,7 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import org.junit.Before;
@@ -92,12 +93,16 @@
     private MagnificationConnectionManager.Callback mMockCallback;
     private MockContentResolver mResolver;
     private MagnificationConnectionManager mMagnificationConnectionManager;
+    @Mock
+    private UserManagerInternal mMockUserManagerInternal;
 
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+        LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
         mResolver = new MockContentResolver();
         mMockConnection = new MockMagnificationConnection();
         mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
@@ -110,6 +115,8 @@
         Settings.Secure.putFloatForUser(mResolver,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f,
                 CURRENT_USER_ID);
+
+        when(mMockUserManagerInternal.isVisibleBackgroundFullUser(anyInt())).thenReturn(false);
     }
 
     private void stubSetConnection(boolean needDelay) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index d80a1f0..45c157d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -93,7 +93,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() {
         final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
         mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
@@ -108,7 +108,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() {
         final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
         stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
@@ -123,7 +123,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() {
         final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
         mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
@@ -138,7 +138,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
     public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() {
         final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
         stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 31bf5f0..4981ceb 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -61,6 +61,7 @@
     private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L;
     private static final long SERVICE_USAGE_INTERACTION = 60 * 1000;
 
+    @SuppressWarnings("GuardedBy")
     @BeforeClass
     public static void setUpOnce() {
         sContext = getInstrumentation().getTargetContext();
@@ -92,8 +93,11 @@
                     return true;
                 }
             };
-            sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null,
-                    injector);
+            sService.mProcessStateController = new ProcessStateController.Builder(sService,
+                    sService.mProcessList, null)
+                    .setOomAdjusterInjector(injector)
+                    .build();
+            sService.mOomAdjuster = sService.mProcessStateController.getOomAdjuster();
             LocalServices.addService(UsageStatsManagerInternal.class,
                     mock(UsageStatsManagerInternal.class));
             sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 390eb93..2fe6918 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -181,12 +181,14 @@
             Intent.ACTION_USER_STARTING);
 
     private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
+            0, // for startUserInternalOnHandler
             REPORT_USER_SWITCH_MSG,
             USER_SWITCH_TIMEOUT_MSG,
             USER_START_MSG,
             USER_CURRENT_MSG);
 
     private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
+            0, // for startUserInternalOnHandler
             USER_START_MSG,
             REPORT_LOCKED_BOOT_COMPLETE_MSG);
 
@@ -374,7 +376,7 @@
         // and the cascade effect goes on...). In fact, a better approach would to not assert the
         // binder calls, but their side effects (in this case, that the user is stopped right away)
         assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
-                .containsExactly(USER_START_MSG);
+                .containsExactly(/* for startUserInternalOnHandler */ 0, USER_START_MSG);
     }
 
     private void startUserAssertions(
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 3e2949d6..de5564c 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -20,6 +20,8 @@
 import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
 import static com.android.media.audio.Flags.absVolumeIndexFix;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -109,12 +111,13 @@
         mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName);
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                        AudioManager.STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+                eq(AudioManager.STREAM_MUSIC), eq(minIndex), anyBoolean(),
+                eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
 
         mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName);
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                AudioManager.STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+                AudioManager.STREAM_MUSIC, midIndex, false, AudioSystem.DEVICE_OUT_USB_DEVICE);
     }
 
     @Test
@@ -151,7 +154,7 @@
 
             // Stream volume changes
             verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                            AudioManager.STREAM_MUSIC, targetIndex,
+                            AudioManager.STREAM_MUSIC, targetIndex, false,
                             AudioSystem.DEVICE_OUT_BLE_HEADSET);
         }
 
@@ -162,7 +165,7 @@
         mTestLooper.dispatchAll();
 
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                        AudioManager.STREAM_MUSIC, maxIndex,
+                        AudioManager.STREAM_MUSIC, maxIndex, false,
                         AudioSystem.DEVICE_OUT_BLE_HEADSET);
     }
 
@@ -193,8 +196,8 @@
             }
             // Stream volume changes
             verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                            AudioManager.STREAM_MUSIC, passedIndex,
-                            AudioSystem.DEVICE_OUT_BLE_HEADSET);
+                    AudioManager.STREAM_MUSIC, passedIndex, false,
+                    AudioSystem.DEVICE_OUT_BLE_HEADSET);
         }
 
         // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
@@ -207,7 +210,7 @@
             passedIndex = 4;
         }
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                        AudioManager.STREAM_MUSIC, passedIndex,
-                        AudioSystem.DEVICE_OUT_BLE_HEADSET);
+                AudioManager.STREAM_MUSIC, passedIndex, false,
+                AudioSystem.DEVICE_OUT_BLE_HEADSET);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 96ac5d2..ce59a86 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -132,12 +132,13 @@
     }
 
     @Override
-    public int setStreamVolumeIndexAS(int stream, int index, int device) {
+    public int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) {
         return AudioSystem.AUDIO_STATUS_OK;
     }
 
     @Override
-    public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+    public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, boolean muted,
+            int device) {
         return AudioSystem.AUDIO_STATUS_OK;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
index dc8c1b9..6b41c43 100644
--- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -18,6 +18,7 @@
 import static android.media.AudioManager.ADJUST_LOWER;
 import static android.media.AudioManager.ADJUST_MUTE;
 import static android.media.AudioManager.ADJUST_RAISE;
+import static android.media.AudioManager.ADJUST_UNMUTE;
 import static android.media.AudioManager.DEVICE_OUT_BLE_SPEAKER;
 import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_SCO;
 import static android.media.AudioManager.DEVICE_OUT_SPEAKER;
@@ -41,13 +42,13 @@
 
 import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX;
 import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+import static com.android.media.audio.Flags.FLAG_RING_MY_CAR;
 import static com.android.media.audio.Flags.absVolumeIndexFix;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
@@ -180,6 +181,10 @@
             }
             return mStreamDevice.get(stream);
         }
+
+        public void setMuteAffectedStreams(int muteAffectedStreams) {
+            mMuteAffectedStreams = muteAffectedStreams;
+        }
     }
 
     private static class TestDeviceVolumeBehaviorDispatcherStub
@@ -223,6 +228,7 @@
                 mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
                 mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer,
                 mMockPermissionProvider);
+        mAudioService.setMuteAffectedStreams(AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED);
 
         mTestLooper.dispatchAll();
         prepareAudioServiceState();
@@ -258,6 +264,8 @@
         for (int streamType : usedStreamTypes) {
             mAudioService.setStreamVolume(streamType, DEFAULT_STREAM_VOLUME, /*flags=*/0,
                     mContext.getOpPackageName());
+            mAudioService.adjustStreamVolume(streamType, ADJUST_UNMUTE, /*flags=*/0,
+                    mContext.getOpPackageName());
         }
 
         if (!mIsAutomotive) {
@@ -301,7 +309,20 @@
         mTestLooper.dispatchAll();
 
         verify(mSpyAudioSystem).setStreamVolumeIndexAS(
-                eq(STREAM_MUSIC), eq(newIndex), eq(DEVICE_OUT_USB_DEVICE));
+                STREAM_MUSIC, newIndex, /*muted=*/false, DEVICE_OUT_USB_DEVICE);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_RING_MY_CAR)
+    public void adjustStreamVolume_adjustMute_callsASSetStreamVolumeIndex() throws Exception {
+        int currentIndex = mAudioService.getStreamVolume(STREAM_MUSIC);
+
+        mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_MUTE, /*flags=*/0,
+                mContext.getOpPackageName());
+        mTestLooper.dispatchAll();
+
+        verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+                eq(STREAM_MUSIC), eq(currentIndex), /*muted=*/eq(true), anyInt());
     }
 
     @Test
@@ -325,7 +346,7 @@
         mTestLooper.dispatchAll();
 
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+                eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(DEVICE_OUT_USB_DEVICE));
     }
 
     @Test
@@ -341,7 +362,7 @@
         mTestLooper.dispatchAll();
 
         verify(mSpyAudioSystem).setStreamVolumeIndexAS(
-                eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+                eq(STREAM_MUSIC), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE));
     }
 
     // --------------- Volume Group APIs ---------------
@@ -356,15 +377,15 @@
         mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
         mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
                 circularNoMinMaxIncrementVolume(STREAM_MUSIC), /*flags=*/0,
-                mContext.getOpPackageName(),  /*attributionTag*/null);
+                mContext.getOpPackageName(), /*attributionTag*/null);
         mTestLooper.dispatchAll();
 
-        verify(mSpyAudioSystem).setVolumeIndexForAttributes(any(), anyInt(),
+        verify(mSpyAudioSystem).setVolumeIndexForAttributes(any(), anyInt(), eq(false),
                 eq(DEVICE_OUT_USB_DEVICE));
     }
 
     @Test
-    public void adjustVolumeGroupVolume_callsASSetVolumeIndexForAttributes() throws Exception {
+    public void adjustVolumeGroupVolume_callsASSetStreamVolumeIndexAS() throws Exception {
         assumeNotNull(mAudioMusicVolumeGroup);
 
         mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
@@ -372,8 +393,24 @@
                 ADJUST_LOWER, /*flags=*/0, mContext.getOpPackageName());
         mTestLooper.dispatchAll();
 
-        verify(mSpyAudioSystem).setVolumeIndexForAttributes(
-                any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+        // adjust calls setStreamVolumeIndexAS instead of setVolumeIndexForAttributes
+        verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+                anyInt(), anyInt(), anyBoolean(), eq(DEVICE_OUT_USB_DEVICE));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_RING_MY_CAR)
+    public void adjustVolumeGroupVolume_adjustMute_callsASSetStreamVolumeIndexAS()
+            throws Exception {
+        assumeNotNull(mAudioMusicVolumeGroup);
+
+        mAudioService.adjustVolumeGroupVolume(mAudioMusicVolumeGroup.getId(),
+                ADJUST_MUTE, /*flags=*/0, mContext.getOpPackageName());
+        mTestLooper.dispatchAll();
+
+        // adjust calls setStreamVolumeIndexAS instead of setVolumeIndexForAttributes
+        verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+                anyInt(), anyInt(), eq(true), anyInt());
     }
 
     @Test
@@ -437,7 +474,7 @@
 
     @Test
     public void check_isStreamAffectedByMute() {
-        assertFalse(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL));
+        assertTrue(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL));
     }
 
     // --------------------- Volume Flag Check --------------------
@@ -452,14 +489,14 @@
                 mContext.getOpPackageName());
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem).setStreamVolumeIndexAS(
-                eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+                eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
 
         reset(mSpyAudioSystem);
         mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
                 FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem).setStreamVolumeIndexAS(
-                eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+                eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
     }
 
     @Test
@@ -471,13 +508,13 @@
                 mContext.getOpPackageName());
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
-                eq(STREAM_NOTIFICATION), eq(newIndex), eq(DEVICE_OUT_BLE_SPEAKER));
+                eq(STREAM_NOTIFICATION), eq(newIndex), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
 
         mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
                 FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
-                eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+                eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER));
     }
 
     @Test
@@ -523,7 +560,7 @@
         mTestLooper.dispatchAll();
 
         verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
-                eq(STREAM_MUSIC), anyInt(), anyInt());
+                eq(STREAM_MUSIC), anyInt(), eq(false), anyInt());
     }
 
     @Test
@@ -537,7 +574,7 @@
         mTestLooper.dispatchAll();
 
         verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
-                eq(STREAM_VOICE_CALL), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+                eq(STREAM_VOICE_CALL), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE));
 
         mAudioService.setDeviceForStream(STREAM_BLUETOOTH_SCO, DEVICE_OUT_BLUETOOTH_SCO);
         mAudioService.adjustStreamVolume(STREAM_BLUETOOTH_SCO, ADJUST_MUTE, /*flags=*/0,
@@ -545,7 +582,7 @@
         mTestLooper.dispatchAll();
 
         verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
-                eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+                eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE));
     }
 
     // ----------------- AudioDeviceVolumeManager -----------------
@@ -568,18 +605,18 @@
         mTestLooper.dispatchAll();
 
         // there is a min/max index mismatch in automotive
-        assertEquals(volMin, mAudioService.getDeviceVolume(volMin, usbDevice,
-                mContext.getOpPackageName()));
+        assertEquals(volMin.getVolumeIndex(), mAudioService.getDeviceVolume(volMin, usbDevice,
+                mContext.getOpPackageName()).getVolumeIndex());
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
+                eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
 
         mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName());
         mTestLooper.dispatchAll();
         // there is a min/max index mismatch in automotive
-        assertEquals(volMid, mAudioService.getDeviceVolume(volMid, usbDevice,
-                mContext.getOpPackageName()));
+        assertEquals(volMid.getVolumeIndex(), mAudioService.getDeviceVolume(volMid, usbDevice,
+                mContext.getOpPackageName()).getVolumeIndex());
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
+                eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
     }
 
     @Test
@@ -617,8 +654,7 @@
                     mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName()));
             // Stream volume changes
             verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                    STREAM_MUSIC, targetIndex,
-                    AudioSystem.DEVICE_OUT_BLE_HEADSET);
+                    STREAM_MUSIC, targetIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
         }
 
         // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
@@ -630,8 +666,7 @@
         assertEquals(volIndex4,
                 mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName()));
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                STREAM_MUSIC, maxIndex,
-                AudioSystem.DEVICE_OUT_BLE_HEADSET);
+                STREAM_MUSIC, maxIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
     }
 
     @Test
@@ -660,8 +695,7 @@
             }
             // Stream volume changes
             verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                    STREAM_MUSIC, passedIndex,
-                    AudioSystem.DEVICE_OUT_BLE_HEADSET);
+                    STREAM_MUSIC, passedIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
         }
 
         // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
@@ -674,8 +708,7 @@
             passedIndex = 4;
         }
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                STREAM_MUSIC, passedIndex,
-                AudioSystem.DEVICE_OUT_BLE_HEADSET);
+                STREAM_MUSIC, passedIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET);
     }
 
     // ---------------- DeviceVolumeBehaviorTest ----------------
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 98b1191..e386808 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -116,7 +116,6 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.compatibility.common.util.SystemUtil;
 import com.android.internal.app.BlockedAppStreamingActivity;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
@@ -161,8 +160,8 @@
     private static final int DISPLAY_ID_1 = 2;
     private static final int DISPLAY_ID_2 = 3;
     private static final int NON_EXISTENT_DISPLAY_ID = 42;
-    private static final int DEVICE_OWNER_UID_1 = 50;
-    private static final int DEVICE_OWNER_UID_2 = 51;
+    private static final int DEVICE_OWNER_UID_1 = Process.myUid();
+    private static final int DEVICE_OWNER_UID_2 = DEVICE_OWNER_UID_1 + 1;
     private static final int UID_1 = 0;
     private static final int UID_2 = 10;
     private static final int UID_3 = 10000;
@@ -245,6 +244,8 @@
     @Mock
     private IDisplayManager mIDisplayManager;
     @Mock
+    private WindowManager mWindowManager;
+    @Mock
     private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
     @Mock
     private DevicePolicyManager mDevicePolicyManagerMock;
@@ -383,8 +384,7 @@
         // Allow virtual devices to be created on the looper thread for testing.
         final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
         mInputController = new InputController(mNativeWrapperMock,
-                new Handler(TestableLooper.get(this).getLooper()),
-                mContext.getSystemService(WindowManager.class),
+                new Handler(TestableLooper.get(this).getLooper()), mWindowManager,
                 AttributionSource.myAttributionSource(), threadVerifier);
         mCameraAccessController =
                 new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
@@ -535,7 +535,7 @@
                 .build();
         mDeviceImpl.close();
         mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
 
         GenericWindowPolicyController gwpc =
                 mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1);
@@ -543,6 +543,83 @@
     }
 
     @Test
+    public void getDevicePolicy_customRecentsPolicy_untrustedDisplaygwpcShowsRecentsOnHostDevice() {
+        VirtualDeviceParams params = new VirtualDeviceParams
+                .Builder()
+                .setDevicePolicy(POLICY_TYPE_RECENTS, DEVICE_POLICY_CUSTOM)
+                .build();
+        mDeviceImpl.close();
+        mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+
+        GenericWindowPolicyController gwpc =
+                mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1);
+        assertThat(gwpc.canShowTasksInHostDeviceRecents()).isTrue();
+    }
+
+    @Test
+    public void deviceOwner_cannotMessWithAnotherDeviceTheyDoNotOwn() {
+        VirtualDeviceImpl unownedDevice =
+                createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2);
+
+        // The arguments don't matter, the owner uid check is always the first statement.
+        assertThrows(SecurityException.class, () -> unownedDevice.goToSleep());
+        assertThrows(SecurityException.class, () -> unownedDevice.wakeUp());
+
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.launchPendingIntent(0, null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.registerIntentInterceptor(null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.unregisterIntentInterceptor(null));
+
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.addActivityPolicyExemption(null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.removeActivityPolicyExemption(null));
+        assertThrows(SecurityException.class, () -> unownedDevice.setDevicePolicy(0, 0));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.setDevicePolicyForDisplay(0, 0, 0));
+        assertThrows(SecurityException.class, () -> unownedDevice.setDisplayImePolicy(0, 0));
+
+        assertThrows(SecurityException.class, () -> unownedDevice.registerVirtualCamera(null));
+        assertThrows(SecurityException.class, () -> unownedDevice.unregisterVirtualCamera(null));
+
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.onAudioSessionStarting(0, null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.onAudioSessionEnded());
+
+        assertThrows(SecurityException.class, () -> unownedDevice.createVirtualDisplay(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.createVirtualDpad(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.createVirtualMouse(null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.createVirtualTouchscreen(null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.createVirtualNavigationTouchpad(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.createVirtualStylus(null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.createVirtualRotaryEncoder(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.unregisterInputDevice(null));
+
+        assertThrows(SecurityException.class, () -> unownedDevice.sendDpadKeyEvent(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.sendKeyEvent(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.sendButtonEvent(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.sendTouchEvent(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.sendRelativeEvent(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.sendScrollEvent(null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.sendStylusMotionEvent(null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.sendStylusButtonEvent(null, null));
+        assertThrows(SecurityException.class,
+                () -> unownedDevice.sendRotaryEncoderScrollEvent(null, null));
+        assertThrows(SecurityException.class, () -> unownedDevice.setShowPointerIcon(true));
+
+        assertThrows(SecurityException.class, () -> unownedDevice.getVirtualSensorList());
+        assertThrows(SecurityException.class, () -> unownedDevice.sendSensorEvent(null, null));
+    }
+
+    @Test
     public void getDeviceOwnerUid_oneDevice_returnsCorrectId() {
         int ownerUid = mLocalService.getDeviceOwnerUid(mDeviceImpl.getDeviceId());
         assertThat(ownerUid).isEqualTo(mDeviceImpl.getOwnerUid());
@@ -660,7 +737,7 @@
     @Test
     public void getDeviceIdsForUid_twoDevicesUidOnOne_returnsCorrectId() {
         VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
-                DEVICE_OWNER_UID_2);
+                DEVICE_OWNER_UID_1);
         addVirtualDisplay(secondDevice, DISPLAY_ID_2);
 
         secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged(
@@ -675,7 +752,7 @@
     public void getDeviceIdsForUid_twoDevicesUidOnBoth_returnsCorrectId() {
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
-                DEVICE_OWNER_UID_2);
+                DEVICE_OWNER_UID_1);
         addVirtualDisplay(secondDevice, DISPLAY_ID_2);
 
 
@@ -692,7 +769,7 @@
 
     @Test
     public void getPreferredLocaleListForApp_keyboardAttached_returnLocaleHints() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
 
         mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1));
@@ -713,7 +790,7 @@
     @Test
     public void getPreferredLocaleListForApp_appOnMultipleVD_localeOnFirstVDReturned() {
         VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
-                DEVICE_OWNER_UID_2);
+                DEVICE_OWNER_UID_1);
         Binder secondBinder = new Binder("secondBinder");
         VirtualKeyboardConfig firstKeyboardConfig =
                 new VirtualKeyboardConfig.Builder()
@@ -732,8 +809,8 @@
                         .setLanguageTag("fr-FR")
                         .build();
 
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        addVirtualDisplay(secondDevice, DISPLAY_ID_2);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
+        addVirtualDisplay(secondDevice, DISPLAY_ID_2, Display.FLAG_TRUSTED);
 
         mDeviceImpl.createVirtualKeyboard(firstKeyboardConfig, BINDER);
         secondDevice.createVirtualKeyboard(secondKeyboardConfig, secondBinder);
@@ -751,7 +828,7 @@
         assertThat(mCameraAccessController.getObserverCount()).isEqualTo(1);
 
         VirtualDeviceImpl secondDevice =
-                createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2);
+                createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_1);
         assertThat(mCameraAccessController.getObserverCount()).isEqualTo(2);
 
         mDeviceImpl.close();
@@ -910,11 +987,24 @@
     }
 
     @Test
-    public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
+    public void onVirtualDisplayCreatedLocked_notTrustedDisplay_noWakeLockIsAcquired()
+            throws RemoteException {
         verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
                 nullable(String.class), anyInt(), eq(null));
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        TestableLooper.get(this).processAllMessages();
+        verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
+                nullable(String.class), nullable(String.class), nullable(WorkSource.class),
+                nullable(String.class), anyInt(), eq(null));
+    }
+
+    @Test
+    public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
+        verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
+                nullable(String.class), nullable(String.class), nullable(WorkSource.class),
+                nullable(String.class), anyInt(), eq(null));
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
                 nullable(String.class), eq(DISPLAY_ID_1), eq(null));
@@ -923,7 +1013,7 @@
     @Test
     public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired()
             throws RemoteException {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         assertThrows(IllegalStateException.class,
                 () -> addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1));
         TestableLooper.get(this).processAllMessages();
@@ -934,7 +1024,7 @@
 
     @Test
     public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
@@ -949,7 +1039,7 @@
 
     @Test
     public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
@@ -970,24 +1060,52 @@
     }
 
     @Test
+    public void createVirtualDpad_untrustedDisplay_failsSecurityException() {
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        assertThrows(SecurityException.class,
+                () -> mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER));
+    }
+
+    @Test
     public void createVirtualKeyboard_noDisplay_failsSecurityException() {
         assertThrows(SecurityException.class,
                 () -> mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER));
     }
 
     @Test
+    public void createVirtualKeyboard_untrustedDisplay_failsSecurityException() {
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        assertThrows(SecurityException.class,
+                () -> mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER));
+    }
+
+    @Test
     public void createVirtualMouse_noDisplay_failsSecurityException() {
         assertThrows(SecurityException.class,
                 () -> mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER));
     }
 
     @Test
+    public void createVirtualMouse_untrustedDisplay_failsSecurityException() {
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        assertThrows(SecurityException.class,
+                () -> mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER));
+    }
+
+    @Test
     public void createVirtualTouchscreen_noDisplay_failsSecurityException() {
         assertThrows(SecurityException.class,
                 () -> mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER));
     }
 
     @Test
+    public void createVirtualTouchscreen_untrustedDisplay_failsSecurityException() {
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        assertThrows(SecurityException.class,
+                () -> mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER));
+    }
+
+    @Test
     public void createVirtualTouchscreen_zeroDisplayDimension_failsIllegalArgumentException() {
         assertThrows(IllegalArgumentException.class,
                 () -> new VirtualTouchscreenConfig.Builder(
@@ -1003,7 +1121,7 @@
 
     @Test
     public void createVirtualTouchscreen_positiveDisplayDimension_successful() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         VirtualTouchscreenConfig positiveConfig =
                 new VirtualTouchscreenConfig.Builder(
                         /* touchscrenWidth= */ 600, /* touchscreenHeight= */ 800)
@@ -1026,6 +1144,14 @@
     }
 
     @Test
+    public void createVirtualNavigationTouchpad_untrustedDisplay_failsSecurityException() {
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        assertThrows(SecurityException.class,
+                () -> mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG,
+                        BINDER));
+    }
+
+    @Test
     public void createVirtualNavigationTouchpad_zeroDisplayDimension_failsWithException() {
         assertThrows(IllegalArgumentException.class,
                 () -> new VirtualNavigationTouchpadConfig.Builder(
@@ -1041,7 +1167,7 @@
 
     @Test
     public void createVirtualNavigationTouchpad_positiveDisplayDimension_successful() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         VirtualNavigationTouchpadConfig positiveConfig =
                 new VirtualNavigationTouchpadConfig.Builder(
                         /* touchpadHeight= */ 50, /* touchpadWidth= */ 50)
@@ -1065,72 +1191,8 @@
     }
 
     @Test
-    public void createVirtualDpad_noPermission_failsSecurityException() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        // Shell doesn't have CREATE_VIRTUAL_DEVICE permission.
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                assertThrows(SecurityException.class,
-                        () -> mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER)));
-    }
-
-    @Test
-    public void createVirtualKeyboard_noPermission_failsSecurityException() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        // Shell doesn't have CREATE_VIRTUAL_DEVICE permission.
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                assertThrows(SecurityException.class,
-                        () -> mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER)));
-    }
-
-    @Test
-    public void createVirtualMouse_noPermission_failsSecurityException() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        // Shell doesn't have CREATE_VIRTUAL_DEVICE permission.
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                assertThrows(SecurityException.class,
-                        () -> mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER)));
-    }
-
-    @Test
-    public void createVirtualTouchscreen_noPermission_failsSecurityException() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        // Shell doesn't have CREATE_VIRTUAL_DEVICE permission.
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                assertThrows(SecurityException.class,
-                        () -> mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER)));
-    }
-
-    @Test
-    public void createVirtualNavigationTouchpad_noPermission_failsSecurityException() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        // Shell doesn't have CREATE_VIRTUAL_DEVICE permission.
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                assertThrows(SecurityException.class,
-                        () -> mDeviceImpl.createVirtualNavigationTouchpad(
-                                NAVIGATION_TOUCHPAD_CONFIG,
-                                BINDER)));
-    }
-
-    @Test
-    public void onAudioSessionStarting_noPermission_failsSecurityException() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        // Shell doesn't have CREATE_VIRTUAL_DEVICE permission.
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                assertThrows(SecurityException.class,
-                        () -> mDeviceImpl.onAudioSessionStarting(
-                                DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback)));
-    }
-
-    @Test
-    public void onAudioSessionEnded_noPermission_failsSecurityException() {
-        // Shell doesn't have CREATE_VIRTUAL_DEVICE permission.
-        SystemUtil.runWithShellPermissionIdentity(() ->
-                assertThrows(SecurityException.class, () -> mDeviceImpl.onAudioSessionEnded()));
-    }
-
-    @Test
     public void createVirtualDpad_hasDisplay_obtainFileDescriptor() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER);
         assertWithMessage("Virtual dpad should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1140,7 +1202,7 @@
 
     @Test
     public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1150,7 +1212,7 @@
 
     @Test
     public void createVirtualKeyboard_keyboardCreated_localeUpdated() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches")
                 .that(mInputController.getInputDeviceDescriptors())
@@ -1171,7 +1233,7 @@
                         .setAssociatedDisplayId(DISPLAY_ID_1)
                         .build();
 
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualKeyboard(configWithoutExplicitLayoutInfo, BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches")
                 .that(mInputController.getInputDeviceDescriptors())
@@ -1192,7 +1254,7 @@
 
     @Test
     public void createVirtualMouse_hasDisplay_obtainFileDescriptor() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER);
         assertWithMessage("Virtual mouse should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1202,7 +1264,7 @@
 
     @Test
     public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER);
         assertWithMessage("Virtual touchscreen should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -1212,7 +1274,7 @@
 
     @Test
     public void createVirtualNavigationTouchpad_hasDisplay_obtainFileDescriptor() {
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1, Display.FLAG_TRUSTED);
         mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG, BINDER);
         assertWithMessage("Virtual navigation touchpad should register fd when the display matches")
                 .that(
@@ -1472,9 +1534,9 @@
 
     @Test
     public void setShowPointerIcon_setsValueForAllDisplays() {
-        addVirtualDisplay(mDeviceImpl, 1);
-        addVirtualDisplay(mDeviceImpl, 2);
-        addVirtualDisplay(mDeviceImpl, 3);
+        addVirtualDisplay(mDeviceImpl, 1, Display.FLAG_TRUSTED);
+        addVirtualDisplay(mDeviceImpl, 2, Display.FLAG_TRUSTED);
+        addVirtualDisplay(mDeviceImpl, 3, Display.FLAG_TRUSTED);
         VirtualMouseConfig config1 = new VirtualMouseConfig.Builder()
                 .setAssociatedDisplayId(1)
                 .setInputDeviceName(DEVICE_NAME_1)
@@ -1507,6 +1569,14 @@
     }
 
     @Test
+    public void setShowPointerIcon_untrustedDisplay_pointerIconIsAlwaysShown() {
+        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+        clearInvocations(mInputManagerInternalMock);
+        mDeviceImpl.setShowPointerIcon(false);
+        verify(mInputManagerInternalMock, times(0)).setPointerIconVisible(eq(false), anyInt());
+    }
+
+    @Test
     public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() {
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
@@ -1968,15 +2038,20 @@
     }
 
     private void addVirtualDisplay(VirtualDeviceImpl virtualDevice, int displayId) {
+        addVirtualDisplay(virtualDevice, displayId, /* flags= */ 0);
+    }
+
+    private void addVirtualDisplay(VirtualDeviceImpl virtualDevice, int displayId, int flags) {
         when(mDisplayManagerInternalMock.createVirtualDisplay(any(), eq(mVirtualDisplayCallback),
                 eq(virtualDevice), any(), any())).thenReturn(displayId);
-        virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback);
         final String uniqueId = UNIQUE_ID + displayId;
         doAnswer(inv -> {
             final DisplayInfo displayInfo = new DisplayInfo();
             displayInfo.uniqueId = uniqueId;
+            displayInfo.flags = flags;
             return displayInfo;
         }).when(mDisplayManagerInternalMock).getDisplayInfo(eq(displayId));
+        virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback);
         mInputManagerMockHelper.addDisplayIdMapping(uniqueId, displayId);
     }
 
@@ -1998,6 +2073,7 @@
                 /* tag= */ null, MacAddress.BROADCAST_ADDRESS, displayName, deviceProfile,
                 /* associatedDevice= */ null, /* selfManaged= */ true,
                 /* notifyOnDeviceNearby= */ false, /* revoked= */ false, /* pending= */ false,
-                /* timeApprovedMs= */0, /* lastTimeConnectedMs= */0, /* systemDataSyncFlags= */ -1);
+                /* timeApprovedMs= */0, /* lastTimeConnectedMs= */0,
+                /* systemDataSyncFlags= */ -1, /* deviceIcon= */ null);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 9df7a36..1d07540 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -33,6 +34,11 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.os.Build;
+import android.os.Process;
+
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -43,6 +49,7 @@
 import com.android.server.LocalServices;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -55,6 +62,8 @@
 public class PlatformCompatTest {
     private static final String PACKAGE_NAME = "my.package";
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private Context mContext;
     @Mock
@@ -441,4 +450,79 @@
         assertThat(mPlatformCompat.isChangeEnabled(3L, systemAppInfo)).isTrue();
         verify(mChangeReporter).reportChange(123, 3L, ChangeReporter.STATE_ENABLED, true, false);
     }
+
+    @DisableFlags(Flags.FLAG_SYSTEM_UID_TARGET_SYSTEM_SDK)
+    @Test
+    public void testSharedSystemUidFlagOff() throws Exception {
+        testSharedSystemUid(false);
+    }
+
+    @EnableFlags(Flags.FLAG_SYSTEM_UID_TARGET_SYSTEM_SDK)
+    @Test
+    public void testSharedSystemUidFlagOn() throws Exception {
+        testSharedSystemUid(true);
+    }
+
+    private void testSharedSystemUid(Boolean expectSystemUidTargetSystemSdk) throws Exception {
+        final String systemUidPackageNameTargetsR = "systemuid.package1";
+        final String systemUidPackageNameTargetsQ = "systemuid.package2";
+        final String nonSystemUidPackageNameTargetsR = "nonsystemuid.package1";
+        final String nonSystemUidPackageNameTargetsQ = "nonsystemuid.package2";
+        final int nonSystemUid = 123;
+
+        mCompatConfig =
+                CompatConfigBuilder.create(mBuildClassifier, mContext)
+                        .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 1L)
+                        .build();
+        mCompatConfig.forceNonDebuggableFinalForTest(true);
+        mPlatformCompat =
+                new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
+
+        ApplicationInfo systemUidAppInfo1 = ApplicationInfoBuilder.create()
+            .withPackageName(systemUidPackageNameTargetsR)
+            .withUid(Process.SYSTEM_UID)
+            .withTargetSdk(Build.VERSION_CODES.R)
+            .build();
+        when(mPackageManagerInternal.getApplicationInfo(
+                 eq(systemUidPackageNameTargetsR), anyLong(), anyInt(), anyInt()))
+            .thenReturn(systemUidAppInfo1);
+
+        ApplicationInfo systemUidAppInfo2 = ApplicationInfoBuilder.create()
+            .withPackageName(systemUidPackageNameTargetsQ)
+            .withUid(Process.SYSTEM_UID)
+            .withTargetSdk(Build.VERSION_CODES.Q)
+            .build();
+        when(mPackageManagerInternal.getApplicationInfo(
+                 eq(systemUidPackageNameTargetsQ), anyLong(), anyInt(), anyInt()))
+            .thenReturn(systemUidAppInfo2);
+
+        ApplicationInfo nonSystemUidAppInfo1 = ApplicationInfoBuilder.create()
+            .withPackageName(nonSystemUidPackageNameTargetsR)
+            .withUid(nonSystemUid)
+            .withTargetSdk(Build.VERSION_CODES.R)
+            .build();
+        when(mPackageManagerInternal.getApplicationInfo(
+                 eq(nonSystemUidPackageNameTargetsR), anyLong(), anyInt(), anyInt()))
+            .thenReturn(nonSystemUidAppInfo1);
+
+        ApplicationInfo nonSystemUidAppInfo2 = ApplicationInfoBuilder.create()
+            .withPackageName(nonSystemUidPackageNameTargetsQ)
+            .withUid(nonSystemUid)
+            .withTargetSdk(Build.VERSION_CODES.Q)
+            .build();
+        when(mPackageManagerInternal.getApplicationInfo(
+                 eq(nonSystemUidPackageNameTargetsQ), anyLong(), anyInt(), anyInt()))
+            .thenReturn(nonSystemUidAppInfo2);
+
+        when(mPackageManager.getPackagesForUid(eq(Process.SYSTEM_UID)))
+            .thenReturn(new String[] {systemUidPackageNameTargetsR, systemUidPackageNameTargetsQ});
+        when(mPackageManager.getPackagesForUid(eq(nonSystemUid)))
+            .thenReturn(new String[] {
+                            nonSystemUidPackageNameTargetsR, nonSystemUidPackageNameTargetsQ
+                        });
+
+        assertThat(mPlatformCompat.isChangeEnabledByUid(1L, Process.SYSTEM_UID))
+            .isEqualTo(expectSystemUidTargetSystemSdk);
+        assertThat(mPlatformCompat.isChangeEnabledByUid(1L, nonSystemUid)).isFalse();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 733f056..ab5a5a9 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -24,13 +24,9 @@
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertThrows;
 
-import android.annotation.NonNull;
+import android.content.Context;
 import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateRequest;
@@ -39,16 +35,20 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.annotation.NonNull;
 import androidx.test.filters.FlakyTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.PollingCheck;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowProcessController;
+import com.android.window.flags.Flags;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -61,18 +61,30 @@
 
 import javax.annotation.Nullable;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 /**
  * Unit tests for {@link DeviceStateManagerService}.
- * <p/>
- * Run with <code>atest DeviceStateManagerServiceTest</code>.
+ *
+ * <p> Build/Install/Run:
+ * atest FrameworksServicesTests:DeviceStateManagerServiceTest
  */
 @Presubmit
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 public final class DeviceStateManagerServiceTest {
     private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(
             new DeviceState.Configuration.Builder(0, "DEFAULT").build());
+    private static final int DEFAULT_DEVICE_STATE_IDENTIFIER = DEFAULT_DEVICE_STATE.getIdentifier();
+    private static final String DEFAULT_DEVICE_STATE_TRACE_STRING =
+            DEFAULT_DEVICE_STATE_IDENTIFIER + ":" + DEFAULT_DEVICE_STATE.getName();
+
     private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(
             new DeviceState.Configuration.Builder(1, "DEFAULT").build());
+    private static final int OTHER_DEVICE_STATE_IDENTIFIER = OTHER_DEVICE_STATE.getIdentifier();
+    private static final String OTHER_DEVICE_STATE_TRACE_STRING =
+            OTHER_DEVICE_STATE_IDENTIFIER + ":" + OTHER_DEVICE_STATE.getName();
+
     private static final DeviceState DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
             new DeviceState(new DeviceState.Configuration.Builder(2,
                     "DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP")
@@ -93,24 +105,43 @@
 
     private static final int TIMEOUT = 2000;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule;
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(Flags.FLAG_WLINFO_ONCREATE);
+    }
+
+    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    @NonNull
     private TestDeviceStatePolicy mPolicy;
+    @NonNull
     private TestDeviceStateProvider mProvider;
+    @NonNull
     private DeviceStateManagerService mService;
+    @NonNull
     private TestSystemPropertySetter mSysPropSetter;
+    @NonNull
     private WindowProcessController mWindowProcessController;
 
+    public DeviceStateManagerServiceTest(FlagsParameterization flags) {
+        mSetFlagsRule = new SetFlagsRule(flags);
+    }
+
     @Before
     public void setup() {
         mProvider = new TestDeviceStateProvider();
-        mPolicy = new TestDeviceStatePolicy(mProvider);
+        mPolicy = new TestDeviceStatePolicy(mContext, mProvider);
         mSysPropSetter = new TestSystemPropertySetter();
         setupDeviceStateManagerService();
-        flushHandler(); // Flush the handler to ensure the initial values are committed.
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler(); // Flush the handler to ensure the initial values are committed.
+        }
     }
 
     private void setupDeviceStateManagerService() {
-        mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy,
-                mSysPropSetter);
+        mService = new DeviceStateManagerService(mContext, mPolicy, mSysPropSetter);
 
         // Necessary to allow us to check for top app process id in tests
         mService.mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
@@ -136,56 +167,53 @@
 
     @Test
     public void baseStateChanged() {
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
 
-        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE_IDENTIFIER);
         flushHandler();
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
     public void baseStateChanged_withStatePendingPolicyCallback() {
         mPolicy.blockConfigure();
 
-        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE_IDENTIFIER);
         flushHandler();
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
-        mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
+        mProvider.setState(DEFAULT_DEVICE_STATE_IDENTIFIER);
         flushHandler();
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
         mPolicy.resumeConfigure();
         flushHandler();
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
@@ -194,13 +222,12 @@
             mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
         });
 
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
@@ -208,25 +235,23 @@
         assertThrows(IllegalArgumentException.class,
                 () -> mProvider.setState(INVALID_DEVICE_STATE_IDENTIFIER));
 
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
     public void supportedStatesChanged() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
         assertThat(mService.getSupportedStates()).containsExactly(DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE, DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
 
@@ -235,30 +260,31 @@
 
         // The current committed and requests states do not change because the current state remains
         // supported.
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
         assertThat(mService.getSupportedStates()).containsExactly(DEFAULT_DEVICE_STATE);
 
-        assertEquals(callback.getLastNotifiedInfo().supportedStates, List.of(DEFAULT_DEVICE_STATE));
+        assertThat(callback.getLastNotifiedInfo().supportedStates)
+                .containsExactly(DEFAULT_DEVICE_STATE);
     }
 
     @Test
     public void supportedStatesChanged_statesRemainSame() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
-        // An initial callback will be triggered on registration, so we clear it here.
-        flushHandler();
-        callback.clearLastNotifiedInfo();
+        if (!Flags.wlinfoOncreate()) {
+            // An initial callback will be triggered on registration, so we clear it here.
+            flushHandler();
+            callback.clearLastNotifiedInfo();
+        }
 
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
         assertThat(mService.getSupportedStates()).containsExactly(DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE, DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
 
@@ -268,26 +294,26 @@
 
         // The current committed and requests states do not change because the current state remains
         // supported.
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
         assertThat(mService.getSupportedStates()).containsExactly(DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE, DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
 
         // The callback wasn't notified about a change in supported states as the states have not
         // changed.
-        assertNull(callback.getLastNotifiedInfo());
+        assertThat(callback.getLastNotifiedInfo()).isNull();
     }
 
     @Test
     public void getDeviceStateInfo() throws RemoteException {
-        DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
-        assertNotNull(info);
-        assertEquals(info.supportedStates, SUPPORTED_DEVICE_STATES);
-        assertEquals(info.baseState, DEFAULT_DEVICE_STATE);
-        assertEquals(info.currentState, DEFAULT_DEVICE_STATE);
+        final DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
+        assertThat(info).isNotNull();
+        assertThat(info.supportedStates)
+                .containsExactlyElementsIn(SUPPORTED_DEVICE_STATES).inOrder();
+        assertThat(info.baseState).isEqualTo(DEFAULT_DEVICE_STATE);
+        assertThat(info.currentState).isEqualTo(DEFAULT_DEVICE_STATE);
     }
 
     @FlakyTest(bugId = 297949293)
@@ -295,105 +321,129 @@
     public void getDeviceStateInfo_baseStateAndCommittedStateNotSet() throws RemoteException {
         // Create a provider and a service without an initial base state.
         mProvider = new TestDeviceStateProvider(null /* initialState */);
-        mPolicy = new TestDeviceStatePolicy(mProvider);
+        mPolicy = new TestDeviceStatePolicy(mContext, mProvider);
         setupDeviceStateManagerService();
-        flushHandler(); // Flush the handler to ensure the initial values are committed.
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler(); // Flush the handler to ensure the initial values are committed.
+        }
 
-        DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
+        final DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
 
-        assertEquals(info.supportedStates, SUPPORTED_DEVICE_STATES);
-        assertEquals(info.baseState.getIdentifier(), INVALID_DEVICE_STATE_IDENTIFIER);
-        assertEquals(info.currentState.getIdentifier(), INVALID_DEVICE_STATE_IDENTIFIER);
+        assertThat(info.supportedStates)
+                .containsExactlyElementsIn(SUPPORTED_DEVICE_STATES).inOrder();
+        assertThat(info.baseState.getIdentifier()).isEqualTo(INVALID_DEVICE_STATE_IDENTIFIER);
+        assertThat(info.currentState.getIdentifier()).isEqualTo(INVALID_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
     public void registerCallback() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
-        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE_IDENTIFIER);
+        if (Flags.wlinfoOncreate()) {
+            waitAndAssert(() -> callback.getLastNotifiedInfo() != null);
+        }
         waitAndAssert(() -> callback.getLastNotifiedInfo().baseState.getIdentifier()
-                == OTHER_DEVICE_STATE.getIdentifier());
+                == OTHER_DEVICE_STATE_IDENTIFIER);
         waitAndAssert(() -> callback.getLastNotifiedInfo().currentState.getIdentifier()
-                == OTHER_DEVICE_STATE.getIdentifier());
+                == OTHER_DEVICE_STATE_IDENTIFIER);
 
-        mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
+        mProvider.setState(DEFAULT_DEVICE_STATE_IDENTIFIER);
         waitAndAssert(() -> callback.getLastNotifiedInfo().baseState.getIdentifier()
-                == DEFAULT_DEVICE_STATE.getIdentifier());
-
+                == DEFAULT_DEVICE_STATE_IDENTIFIER);
         waitAndAssert(() -> callback.getLastNotifiedInfo().currentState.getIdentifier()
-                == DEFAULT_DEVICE_STATE.getIdentifier());
+                == DEFAULT_DEVICE_STATE_IDENTIFIER);
 
         mPolicy.blockConfigure();
-        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE_IDENTIFIER);
         // The callback should not have been notified of the state change as the policy is still
         // pending callback.
         waitAndAssert(() -> callback.getLastNotifiedInfo().baseState.getIdentifier()
-                == DEFAULT_DEVICE_STATE.getIdentifier());
+                == DEFAULT_DEVICE_STATE_IDENTIFIER);
         waitAndAssert(() -> callback.getLastNotifiedInfo().currentState.getIdentifier()
-                == DEFAULT_DEVICE_STATE.getIdentifier());
+                == DEFAULT_DEVICE_STATE_IDENTIFIER);
 
         mPolicy.resumeConfigure();
         // Now that the policy is finished processing the callback should be notified of the state
         // change.
         waitAndAssert(() -> callback.getLastNotifiedInfo().baseState.getIdentifier()
-                == OTHER_DEVICE_STATE.getIdentifier());
+                == OTHER_DEVICE_STATE_IDENTIFIER);
         waitAndAssert(() -> callback.getLastNotifiedInfo().currentState.getIdentifier()
-                == OTHER_DEVICE_STATE.getIdentifier());
+                == OTHER_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
-    public void registerCallback_emitsInitialValue() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
-        mService.getBinderService().registerCallback(callback);
-        flushHandler();
-        assertNotNull(callback.getLastNotifiedInfo());
-        assertEquals(callback.getLastNotifiedInfo().baseState, DEFAULT_DEVICE_STATE);
-        assertEquals(callback.getLastNotifiedInfo().currentState, DEFAULT_DEVICE_STATE);
+    public void registerCallback_initialValueAvailable_emitsDeviceState() throws RemoteException {
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+
+        final DeviceStateInfo stateInfo;
+        if (Flags.wlinfoOncreate()) {
+            stateInfo = mService.getBinderService().registerCallback(callback);
+        } else {
+            mService.getBinderService().registerCallback(callback);
+            flushHandler();
+            stateInfo = callback.getLastNotifiedInfo();
+        }
+
+        assertThat(stateInfo).isNotNull();
+        assertThat(stateInfo.baseState).isEqualTo(DEFAULT_DEVICE_STATE);
+        assertThat(stateInfo.currentState).isEqualTo(DEFAULT_DEVICE_STATE);
     }
 
     @Test
-    public void registerCallback_initialValueUnavailable() throws RemoteException {
+    public void registerCallback_initialValueUnavailable_nullDeviceState() throws RemoteException {
         // Create a provider and a service without an initial base state.
         mProvider = new TestDeviceStateProvider(null /* initialState */);
-        mPolicy = new TestDeviceStatePolicy(mProvider);
+        mPolicy = new TestDeviceStatePolicy(mContext, mProvider);
         setupDeviceStateManagerService();
-        flushHandler(); // Flush the handler to ensure the initial values are committed.
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler(); // Flush the handler to ensure the initial values are committed.
+        }
 
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
-        mService.getBinderService().registerCallback(callback);
-        flushHandler();
-        // The callback should never be called when the base state is not set yet.
-        assertNull(callback.getLastNotifiedInfo());
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final DeviceStateInfo stateInfo;
+        if (Flags.wlinfoOncreate()) {
+            // Return null when the base state is not set yet.
+            stateInfo = mService.getBinderService().registerCallback(callback);
+        } else {
+            mService.getBinderService().registerCallback(callback);
+            flushHandler();
+            // The callback should never be called when the base state is not set yet.
+            stateInfo = callback.getLastNotifiedInfo();
+        }
+
+        assertThat(stateInfo).isNull();
     }
 
     @Test
     public void requestState() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
-        flushHandler();
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler();
+        }
 
         final IBinder token = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
-        mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
-                0 /* flags */);
+        mService.getBinderService()
+                .requestState(token, OTHER_DEVICE_STATE_IDENTIFIER, 0 /* flags */);
 
         waitAndAssert(() -> callback.getLastNotifiedStatus(token)
                 == TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getOverrideState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
-        assertNotNull(callback.getLastNotifiedInfo());
-        assertEquals(callback.getLastNotifiedInfo().baseState, DEFAULT_DEVICE_STATE);
-        assertEquals(callback.getLastNotifiedInfo().currentState, OTHER_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo()).isNotNull();
+        assertThat(callback.getLastNotifiedInfo().baseState).isEqualTo(DEFAULT_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo().currentState).isEqualTo(OTHER_DEVICE_STATE);
 
         mService.getBinderService().cancelStateRequest();
 
@@ -401,156 +451,156 @@
                 == TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set back to the requested state once the override is cleared.
         waitAndAssert(() -> mService.getCommittedState().equals(Optional.of(DEFAULT_DEVICE_STATE)));
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertFalse(mService.getOverrideState().isPresent());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getOverrideState()).isEmpty();
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
 
-        assertEquals(callback.getLastNotifiedInfo().baseState, DEFAULT_DEVICE_STATE);
-        assertEquals(callback.getLastNotifiedInfo().currentState, DEFAULT_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo().baseState).isEqualTo(DEFAULT_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo().currentState).isEqualTo(DEFAULT_DEVICE_STATE);
     }
 
     @FlakyTest(bugId = 200332057)
     @Test
     public void requestState_pendingStateAtRequest() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
-        flushHandler();
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler();
+        }
 
         mPolicy.blockConfigure();
 
         final IBinder firstRequestToken = new Binder();
         final IBinder secondRequestToken = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
-        assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(firstRequestToken))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(secondRequestToken))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
-        mService.getBinderService().requestState(firstRequestToken,
-                OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+        mService.getBinderService()
+                .requestState(firstRequestToken, OTHER_DEVICE_STATE_IDENTIFIER, 0 /* flags */);
         // Flush the handler twice. The first flush ensures the request is added and the policy is
         // notified, while the second flush ensures the callback is notified once the change is
         // committed.
         flushHandler(2 /* count */);
 
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
-        mService.getBinderService().requestState(secondRequestToken,
-                DEFAULT_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+        mService.getBinderService()
+                .requestState(secondRequestToken, DEFAULT_DEVICE_STATE_IDENTIFIER, 0 /* flags */);
         mPolicy.resumeConfigureOnce();
         flushHandler();
 
         // First request status is now canceled as there is another pending request.
-        assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
-                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        assertThat(callback.getLastNotifiedStatus(firstRequestToken))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Second request status still unknown because the service is still awaiting policy
         // callback.
-        assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(callback.getLastNotifiedStatus(secondRequestToken))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getPendingState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
 
         mPolicy.resumeConfigure();
         flushHandler();
 
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
 
         // Now cancel the second request to make the first request active.
         mService.getBinderService().cancelStateRequest();
         flushHandler();
 
-        assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
-                TestDeviceStateManagerCallback.STATUS_CANCELED);
-        assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
-                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        assertThat(callback.getLastNotifiedStatus(firstRequestToken))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_CANCELED);
+        assertThat(callback.getLastNotifiedStatus(secondRequestToken))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_CANCELED);
 
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getPendingState(), Optional.empty());
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getPendingState()).isEmpty();
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
     public void requestState_sameAsBaseState() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
-        flushHandler();
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler();
+        }
 
         final IBinder token = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
-        mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
-                0 /* flags */);
+        mService.getBinderService()
+                .requestState(token, DEFAULT_DEVICE_STATE_IDENTIFIER, 0 /* flags */);
         flushHandler();
 
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_ACTIVE);
     }
 
     @Test
     public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
-        flushHandler();
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler();
+        }
 
         final IBinder token = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
-        mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+        mService.getBinderService().requestState(token, OTHER_DEVICE_STATE_IDENTIFIER,
                 DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
         // Flush the handler twice. The first flush ensures the request is added and the policy is
         // notified, while the second flush ensures the callback is notified once the change is
         // committed.
         flushHandler(2 /* count */);
 
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_ACTIVE);
 
         // Committed state changes as there is a requested override.
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getOverrideState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
-        mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        mProvider.setState(OTHER_DEVICE_STATE_IDENTIFIER);
         flushHandler();
 
         // Request is canceled because the base state changed.
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set back to the requested state once the override is cleared.
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertFalse(mService.getOverrideState().isPresent());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getBaseState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getOverrideState()).isEmpty();
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
@@ -581,8 +631,8 @@
         requestState_flagCancelWhenRequesterNotOnTop_common(
                 // When the app is foreground, the state should not change
                 () -> {
-                    int pid = Binder.getCallingPid();
-                    int uid = Binder.getCallingUid();
+                    final int pid = Binder.getCallingPid();
+                    final int uid = Binder.getCallingUid();
                     try {
                         mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
                                 true /* foregroundActivities */);
@@ -594,8 +644,8 @@
                 () -> {
                     when(mWindowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID);
                     try {
-                        int pid = Binder.getCallingPid();
-                        int uid = Binder.getCallingUid();
+                        final int pid = Binder.getCallingPid();
+                        final int uid = Binder.getCallingUid();
                         mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
                                 false /* foregroundActivities */);
 
@@ -609,68 +659,68 @@
     @FlakyTest(bugId = 200332057)
     @Test
     public void requestState_becomesUnsupported() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
-        flushHandler();
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler();
+        }
 
         final IBinder token = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
-        mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
-                0 /* flags */);
+        mService.getBinderService()
+                .requestState(token, OTHER_DEVICE_STATE_IDENTIFIER, 0 /* flags */);
         flushHandler();
 
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getOverrideState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
         mProvider.notifySupportedDeviceStates(
                 new DeviceState[]{DEFAULT_DEVICE_STATE});
         flushHandler();
 
         // Request is canceled because the state is no longer supported.
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set back to the requested state as the override state is no longer
         // supported.
-        assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertFalse(mService.getOverrideState().isPresent());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getOverrideState()).isEmpty();
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
     }
 
     @Test
     public void requestState_unsupportedState() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
         assertThrows(IllegalArgumentException.class, () -> {
             final IBinder token = new Binder();
-            mService.getBinderService().requestState(token,
-                    UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+            mService.getBinderService()
+                    .requestState(token, UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
         });
     }
 
     @Test
     public void requestState_invalidState() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
         assertThrows(IllegalArgumentException.class, () -> {
             final IBinder token = new Binder();
-            mService.getBinderService().requestState(token, INVALID_DEVICE_STATE_IDENTIFIER,
-                    0 /* flags */);
+            mService.getBinderService()
+                    .requestState(token, INVALID_DEVICE_STATE_IDENTIFIER, 0 /* flags */);
         });
     }
 
@@ -678,40 +728,41 @@
     public void requestState_beforeRegisteringCallback() {
         assertThrows(IllegalStateException.class, () -> {
             final IBinder token = new Binder();
-            mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
-                    0 /* flags */);
+            mService.getBinderService()
+                    .requestState(token, DEFAULT_DEVICE_STATE_IDENTIFIER, 0 /* flags */);
         });
     }
 
     @Test
     public void requestBaseStateOverride() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
-        flushHandler();
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler();
+        }
 
         final IBinder token = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
         mService.getBinderService().requestBaseStateOverride(token,
-                OTHER_DEVICE_STATE.getIdentifier(),
+                OTHER_DEVICE_STATE_IDENTIFIER,
                 0 /* flags */);
 
         waitAndAssert(() -> callback.getLastNotifiedStatus(token)
                 == TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
-        assertFalse(mService.getOverrideState().isPresent());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getOverrideBaseState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getOverrideState()).isEmpty();
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
-        assertNotNull(callback.getLastNotifiedInfo());
-        assertEquals(callback.getLastNotifiedInfo().baseState, OTHER_DEVICE_STATE);
-        assertEquals(callback.getLastNotifiedInfo().currentState, OTHER_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo()).isNotNull();
+        assertThat(callback.getLastNotifiedInfo().baseState).isEqualTo(OTHER_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo().currentState).isEqualTo(OTHER_DEVICE_STATE);
 
         mService.getBinderService().cancelBaseStateOverride();
 
@@ -719,52 +770,50 @@
                 == TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set back to the requested state once the override is cleared.
         waitAndAssert(() -> mService.getCommittedState().equals(Optional.of(DEFAULT_DEVICE_STATE)));
-        assertEquals(mSysPropSetter.getValue(),
-                DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
-        assertFalse(mService.getOverrideBaseState().isPresent());
-        assertFalse(mService.getOverrideState().isPresent());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                DEFAULT_DEVICE_STATE.getIdentifier());
+        assertThat(mSysPropSetter.getValue()).isEqualTo(DEFAULT_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(DEFAULT_DEVICE_STATE);
+        assertThat(mService.getOverrideBaseState()).isEmpty();
+        assertThat(mService.getOverrideState()).isEmpty();
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(DEFAULT_DEVICE_STATE_IDENTIFIER);
 
         waitAndAssert(() -> callback.getLastNotifiedInfo().baseState.getIdentifier()
-                == DEFAULT_DEVICE_STATE.getIdentifier());
-        assertEquals(callback.getLastNotifiedInfo().currentState, DEFAULT_DEVICE_STATE);
+                == DEFAULT_DEVICE_STATE_IDENTIFIER);
+        assertThat(callback.getLastNotifiedInfo().currentState).isEqualTo(DEFAULT_DEVICE_STATE);
     }
 
     @Test
     public void requestBaseStateOverride_cancelledByBaseStateUpdate() throws RemoteException {
-        final DeviceState testDeviceState = new DeviceState(new DeviceState.Configuration.Builder(2,
-                "TEST").build());
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final DeviceState testDeviceState = new DeviceState(
+                new DeviceState.Configuration.Builder(2, "TEST").build());
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
         mProvider.notifySupportedDeviceStates(new DeviceState[]{DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE, testDeviceState});
         flushHandler();
 
         final IBinder token = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
         mService.getBinderService().requestBaseStateOverride(token,
-                OTHER_DEVICE_STATE.getIdentifier(),
+                OTHER_DEVICE_STATE_IDENTIFIER,
                 0 /* flags */);
 
         waitAndAssert(() -> callback.getLastNotifiedStatus(token)
                 == TestDeviceStateManagerCallback.STATUS_ACTIVE);
         // Committed state changes as there is a requested override.
-        assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mSysPropSetter.getValue(),
-                OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
-        assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
-        assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
-        assertFalse(mService.getOverrideState().isPresent());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                OTHER_DEVICE_STATE.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mSysPropSetter.getValue()).isEqualTo(OTHER_DEVICE_STATE_TRACE_STRING);
+        assertThat(mService.getBaseState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getOverrideBaseState()).hasValue(OTHER_DEVICE_STATE);
+        assertThat(mService.getOverrideState()).isEmpty();
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(OTHER_DEVICE_STATE_IDENTIFIER);
 
-        assertNotNull(callback.getLastNotifiedInfo());
-        assertEquals(callback.getLastNotifiedInfo().baseState, OTHER_DEVICE_STATE);
-        assertEquals(callback.getLastNotifiedInfo().currentState, OTHER_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo()).isNotNull();
+        assertThat(callback.getLastNotifiedInfo().baseState).isEqualTo(OTHER_DEVICE_STATE);
+        assertThat(callback.getLastNotifiedInfo().currentState).isEqualTo(OTHER_DEVICE_STATE);
 
         mProvider.setState(testDeviceState.getIdentifier());
 
@@ -772,22 +821,22 @@
                 == TestDeviceStateManagerCallback.STATUS_CANCELED);
         // Committed state is set to the new base state once the override is cleared.
         waitAndAssert(() -> mService.getCommittedState().equals(Optional.of(testDeviceState)));
-        assertEquals(mSysPropSetter.getValue(),
-                testDeviceState.getIdentifier() + ":" + testDeviceState.getName());
-        assertEquals(mService.getBaseState(), Optional.of(testDeviceState));
-        assertFalse(mService.getOverrideBaseState().isPresent());
-        assertFalse(mService.getOverrideState().isPresent());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                testDeviceState.getIdentifier());
+        assertThat(mSysPropSetter.getValue())
+                .isEqualTo(testDeviceState.getIdentifier() + ":" + testDeviceState.getName());
+        assertThat(mService.getBaseState()).hasValue(testDeviceState);
+        assertThat(mService.getOverrideBaseState()).isEmpty();
+        assertThat(mService.getOverrideState()).isEmpty();
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(testDeviceState.getIdentifier());
 
         waitAndAssert(() -> callback.getLastNotifiedInfo().baseState.getIdentifier()
                 == testDeviceState.getIdentifier());
-        assertEquals(callback.getLastNotifiedInfo().currentState, testDeviceState);
+        assertThat(callback.getLastNotifiedInfo().currentState).isEqualTo(testDeviceState);
     }
 
     @Test
     public void requestBaseState_unsupportedState() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
         assertThrows(IllegalArgumentException.class, () -> {
@@ -799,7 +848,7 @@
 
     @Test
     public void requestBaseState_invalidState() throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
 
         assertThrows(IllegalArgumentException.class, () -> {
@@ -814,7 +863,7 @@
         assertThrows(IllegalStateException.class, () -> {
             final IBinder token = new Binder();
             mService.getBinderService().requestBaseStateOverride(token,
-                    DEFAULT_DEVICE_STATE.getIdentifier(),
+                    DEFAULT_DEVICE_STATE_IDENTIFIER,
                     0 /* flags */);
         });
     }
@@ -834,21 +883,23 @@
             Runnable noChangeEvent,
             Runnable autoCancelEvent
     ) throws RemoteException {
-        TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+        final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
-        flushHandler();
+        if (!Flags.wlinfoOncreate()) {
+            flushHandler();
+        }
 
         final IBinder token = new Binder();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_UNKNOWN);
 
         mService.getBinderService().requestState(token,
                 DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP.getIdentifier(),
                 0 /* flags */);
         flushHandler(2 /* count */);
 
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_ACTIVE);
 
         // Committed state changes as there is a requested override.
         assertDeviceStateConditions(
@@ -858,8 +909,8 @@
 
         noChangeEvent.run();
         flushHandler();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_ACTIVE);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_ACTIVE);
         assertDeviceStateConditions(
                 DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
                 DEFAULT_DEVICE_STATE, /* base state */
@@ -867,8 +918,8 @@
 
         autoCancelEvent.run();
         flushHandler();
-        assertEquals(callback.getLastNotifiedStatus(token),
-                TestDeviceStateManagerCallback.STATUS_CANCELED);
+        assertThat(callback.getLastNotifiedStatus(token))
+                .isEqualTo(TestDeviceStateManagerCallback.STATUS_CANCELED);
         assertDeviceStateConditions(DEFAULT_DEVICE_STATE, DEFAULT_DEVICE_STATE,
                 false /* isOverrideState */);
     }
@@ -881,20 +932,20 @@
      * @param isOverrideState whether a state override is active.
      */
     private void assertDeviceStateConditions(
-            DeviceState state, DeviceState baseState,
+            @NonNull DeviceState state, @NonNull DeviceState baseState,
             boolean isOverrideState) {
-        assertEquals(mService.getCommittedState(), Optional.of(state));
-        assertEquals(mService.getBaseState(), Optional.of(baseState));
-        assertEquals(mSysPropSetter.getValue(),
-                state.getIdentifier() + ":" + state.getName());
-        assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
-                state.getIdentifier());
+        assertThat(mService.getCommittedState()).hasValue(state);
+        assertThat(mService.getBaseState()).hasValue(baseState);
+        assertThat(mSysPropSetter.getValue())
+                .isEqualTo(state.getIdentifier() + ":" + state.getName());
+        assertThat(mPolicy.getMostRecentRequestedStateToConfigure())
+                .isEqualTo(state.getIdentifier());
         if (isOverrideState) {
             // When a state override is active, the committed state should batch the override state.
-            assertEquals(mService.getOverrideState().get(), state);
+            assertThat(mService.getOverrideState()).hasValue(state);
         } else {
             // When there is no state override, the override state should be empty.
-            assertFalse(mService.getOverrideState().isPresent());
+            assertThat(mService.getOverrideState()).isEmpty();
         }
     }
 
@@ -902,10 +953,11 @@
         private final DeviceStateProvider mProvider;
         private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE_IDENTIFIER;
         private boolean mConfigureBlocked = false;
+        @Nullable
         private Runnable mPendingConfigureCompleteRunnable;
 
-        TestDeviceStatePolicy(DeviceStateProvider provider) {
-            super(InstrumentationRegistry.getContext());
+        TestDeviceStatePolicy(@NonNull Context context, @NonNull DeviceStateProvider provider) {
+            super(context);
             mProvider = provider;
         }
 
@@ -921,7 +973,7 @@
         public void resumeConfigure() {
             mConfigureBlocked = false;
             if (mPendingConfigureCompleteRunnable != null) {
-                Runnable onComplete = mPendingConfigureCompleteRunnable;
+                final Runnable onComplete = mPendingConfigureCompleteRunnable;
                 mPendingConfigureCompleteRunnable = null;
                 onComplete.run();
             }
@@ -929,7 +981,7 @@
 
         public void resumeConfigureOnce() {
             if (mPendingConfigureCompleteRunnable != null) {
-                Runnable onComplete = mPendingConfigureCompleteRunnable;
+                final Runnable onComplete = mPendingConfigureCompleteRunnable;
                 mPendingConfigureCompleteRunnable = null;
                 onComplete.run();
             }
@@ -940,7 +992,7 @@
         }
 
         @Override
-        public void configureDeviceForState(int state, Runnable onComplete) {
+        public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
             if (mPendingConfigureCompleteRunnable != null) {
                 throw new IllegalStateException("configureDeviceForState() called while configure"
                         + " is pending");
@@ -966,7 +1018,9 @@
                         OTHER_DEVICE_STATE,
                         DEVICE_STATE_CANCEL_WHEN_REQUESTER_NOT_ON_TOP};
 
-        @Nullable private final DeviceState mInitialState;
+        @Nullable
+        private final DeviceState mInitialState;
+        @Nullable
         private Listener mListener;
 
         private TestDeviceStateProvider() {
@@ -991,7 +1045,7 @@
             }
         }
 
-        public void notifySupportedDeviceStates(DeviceState[] supportedDeviceStates) {
+        public void notifySupportedDeviceStates(@NonNull DeviceState[] supportedDeviceStates) {
             mSupportedDeviceStates = supportedDeviceStates;
             mListener.onSupportedDeviceStatesChanged(supportedDeviceStates,
                     SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
@@ -1018,17 +1072,17 @@
         private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
 
         @Override
-        public void onDeviceStateInfoChanged(DeviceStateInfo info) {
+        public void onDeviceStateInfoChanged(@NonNull DeviceStateInfo info) {
             mLastNotifiedInfo = info;
         }
 
         @Override
-        public void onRequestActive(IBinder token) {
+        public void onRequestActive(@NonNull IBinder token) {
             mLastNotifiedStatus.put(token, STATUS_ACTIVE);
         }
 
         @Override
-        public void onRequestCanceled(IBinder token) {
+        public void onRequestCanceled(@NonNull IBinder token) {
             mLastNotifiedStatus.put(token, STATUS_CANCELED);
         }
 
@@ -1041,20 +1095,22 @@
             mLastNotifiedInfo = null;
         }
 
-        int getLastNotifiedStatus(IBinder requestToken) {
+        int getLastNotifiedStatus(@NonNull IBinder requestToken) {
             return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
         }
     }
 
     private static final class TestSystemPropertySetter implements
             DeviceStateManagerService.SystemPropertySetter {
+        @NonNull
         private String mValue;
 
         @Override
-        public void setDebugTracingDeviceStateProperty(String value) {
+        public void setDebugTracingDeviceStateProperty(@NonNull String value) {
             mValue = value;
         }
 
+        @NonNull
         public String getValue() {
             return mValue;
         }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index eb9cce0..9c6412b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -67,11 +67,8 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.R;
-import com.android.internal.pm.parsing.PackageParser2;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.integrity.engine.RuleEvaluationEngine;
 import com.android.server.integrity.model.IntegrityCheckResult;
-import com.android.server.pm.parsing.TestPackageParser2;
 import com.android.server.testutils.TestUtils;
 
 import org.junit.After;
@@ -138,12 +135,9 @@
     @Mock PlatformCompat mPlatformCompat;
     @Mock Context mMockContext;
     @Mock Resources mMockResources;
-    @Mock RuleEvaluationEngine mRuleEvaluationEngine;
     @Mock IntegrityFileManager mIntegrityFileManager;
     @Mock Handler mHandler;
 
-    private Supplier<PackageParser2> mParserSupplier = TestPackageParser2::new;
-
     private final Context mRealContext = InstrumentationRegistry.getTargetContext();
 
     private PackageManager mSpyPackageManager;
@@ -175,8 +169,6 @@
                 new AppIntegrityManagerServiceImpl(
                         mMockContext,
                         mPackageManagerInternal,
-                        mParserSupplier,
-                        mRuleEvaluationEngine,
                         mIntegrityFileManager,
                         mHandler);
 
@@ -307,91 +299,6 @@
     }
 
     @Test
-    public void handleBroadcast_correctArgs() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext)
-                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
-
-        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-        runJobInHandler();
-
-        ArgumentCaptor<AppInstallMetadata> metadataCaptor =
-                ArgumentCaptor.forClass(AppInstallMetadata.class);
-        verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
-        AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
-        assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
-        assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
-        assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
-        // we cannot check installer cert because it seems to be device specific.
-        assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
-        assertFalse(appInstallMetadata.isPreInstalled());
-        // Asserting source stamp not present.
-        assertFalse(appInstallMetadata.isStampPresent());
-        assertFalse(appInstallMetadata.isStampVerified());
-        assertFalse(appInstallMetadata.isStampTrusted());
-        assertNull(appInstallMetadata.getStampCertificateHash());
-        // These are hardcoded in the test apk android manifest
-        Map<String, String> allowedInstallers =
-                appInstallMetadata.getAllowedInstallersAndCertificates();
-        assertEquals(2, allowedInstallers.size());
-        assertEquals(PLAY_STORE_CERT, allowedInstallers.get(PLAY_STORE_PKG));
-        assertEquals(INSTALLER_CERTIFICATE_NOT_EVALUATED, allowedInstallers.get(ADB_INSTALLER));
-    }
-
-    @Test
-    public void handleBroadcast_correctArgs_multipleCerts() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext)
-                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        Intent intent = makeVerificationIntent();
-        intent.setDataAndType(Uri.fromFile(mTestApkTwoCerts), PACKAGE_MIME_TYPE);
-        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
-
-        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-        runJobInHandler();
-
-        ArgumentCaptor<AppInstallMetadata> metadataCaptor =
-                ArgumentCaptor.forClass(AppInstallMetadata.class);
-        verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
-        AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
-        assertThat(appInstallMetadata.getAppCertificates())
-                .containsExactly(DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2);
-    }
-
-    @Test
-    public void handleBroadcast_correctArgs_sourceStamp() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext)
-                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        Intent intent = makeVerificationIntent();
-        intent.setDataAndType(Uri.fromFile(mTestApkSourceStamp), PACKAGE_MIME_TYPE);
-        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
-
-        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-        runJobInHandler();
-
-        ArgumentCaptor<AppInstallMetadata> metadataCaptor =
-                ArgumentCaptor.forClass(AppInstallMetadata.class);
-        verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
-        AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
-        assertTrue(appInstallMetadata.isStampPresent());
-        assertTrue(appInstallMetadata.isStampVerified());
-        assertTrue(appInstallMetadata.isStampTrusted());
-        assertEquals(SOURCE_STAMP_CERTIFICATE_HASH, appInstallMetadata.getStampCertificateHash());
-    }
-
-    @Test
     public void handleBroadcast_allow() throws Exception {
         allowlistUsAsRuleProvider();
         makeUsSystemApp();
@@ -400,7 +307,6 @@
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
@@ -411,32 +317,6 @@
     }
 
     @Test
-    public void handleBroadcast_reject() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext)
-                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        when(mRuleEvaluationEngine.evaluate(any()))
-                .thenReturn(
-                        IntegrityCheckResult.deny(
-                                Arrays.asList(
-                                        new Rule(
-                                                new AtomicFormula.BooleanAtomicFormula(
-                                                        AtomicFormula.PRE_INSTALLED, false),
-                                                Rule.DENY))));
-        Intent intent = makeVerificationIntent();
-
-        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-        runJobInHandler();
-
-        verify(mPackageManagerInternal)
-                .setIntegrityVerificationResult(
-                        1, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
-    }
-
-    @Test
     public void handleBroadcast_notInitialized() throws Exception {
         allowlistUsAsRuleProvider();
         makeUsSystemApp();
@@ -446,7 +326,6 @@
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
@@ -467,8 +346,6 @@
         verify(mMockContext, atLeastOnce())
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
-        when(mRuleEvaluationEngine.evaluate(any()))
-                .thenReturn(IntegrityCheckResult.deny(/* rule= */ null));
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
deleted file mode 100644
index 1c860ca..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.engine;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-
-import com.android.server.integrity.IntegrityFileManager;
-import com.android.server.integrity.model.IntegrityCheckResult;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(JUnit4.class)
-public class RuleEvaluationEngineTest {
-
-    private static final String INSTALLER_1 = "installer1";
-    private static final String INSTALLER_1_CERT = "installer1_cert";
-    private static final String INSTALLER_2 = "installer2";
-    private static final String INSTALLER_2_CERT = "installer2_cert";
-
-    private static final String RANDOM_INSTALLER = "random";
-    private static final String RANDOM_INSTALLER_CERT = "random_cert";
-
-    @Mock
-    private IntegrityFileManager mIntegrityFileManager;
-
-    private RuleEvaluationEngine mEngine;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mEngine = new RuleEvaluationEngine(mIntegrityFileManager);
-
-        when(mIntegrityFileManager.readRules(any())).thenReturn(Collections.singletonList(new Rule(
-                IntegrityFormula.Installer.notAllowedByManifest(), Rule.DENY)));
-
-        when(mIntegrityFileManager.initialized()).thenReturn(true);
-    }
-
-    @Test
-    public void testAllowedInstallers_empty() {
-        AppInstallMetadata appInstallMetadata1 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
-                        .build();
-        AppInstallMetadata appInstallMetadata2 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_2)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
-                        .build();
-        AppInstallMetadata appInstallMetadata3 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(RANDOM_INSTALLER)
-                        .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
-                        .build();
-
-        assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-        assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-        assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-    }
-
-    @Test
-    public void testAllowedInstallers_oneElement() {
-        Map<String, String> allowedInstallers =
-                Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT);
-
-        AppInstallMetadata appInstallMetadata1 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
-        AppInstallMetadata appInstallMetadata2 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(RANDOM_INSTALLER)
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.DENY);
-
-        AppInstallMetadata appInstallMetadata3 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_1)
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.DENY);
-
-        AppInstallMetadata appInstallMetadata4 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_1)
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata4).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.DENY);
-    }
-
-    @Test
-    public void testAllowedInstallers_multipleElement() {
-        Map<String, String> allowedInstallers = new HashMap<>(2);
-        allowedInstallers.put(INSTALLER_1, INSTALLER_1_CERT);
-        allowedInstallers.put(INSTALLER_2, INSTALLER_2_CERT);
-
-        AppInstallMetadata appInstallMetadata1 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_1)
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata1).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
-        AppInstallMetadata appInstallMetadata2 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_2)
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata2).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
-
-        AppInstallMetadata appInstallMetadata3 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_1)
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata3).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.DENY);
-
-        AppInstallMetadata appInstallMetadata4 =
-                getAppInstallMetadataBuilder()
-                        .setInstallerName(INSTALLER_2)
-                        .setAllowedInstallersAndCert(allowedInstallers)
-                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
-                        .build();
-        assertThat(mEngine.evaluate(appInstallMetadata4).getEffect())
-                .isEqualTo(IntegrityCheckResult.Effect.DENY);
-    }
-
-    /** Returns a builder with all fields filled with some placeholder data. */
-    private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
-        return new AppInstallMetadata.Builder()
-                .setPackageName("abc")
-                .setAppCertificates(Collections.singletonList("abc"))
-                .setAppCertificateLineage(Collections.singletonList("abc"))
-                .setInstallerCertificates(Collections.singletonList("abc"))
-                .setInstallerName("abc")
-                .setVersionCode(-1)
-                .setIsPreInstalled(true);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
deleted file mode 100644
index 5089f74..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.engine;
-
-import static com.android.server.integrity.model.IntegrityCheckResult.Effect.ALLOW;
-import static com.android.server.integrity.model.IntegrityCheckResult.Effect.DENY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.AtomicFormula.LongAtomicFormula;
-import android.content.integrity.AtomicFormula.StringAtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.Rule;
-
-import com.android.server.integrity.model.IntegrityCheckResult;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleEvaluatorTest {
-
-    private static final String PACKAGE_NAME_1 = "com.test.app";
-    private static final String PACKAGE_NAME_2 = "com.test.app2";
-    private static final String APP_CERTIFICATE = "test_cert";
-    private static final AppInstallMetadata APP_INSTALL_METADATA =
-            new AppInstallMetadata.Builder()
-                    .setPackageName(PACKAGE_NAME_1)
-                    .setAppCertificates(Collections.singletonList(APP_CERTIFICATE))
-                    .setAppCertificateLineage(Collections.singletonList(APP_CERTIFICATE))
-                    .setVersionCode(2)
-                    .build();
-
-    @Test
-    public void testEvaluateRules_noRules_allow() {
-        List<Rule> rules = new ArrayList<>();
-
-        IntegrityCheckResult result = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(ALLOW);
-    }
-
-    @Test
-    public void testEvaluateRules_noMatchedRules_allow() {
-        Rule rule =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                PACKAGE_NAME_2,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(ALLOW);
-    }
-
-    @Test
-    public void testEvaluateRules_oneMatch_deny() {
-        Rule rule1 =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                PACKAGE_NAME_1,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
-        Rule rule2 =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                PACKAGE_NAME_2,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(DENY);
-        assertThat(result.getMatchedRules()).containsExactly(rule1);
-    }
-
-    @Test
-    public void testEvaluateRules_multipleMatches_deny() {
-        Rule rule1 =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                PACKAGE_NAME_1,
-                                /* isHashedValue= */ false),
-                        Rule.DENY);
-        Rule rule2 = new Rule(
-                new CompoundFormula(
-                        CompoundFormula.AND,
-                        Arrays.asList(
-                                new StringAtomicFormula(
-                                        AtomicFormula.PACKAGE_NAME,
-                                        PACKAGE_NAME_1,
-                                        /* isHashedValue= */ false),
-                                new StringAtomicFormula(
-                                        AtomicFormula.APP_CERTIFICATE,
-                                        APP_CERTIFICATE,
-                                        /* isHashedValue= */ false))),
-                Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(DENY);
-        assertThat(result.getMatchedRules()).containsExactly(rule1, rule2);
-    }
-
-    @Test
-    public void testEvaluateRules_ruleWithNot_deny() {
-        Rule rule = new Rule(
-                new CompoundFormula(
-                        CompoundFormula.NOT,
-                        Collections.singletonList(
-                                new StringAtomicFormula(
-                                        AtomicFormula.PACKAGE_NAME,
-                                        PACKAGE_NAME_2,
-                                        /* isHashedValue= */ false))),
-                Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(DENY);
-        assertThat(result.getMatchedRules()).containsExactly(rule);
-    }
-
-    @Test
-    public void testEvaluateRules_ruleWithIntegerOperators_deny() {
-        Rule rule =
-                new Rule(
-                        new LongAtomicFormula(AtomicFormula.VERSION_CODE,
-                                AtomicFormula.GT, 1),
-                        Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(DENY);
-        assertThat(result.getMatchedRules()).containsExactly(rule);
-    }
-
-    @Test
-    public void testEvaluateRules_validForm_deny() {
-        Rule rule = new Rule(
-                new CompoundFormula(
-                        CompoundFormula.AND,
-                        Arrays.asList(
-                                new StringAtomicFormula(
-                                        AtomicFormula.PACKAGE_NAME,
-                                        PACKAGE_NAME_1,
-                                        /* isHashedValue= */ false),
-                                new StringAtomicFormula(
-                                        AtomicFormula.APP_CERTIFICATE,
-                                        APP_CERTIFICATE,
-                                        /* isHashedValue= */ false))),
-                Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(DENY);
-        assertThat(result.getMatchedRules()).containsExactly(rule);
-    }
-
-    @Test
-    public void testEvaluateRules_orRules() {
-        Rule rule = new Rule(
-                new CompoundFormula(
-                        CompoundFormula.OR,
-                        Arrays.asList(
-                                new StringAtomicFormula(
-                                        AtomicFormula.PACKAGE_NAME,
-                                        PACKAGE_NAME_1,
-                                        /* isHashedValue= */ false),
-                                new StringAtomicFormula(
-                                        AtomicFormula.APP_CERTIFICATE,
-                                        APP_CERTIFICATE,
-                                        /* isHashedValue= */ false))),
-                Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(DENY);
-        assertThat(result.getMatchedRules()).containsExactly(rule);
-    }
-
-    @Test
-    public void testEvaluateRules_compoundFormulaWithNot_deny() {
-        CompoundFormula openSubFormula =
-                new CompoundFormula(
-                        CompoundFormula.AND,
-                        Arrays.asList(
-                                new StringAtomicFormula(
-                                        AtomicFormula.PACKAGE_NAME,
-                                        PACKAGE_NAME_2,
-                                        /* isHashedValue= */ false),
-                                new StringAtomicFormula(
-                                        AtomicFormula.APP_CERTIFICATE,
-                                        APP_CERTIFICATE,
-                                        /* isHashedValue= */ false)));
-        CompoundFormula compoundFormula =
-                new CompoundFormula(CompoundFormula.NOT, Collections.singletonList(openSubFormula));
-        Rule rule = new Rule(compoundFormula, Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(DENY);
-        assertThat(result.getMatchedRules()).containsExactly(rule);
-    }
-
-    @Test
-    public void testEvaluateRules_forceAllow() {
-        Rule rule1 =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                PACKAGE_NAME_1,
-                                /* isHashedValue= */ false),
-                        Rule.FORCE_ALLOW);
-        Rule rule2 = new Rule(
-                new CompoundFormula(
-                        CompoundFormula.AND,
-                        Arrays.asList(
-                                new StringAtomicFormula(
-                                        AtomicFormula.PACKAGE_NAME,
-                                        PACKAGE_NAME_1,
-                                        /* isHashedValue= */ false),
-                                new StringAtomicFormula(
-                                        AtomicFormula.APP_CERTIFICATE,
-                                        APP_CERTIFICATE,
-                                        /* isHashedValue= */ false))),
-                Rule.DENY);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(ALLOW);
-        assertThat(result.getMatchedRules()).containsExactly(rule1);
-    }
-
-    @Test
-    public void testEvaluateRules_multipleMatches_forceAllow() {
-        Rule rule1 =
-                new Rule(
-                        new StringAtomicFormula(
-                                AtomicFormula.PACKAGE_NAME,
-                                PACKAGE_NAME_1,
-                                /* isHashedValue= */ false),
-                        Rule.FORCE_ALLOW);
-        Rule rule2 = new Rule(
-                new CompoundFormula(
-                        CompoundFormula.AND,
-                        Arrays.asList(
-                                new StringAtomicFormula(
-                                        AtomicFormula.PACKAGE_NAME,
-                                        PACKAGE_NAME_1,
-                                        /* isHashedValue= */ false),
-                                new StringAtomicFormula(
-                                        AtomicFormula.APP_CERTIFICATE,
-                                        APP_CERTIFICATE,
-                                        /* isHashedValue= */ false))),
-                Rule.FORCE_ALLOW);
-
-        IntegrityCheckResult result =
-                RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
-
-        assertThat(result.getEffect()).isEqualTo(ALLOW);
-        assertThat(result.getMatchedRules()).containsExactly(rule1, rule2);
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
index 6c23ff6..d31ed68 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
@@ -22,8 +22,6 @@
 import android.content.integrity.CompoundFormula;
 import android.content.integrity.Rule;
 
-import com.android.internal.util.FrameworkStatsLog;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -40,8 +38,6 @@
 
         assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
         assertThat(allowResult.getMatchedRules()).isEmpty();
-        assertThat(allowResult.getLoggingResponse())
-                .isEqualTo(FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED);
     }
 
     @Test
@@ -58,9 +54,6 @@
 
         assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
         assertThat(allowResult.getMatchedRules()).containsExactly(forceAllowRule);
-        assertThat(allowResult.getLoggingResponse())
-                .isEqualTo(
-                        FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED);
     }
 
     @Test
@@ -77,8 +70,6 @@
 
         assertThat(denyResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.DENY);
         assertThat(denyResult.getMatchedRules()).containsExactly(failedRule);
-        assertThat(denyResult.getLoggingResponse())
-                .isEqualTo(FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 7e22d74..b1d658c 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -25,6 +25,7 @@
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK;
 import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN;
+import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
 import static android.view.ContentRecordingSession.TARGET_UID_FULL_SCREEN;
 import static android.view.ContentRecordingSession.TARGET_UID_UNKNOWN;
 import static android.view.ContentRecordingSession.createDisplaySession;
@@ -80,6 +81,7 @@
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
 import android.testing.TestableContext;
 import android.view.ContentRecordingSession;
 import android.view.ContentRecordingSession.RecordContent;
@@ -372,6 +374,50 @@
         });
     }
 
+    @EnableFlags(android.companion.virtualdevice.flags
+            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+    @Test
+    public void testCreateProjection_keyguardLocked_screenshareProtectionsDisabled()
+            throws NameNotFoundException {
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        int value = Settings.Global.getInt(mContext.getContentResolver(),
+                DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0);
+        try {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1);
+            doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+
+            doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+                    RECORD_SENSITIVE_CONTENT, projection.packageName);
+
+            projection.start(mIMediaProjectionCallback);
+            projection.notifyVirtualDisplayCreated(10);
+
+            // The projection was started because it was allowed to capture the keyguard.
+            assertThat(mService.getActiveProjectionInfo()).isNotNull();
+        } finally {
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, value);
+        }
+    }
+
+    @EnableFlags(android.companion.virtualdevice.flags
+            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+    @Test
+    public void testCreateProjection_keyguardLocked_noDisplayCreated()
+            throws NameNotFoundException {
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+
+        doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+                RECORD_SENSITIVE_CONTENT, projection.packageName);
+
+        projection.start(mIMediaProjectionCallback);
+
+        // The projection was started because it was allowed to capture the keyguard.
+        assertThat(mService.getActiveProjectionInfo()).isNotNull();
+    }
+
     @Test
     public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
             throws NameNotFoundException {
@@ -485,6 +531,7 @@
         MediaProjectionManagerService.MediaProjection projection =
                 startProjectionPreconditions(service);
         projection.start(mIMediaProjectionCallback);
+        projection.notifyVirtualDisplayCreated(10);
 
         assertThat(service.getActiveProjectionInfo()).isNotNull();
 
@@ -507,6 +554,7 @@
         MediaProjectionManagerService.MediaProjection projection =
                 startProjectionPreconditions(service);
         projection.start(mIMediaProjectionCallback);
+        projection.notifyVirtualDisplayCreated(10);
 
         assertThat(service.getActiveProjectionInfo()).isNotNull();
 
diff --git a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
index a545010..f45eddc 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
@@ -24,6 +24,7 @@
 
 import android.database.Cursor;
 import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.provider.CallLog.Calls;
 import android.test.mock.MockContentProvider;
@@ -58,6 +59,7 @@
     private MatrixCursor mCursor;
     private EventConsumer mEventConsumer;
     private CallLogQueryHelper mHelper;
+    private CallLogContentProvider mCallLogContentProvider;
 
     @Before
     public void setUp() {
@@ -66,7 +68,8 @@
         mCursor = new MatrixCursor(CALL_LOG_COLUMNS);
 
         MockContentResolver contentResolver = new MockContentResolver();
-        contentResolver.addProvider(CALL_LOG_AUTHORITY, new CallLogContentProvider());
+        mCallLogContentProvider = new CallLogContentProvider();
+        contentResolver.addProvider(CALL_LOG_AUTHORITY, mCallLogContentProvider);
         when(mContext.getContentResolver()).thenReturn(contentResolver);
 
         mEventConsumer = new EventConsumer();
@@ -80,6 +83,12 @@
     }
 
     @Test
+    public void testQueryWithSQLiteException() {
+        mCallLogContentProvider.setThrowSQLiteException(true);
+        assertFalse(mHelper.querySince(50L));
+    }
+
+    @Test
     public void testQueryIncomingCall() {
         mCursor.addRow(new Object[] {
                 NORMALIZED_PHONE_NUMBER, /* date= */ 100L, /* duration= */ 30L,
@@ -159,11 +168,20 @@
     }
 
     private class CallLogContentProvider extends MockContentProvider {
+        private boolean mThrowSQLiteException = false;
 
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                 String sortOrder) {
+            if (mThrowSQLiteException) {
+                throw new SQLiteException();
+            }
+
             return mCursor;
         }
+
+        public void setThrowSQLiteException(boolean throwException) {
+            this.mThrowSQLiteException = throwException;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
index 16a02b6..1daee39 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
@@ -99,6 +99,14 @@
     }
 
     @Test
+    public void testQueryOtherException_returnsFalse() {
+        contentProvider.setThrowOtherException(true);
+
+        Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
+        assertFalse(mHelper.query(contactUri.toString()));
+    }
+
+    @Test
     public void testQueryIllegalArgumentException_returnsFalse() {
         contentProvider.setThrowIllegalArgumentException(true);
 
@@ -152,6 +160,13 @@
     }
 
     @Test
+    public void testQueryWithPhoneNumber_otherExceptionReturnsFalse() {
+        contentProvider.setThrowOtherException(true);
+        String contactUri = "tel:" + PHONE_NUMBER;
+        assertFalse(mHelper.query(contactUri));
+    }
+
+    @Test
     public void testQueryWithEmail() {
         mContactsLookupCursor.addRow(new Object[] {
                 /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 0 });
@@ -188,6 +203,7 @@
         private Map<Uri, Cursor> mUriPrefixToCursorMap = new ArrayMap<>();
         private boolean mThrowSQLiteException = false;
         private boolean mThrowIllegalArgumentException = false;
+        private boolean mThrowOtherException = false;
 
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
@@ -198,6 +214,9 @@
             if (mThrowIllegalArgumentException) {
                 throw new IllegalArgumentException();
             }
+            if (mThrowOtherException) {
+                throw new ArrayIndexOutOfBoundsException();
+            }
 
             for (Uri prefixUri : mUriPrefixToCursorMap.keySet()) {
                 if (uri.isPathPrefixMatch(prefixUri)) {
@@ -215,6 +234,10 @@
             this.mThrowIllegalArgumentException = throwException;
         }
 
+        public void setThrowOtherException(boolean throwException) {
+            this.mThrowOtherException = throwException;
+        }
+
         private void registerCursor(Uri uriPrefix, Cursor cursor) {
             mUriPrefixToCursorMap.put(uriPrefix, cursor);
         }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
index 7730890..9f4a43df 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
@@ -23,6 +23,7 @@
 
 import android.database.Cursor;
 import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.provider.Telephony.BaseMmsColumns;
 import android.provider.Telephony.Mms;
@@ -63,6 +64,7 @@
     private final List<MatrixCursor> mAddrCursors = new ArrayList<>();
     private EventConsumer mEventConsumer;
     private MmsQueryHelper mHelper;
+    private MmsContentProvider mMmsContentProvider;
 
     @Before
     public void setUp() {
@@ -73,7 +75,8 @@
         mAddrCursors.add(new MatrixCursor(ADDR_COLUMNS));
 
         MockContentResolver contentResolver = new MockContentResolver();
-        contentResolver.addProvider(MMS_AUTHORITY, new MmsContentProvider());
+        mMmsContentProvider = new MmsContentProvider();
+        contentResolver.addProvider(MMS_AUTHORITY, mMmsContentProvider);
         when(mContext.getContentResolver()).thenReturn(contentResolver);
 
         mEventConsumer = new EventConsumer();
@@ -87,6 +90,12 @@
     }
 
     @Test
+    public void testQueryWithSQLiteException() {
+        mMmsContentProvider.setThrowSQLiteException(true);
+        assertFalse(mHelper.querySince(50_000L));
+    }
+
+    @Test
     public void testQueryIncomingMessage() {
         mMmsCursor.addRow(new Object[] {
                 /* id= */ 0, /* date= */ 100L, /* msgBox= */ BaseMmsColumns.MESSAGE_BOX_INBOX });
@@ -159,10 +168,15 @@
     }
 
     private class MmsContentProvider extends MockContentProvider {
+        private boolean mThrowSQLiteException = false;
 
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                 String sortOrder) {
+            if (mThrowSQLiteException) {
+                throw new SQLiteException();
+            }
+
             List<String> segments = uri.getPathSegments();
             if (segments.size() == 2 && "addr".equals(segments.get(1))) {
                 int messageId = Integer.valueOf(segments.get(0));
@@ -170,5 +184,9 @@
             }
             return mMmsCursor;
         }
+
+        public void setThrowSQLiteException(boolean throwException) {
+            this.mThrowSQLiteException = throwException;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
index 5cb8cb4..09a0dff 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
@@ -23,6 +23,7 @@
 
 import android.database.Cursor;
 import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.provider.Telephony.Sms;
 import android.provider.Telephony.TextBasedSmsColumns;
@@ -59,6 +60,7 @@
     private MatrixCursor mSmsCursor;
     private EventConsumer mEventConsumer;
     private SmsQueryHelper mHelper;
+    private SmsContentProvider mSmsContentProvider;
 
     @Before
     public void setUp() {
@@ -67,7 +69,8 @@
         mSmsCursor = new MatrixCursor(SMS_COLUMNS);
 
         MockContentResolver contentResolver = new MockContentResolver();
-        contentResolver.addProvider(SMS_AUTHORITY, new SmsContentProvider());
+        mSmsContentProvider = new SmsContentProvider();
+        contentResolver.addProvider(SMS_AUTHORITY, mSmsContentProvider);
         when(mContext.getContentResolver()).thenReturn(contentResolver);
 
         mEventConsumer = new EventConsumer();
@@ -130,6 +133,12 @@
         assertEquals(110L, events.get(1).getTimestamp());
     }
 
+    @Test
+    public void testQueryWithSQLiteException() {
+        mSmsContentProvider.setThrowSQLiteException(true);
+        assertFalse(mHelper.querySince(50L));
+    }
+
     private class EventConsumer implements BiConsumer<String, Event> {
 
         private final Map<String, List<Event>> mEventMap = new ArrayMap<>();
@@ -141,11 +150,19 @@
     }
 
     private class SmsContentProvider extends MockContentProvider {
+        private boolean mThrowSQLiteException = false;
 
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                 String sortOrder) {
+            if (mThrowSQLiteException) {
+                throw new SQLiteException();
+            }
             return mSmsCursor;
         }
+
+        public void setThrowSQLiteException(boolean throwException) {
+            this.mThrowSQLiteException = throwException;
+        }
     }
 }
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 e652df5..f994660 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -539,6 +539,79 @@
 
     @MediumTest
     @Test
+    public void testRemoveUser_shouldRemovePrivateUser() {
+        UserInfo privateProfileUser =
+                createProfileForUser(
+                        "Private profile",
+                        UserManager.USER_TYPE_PROFILE_PRIVATE,
+                        mUserManager.getMainUser().getIdentifier());
+        assertThat(privateProfileUser).isNotNull();
+        assertThat(hasUser(privateProfileUser.id)).isTrue();
+
+        removeUser(privateProfileUser.id);
+
+        assertThat(hasUser(privateProfileUser.id)).isFalse();
+    }
+
+    @MediumTest
+    @Test
+    @RequiresFlagsEnabled(
+            android.multiuser.Flags.FLAG_IGNORE_RESTRICTIONS_WHEN_DELETING_PRIVATE_PROFILE)
+    public void testRemoveUser_shouldRemovePrivateUser_withDisallowRemoveUserRestriction() {
+        UserHandle mainUser = mUserManager.getMainUser();
+        mUserManager.setUserRestriction(
+                UserManager.DISALLOW_REMOVE_USER, /* value= */ true, mainUser);
+        try {
+            UserInfo privateProfileUser =
+                    createProfileForUser(
+                            "Private profile",
+                            UserManager.USER_TYPE_PROFILE_PRIVATE,
+                            mainUser.getIdentifier());
+            assertThat(privateProfileUser).isNotNull();
+            assertThat(hasUser(privateProfileUser.id)).isTrue();
+            removeUser(privateProfileUser.id);
+
+            assertThat(hasUser(privateProfileUser.id)).isFalse();
+        } finally {
+            mUserManager.setUserRestriction(
+                    UserManager.DISALLOW_REMOVE_USER, /* value= */ false, mainUser);
+        }
+    }
+
+    @MediumTest
+    @Test
+    public void testRemoveUser_withDisallowRemoveUserRestrictionAndMultipleUsersPresent() {
+        UserInfo privateProfileUser =
+                createProfileForUser(
+                        "Private profile",
+                        UserManager.USER_TYPE_PROFILE_PRIVATE,
+                        mUserManager.getMainUser().getIdentifier());
+        assertThat(privateProfileUser).isNotNull();
+        assertThat(hasUser(privateProfileUser.id)).isTrue();
+        UserInfo testUser = createUser("TestUser", /* flags= */ 0);
+        assertThat(testUser).isNotNull();
+        assertThat(hasUser(testUser.id)).isTrue();
+        UserHandle mainUser = mUserManager.getMainUser();
+        mUserManager.setUserRestriction(
+                UserManager.DISALLOW_REMOVE_USER, /* value= */ true, mainUser);
+        try {
+            assertThat(
+                    mUserManager.removeUserWhenPossible(
+                            testUser.getUserHandle(), /* overrideDevicePolicy= */ false))
+                    .isEqualTo(UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION);
+
+            // Non private profile users should be prevented from being removed.
+            assertThat(mUserManager.removeUser(testUser.id)).isEqualTo(false);
+
+            assertThat(hasUser(testUser.id)).isTrue();
+        } finally {
+            mUserManager.setUserRestriction(
+                    UserManager.DISALLOW_REMOVE_USER, /* value= */ false, mainUser);
+        }
+    }
+
+    @MediumTest
+    @Test
     public void testRemoveUserShouldNotRemoveTargetUser_DuringUserSwitch() {
         final int startUser = ActivityManager.getCurrentUser();
         final UserInfo testUser = createUser("TestUser", /* flags= */ 0);
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
new file mode 100644
index 0000000..727b435
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.advancedprotection;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.test.FakePermissionEnforcer;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AdvancedProtectionServiceTest {
+    private AdvancedProtectionService mService;
+    private FakePermissionEnforcer mPermissionEnforcer;
+    private Context mContext;
+
+    @Before
+    @SuppressLint("VisibleForTests")
+    public void setup() throws Settings.SettingNotFoundException {
+        mContext = mock(Context.class);
+        mPermissionEnforcer = new FakePermissionEnforcer();
+        mPermissionEnforcer.grant(Manifest.permission.SET_ADVANCED_PROTECTION_MODE);
+        mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
+
+        AdvancedProtectionService.AdvancedProtectionStore store =
+                new AdvancedProtectionService.AdvancedProtectionStore(mContext) {
+                    private boolean mEnabled = false;
+
+                    @Override
+                    boolean retrieve() {
+                        return mEnabled;
+                    }
+
+                    @Override
+                    void store(boolean enabled) {
+                        this.mEnabled = enabled;
+                    }
+                };
+
+        mService = new AdvancedProtectionService(mContext, store, new TestLooper().getLooper(),
+                mPermissionEnforcer);
+    }
+
+    @Test
+    public void testEnableProtection() throws RemoteException {
+        mService.setAdvancedProtectionEnabled(true);
+        assertTrue(mService.isAdvancedProtectionEnabled());
+    }
+
+    @Test
+    public void testDisableProtection() throws RemoteException {
+        mService.setAdvancedProtectionEnabled(false);
+        assertFalse(mService.isAdvancedProtectionEnabled());
+    }
+
+    @Test
+    public void testSetProtection_withoutPermission() {
+        mPermissionEnforcer.revoke(Manifest.permission.SET_ADVANCED_PROTECTION_MODE);
+        assertThrows(SecurityException.class, () -> mService.setAdvancedProtectionEnabled(true));
+    }
+
+    @Test
+    public void testGetProtection_withoutPermission() {
+        mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
+        assertThrows(SecurityException.class, () -> mService.isAdvancedProtectionEnabled());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/stats/pull/netstats/NetworkStatsAccumulatorTest.kt b/services/tests/servicestests/src/com/android/server/stats/pull/netstats/NetworkStatsAccumulatorTest.kt
index 7280c69..8cf0e82 100644
--- a/services/tests/servicestests/src/com/android/server/stats/pull/netstats/NetworkStatsAccumulatorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/stats/pull/netstats/NetworkStatsAccumulatorTest.kt
@@ -60,7 +60,7 @@
 
         // Accumulator has data until 1000 (= 0), and its end-point is still in the history period.
         // Current time is less than one bucket away from snapshot end-point: 1050 - 1000 < 200
-        val stats = snapshot.queryStats(1050, FakeStats(500, 1050, 1))!!
+        val stats = snapshot.queryStats(1050, FakeStats(500, 1050, 1))
 
         // After the query at 1050, accumulator should have 1 * (1050 - 1000) = 50 bytes.
         assertNetworkStatsEquals(stats, networkStatsWithBytes(50))
@@ -72,7 +72,7 @@
 
         // Accumulator has data until 1000 (= 0), and its end-point is still in the history period.
         // Current time is one bucket away from snapshot end-point: 1250 - 1000 > 200
-        val stats = snapshot.queryStats(1250, FakeStats(550, 1250, 2))!!
+        val stats = snapshot.queryStats(1250, FakeStats(550, 1250, 2))
 
         // After the query at 1250, accumulator should have 2 * (1250 - 1000) = 500 bytes.
         assertNetworkStatsEquals(stats, networkStatsWithBytes(500))
@@ -84,7 +84,7 @@
 
         // Accumulator has data until 1000 (= 0), and its end-point is in the history period.
         // Current time is two buckets away from snapshot end-point: 1450 - 1000 > 2*200
-        val stats = snapshot.queryStats(1450, FakeStats(600, 1450, 3))!!
+        val stats = snapshot.queryStats(1450, FakeStats(600, 1450, 3))
 
         // After the query at 1450, accumulator should have 3 * (1450 - 1000) = 1350 bytes.
         assertNetworkStatsEquals(stats, networkStatsWithBytes(1350))
@@ -96,7 +96,7 @@
 
         // Accumulator has data until 1000 (= 0), and its end-point is still in the history period.
         // Current time is many buckets away from snapshot end-point
-        val stats = snapshot.queryStats(6100, FakeStats(900, 6100, 1))!!
+        val stats = snapshot.queryStats(6100, FakeStats(900, 6100, 1))
 
         // After the query at 6100, accumulator should have 1 * (6100 - 1000) = 5100 bytes.
         assertNetworkStatsEquals(stats, networkStatsWithBytes(5100))
@@ -108,9 +108,9 @@
 
         // Accumulator is queried within the history period, whose starting point stays the same.
         // After each query, accumulator should contain bytes from the initial end-point until now.
-        val stats1 = snapshot.queryStats(5100, FakeStats(900, 5100, 1))!!
-        val stats2 = snapshot.queryStats(10100, FakeStats(900, 10100, 1))!!
-        val stats3 = snapshot.queryStats(15100, FakeStats(900, 15100, 1))!!
+        val stats1 = snapshot.queryStats(5100, FakeStats(900, 5100, 1))
+        val stats2 = snapshot.queryStats(10100, FakeStats(900, 10100, 1))
+        val stats3 = snapshot.queryStats(15100, FakeStats(900, 15100, 1))
 
         assertNetworkStatsEquals(stats1, networkStatsWithBytes(4100))
         assertNetworkStatsEquals(stats2, networkStatsWithBytes(9100))
@@ -123,9 +123,9 @@
 
         // Accumulator is queried within the history period, whose starting point is moving.
         // After each query, accumulator should contain bytes from the initial end-point until now.
-        val stats1 = snapshot.queryStats(5100, FakeStats(900, 5100, 1))!!
-        val stats2 = snapshot.queryStats(10100, FakeStats(4000, 10100, 1))!!
-        val stats3 = snapshot.queryStats(15100, FakeStats(7000, 15100, 1))!!
+        val stats1 = snapshot.queryStats(5100, FakeStats(900, 5100, 1))
+        val stats2 = snapshot.queryStats(10100, FakeStats(4000, 10100, 1))
+        val stats3 = snapshot.queryStats(15100, FakeStats(7000, 15100, 1))
 
         assertNetworkStatsEquals(stats1, networkStatsWithBytes(4100))
         assertNetworkStatsEquals(stats2, networkStatsWithBytes(9100))
@@ -138,7 +138,7 @@
 
         // Accumulator has data until 1000 (= 0), but its end-point is not in the history period.
         // After the query, accumulator should add only those bytes that are covered by the history.
-        val stats = snapshot.queryStats(2700, FakeStats(2200, 2700, 1))!!
+        val stats = snapshot.queryStats(2700, FakeStats(2200, 2700, 1))
 
         assertNetworkStatsEquals(stats, networkStatsWithBytes(500))
     }
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt
new file mode 100644
index 0000000..22d894a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.systemconfig
+
+import android.content.Context
+import android.content.pm.FeatureInfo
+import android.util.ArrayMap
+import android.util.Xml
+
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.SystemConfig
+import com.google.common.truth.Truth.assertThat
+import org.junit.runner.RunWith
+import org.junit.Rule
+
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class SystemConfigReadOnlyFeaturesTest {
+
+    companion object {
+        private const val FEATURE_ONE = "feature.test.1"
+        private const val FEATURE_TWO = "feature.test.2"
+        private const val FEATURE_RUNTIME_AVAILABLE_TEMPLATE =
+        """
+            <permissions>
+                <feature name="%s" />
+            </permissions>
+        """
+        private const val FEATURE_RUNTIME_DISABLED_TEMPLATE =
+        """
+            <permissions>
+                <Disabled-feature name="%s" />
+            </permissions>
+        """
+
+        fun featureInfo(featureName: String) = FeatureInfo().apply { name = featureName }
+    }
+
+    private val context: Context = InstrumentationRegistry.getInstrumentation().context
+
+    @get:Rule
+    val tempFolder = TemporaryFolder(context.filesDir)
+
+    private val injector = TestInjector()
+
+    private var uniqueCounter = 0
+
+    @Test
+    fun empty() {
+        assertFeatures().isEmpty()
+    }
+
+    @Test
+    fun readOnlyEnabled() {
+        addReadOnlyEnabledFeature(FEATURE_ONE)
+        addReadOnlyEnabledFeature(FEATURE_TWO)
+
+        assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO)
+    }
+
+    @Test
+    fun readOnlyAndRuntimeEnabled() {
+        addReadOnlyEnabledFeature(FEATURE_ONE)
+        addRuntimeEnabledFeature(FEATURE_TWO)
+
+        // No issues with matching availability.
+        assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO)
+    }
+
+    @Test
+    fun readOnlyEnabledRuntimeDisabled() {
+        addReadOnlyEnabledFeature(FEATURE_ONE)
+        addRuntimeDisabledFeature(FEATURE_ONE)
+
+        // Read-only feature availability should take precedence.
+        assertFeatures().contains(FEATURE_ONE)
+    }
+
+    @Test
+    fun readOnlyDisabled() {
+        addReadOnlyDisabledFeature(FEATURE_ONE)
+
+        assertFeatures().doesNotContain(FEATURE_ONE)
+    }
+
+    @Test
+    fun readOnlyAndRuntimeDisabled() {
+        addReadOnlyDisabledFeature(FEATURE_ONE)
+        addRuntimeDisabledFeature(FEATURE_ONE)
+
+        // No issues with matching (un)availability.
+        assertFeatures().doesNotContain(FEATURE_ONE)
+    }
+
+    @Test
+    fun readOnlyDisabledRuntimeEnabled() {
+        addReadOnlyDisabledFeature(FEATURE_ONE)
+        addRuntimeEnabledFeature(FEATURE_ONE)
+        addRuntimeEnabledFeature(FEATURE_TWO)
+
+        // Read-only feature (un)availability should take precedence.
+        assertFeatures().doesNotContain(FEATURE_ONE)
+        assertFeatures().contains(FEATURE_TWO)
+    }
+
+    fun addReadOnlyEnabledFeature(featureName: String) {
+        injector.readOnlyEnabledFeatures[featureName] = featureInfo(featureName)
+    }
+
+    fun addReadOnlyDisabledFeature(featureName: String) {
+        injector.readOnlyDisabledFeatures.add(featureName)
+    }
+
+    fun addRuntimeEnabledFeature(featureName: String) {
+        FEATURE_RUNTIME_AVAILABLE_TEMPLATE.format(featureName).write()
+    }
+
+    fun addRuntimeDisabledFeature(featureName: String) {
+        FEATURE_RUNTIME_DISABLED_TEMPLATE.format(featureName).write()
+    }
+
+    private fun String.write() = tempFolder.root.resolve("${uniqueCounter++}.xml")
+            .writeText(this.trimIndent())
+
+    private fun assertFeatures() = assertThat(availableFeatures().keys)
+
+    private fun availableFeatures() = SystemConfig(false, injector).apply {
+        val parser = Xml.newPullParser()
+        readPermissions(parser, tempFolder.root, /*Grant all permission flags*/ 0.inv())
+    }.let { it.availableFeatures }
+
+    internal class TestInjector() : SystemConfig.Injector() {
+        val readOnlyEnabledFeatures = ArrayMap<String, FeatureInfo>()
+        val readOnlyDisabledFeatures = mutableSetOf<String>()
+
+        override fun isReadOnlySystemEnabledFeature(featureName: String, version: Int): Boolean {
+            return readOnlyEnabledFeatures.containsKey(featureName)
+        }
+
+        override fun isReadOnlySystemDisabledFeature(featureName: String, version: Int): Boolean {
+            return readOnlyDisabledFeatures.contains(featureName)
+        }
+
+        override fun getReadOnlySystemEnabledFeatures(): ArrayMap<String, FeatureInfo> {
+            return readOnlyEnabledFeatures
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index bf58443..a222ef0 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -175,8 +175,7 @@
                 tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
-        Map<Integer, FrontendResource> resources =
-                mTunerResourceManagerService.getFrontendResources();
+        Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
         for (int id = 0; id < infos.length; id++) {
             assertThat(resources.get(infos[id].handle)
                     .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
@@ -203,15 +202,14 @@
                 tunerFrontendInfo(3 /*handle*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
-        Map<Integer, FrontendResource> resources =
-                mTunerResourceManagerService.getFrontendResources();
+        Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
         assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
                 .containsExactlyElementsIn(Arrays.asList(infos));
 
-        assertThat(resources.get(0).getExclusiveGroupMemberFeHandles()).isEmpty();
-        assertThat(resources.get(1).getExclusiveGroupMemberFeHandles()).containsExactly(2, 3);
-        assertThat(resources.get(2).getExclusiveGroupMemberFeHandles()).containsExactly(1, 3);
-        assertThat(resources.get(3).getExclusiveGroupMemberFeHandles()).containsExactly(1, 2);
+        assertThat(resources.get(0L).getExclusiveGroupMemberFeHandles()).isEmpty();
+        assertThat(resources.get(1L).getExclusiveGroupMemberFeHandles()).containsExactly(2L, 3L);
+        assertThat(resources.get(2L).getExclusiveGroupMemberFeHandles()).containsExactly(1L, 3L);
+        assertThat(resources.get(3L).getExclusiveGroupMemberFeHandles()).containsExactly(1L, 2L);
     }
 
     @Test
@@ -224,11 +222,11 @@
                 tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
 
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
-        Map<Integer, FrontendResource> resources0 =
+        Map<Long, FrontendResource> resources0 =
                 mTunerResourceManagerService.getFrontendResources();
 
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
-        Map<Integer, FrontendResource> resources1 =
+        Map<Long, FrontendResource> resources1 =
                 mTunerResourceManagerService.getFrontendResources();
 
         assertThat(resources0).isEqualTo(resources1);
@@ -251,8 +249,7 @@
                 tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
 
-        Map<Integer, FrontendResource> resources =
-                mTunerResourceManagerService.getFrontendResources();
+        Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
         for (int id = 0; id < infos1.length; id++) {
             assertThat(resources.get(infos1[id].handle)
                     .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
@@ -278,8 +275,7 @@
                 tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
 
-        Map<Integer, FrontendResource> resources =
-                mTunerResourceManagerService.getFrontendResources();
+        Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
         for (int id = 0; id < infos1.length; id++) {
             assertThat(resources.get(infos1[id].handle)
                     .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
@@ -296,7 +292,7 @@
         mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
         TunerFrontendRequest request =
                 tunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isFalse();
         assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
@@ -317,7 +313,7 @@
 
         TunerFrontendRequest request =
                 tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isFalse();
         assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
@@ -349,7 +345,7 @@
 
         TunerFrontendRequest request =
                 tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(0);
@@ -382,7 +378,7 @@
                 1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         TunerFrontendRequest request =
                 tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         assertThat(mTunerResourceManagerService
@@ -423,7 +419,7 @@
 
         TunerFrontendRequest request =
                 tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
 
@@ -463,12 +459,12 @@
 
         TunerFrontendRequest request =
                 tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
         assertThat(client0.getProfile().getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(infos[0].handle, infos[1].handle)));
+                .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
 
         request =
                 tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS);
@@ -505,7 +501,7 @@
 
         TunerFrontendRequest request =
                 tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
@@ -536,7 +532,7 @@
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
         CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
-        int[] casSessionHandle = new int[1];
+        long[] casSessionHandle = new long[1];
         // Request for 2 cas sessions.
         assertThat(mTunerResourceManagerService
                 .requestCasSessionInternal(request, casSessionHandle)).isTrue();
@@ -583,7 +579,7 @@
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
         TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
-        int[] ciCamHandle = new int[1];
+        long[] ciCamHandle = new long[1];
         // Request for 2 ciCam sessions.
         assertThat(mTunerResourceManagerService
                 .requestCiCamInternal(request, ciCamHandle)).isTrue();
@@ -626,7 +622,7 @@
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
         CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
-        int[] casSessionHandle = new int[1];
+        long[] casSessionHandle = new long[1];
         // Request for 1 cas sessions.
         assertThat(mTunerResourceManagerService
                 .requestCasSessionInternal(request, casSessionHandle)).isTrue();
@@ -660,7 +656,7 @@
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
         TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
-        int[] ciCamHandle = new int[1];
+        long[] ciCamHandle = new long[1];
         // Request for 1 ciCam sessions.
         assertThat(mTunerResourceManagerService
                 .requestCiCamInternal(request, ciCamHandle)).isTrue();
@@ -696,17 +692,17 @@
                         TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
 
         // Init lnb resources.
-        int[] lnbHandles = {1};
+        long[] lnbHandles = {1};
         mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         TunerLnbRequest request = new TunerLnbRequest();
         request.clientId = client0.getId();
-        int[] lnbHandle = new int[1];
+        long[] lnbHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
         assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
         assertThat(client0.getProfile().getInUseLnbHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0])));
+                .isEqualTo(new HashSet<Long>(Arrays.asList(lnbHandles[0])));
 
         request = new TunerLnbRequest();
         request.clientId = client1.getId();
@@ -732,12 +728,12 @@
                         TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init lnb resources.
-        int[] lnbHandles = {0};
+        long[] lnbHandles = {0};
         mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         TunerLnbRequest request = new TunerLnbRequest();
         request.clientId = client0.getId();
-        int[] lnbHandle = new int[1];
+        long[] lnbHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
         assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
@@ -768,7 +764,7 @@
 
         TunerFrontendRequest request =
                 tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
@@ -799,12 +795,12 @@
         infos[2] = tunerDemuxInfo(2 /* handle */, Filter.TYPE_TS);
         mTunerResourceManagerService.setDemuxInfoListInternal(infos);
 
-        int[] demuxHandle0 = new int[1];
+        long[] demuxHandle0 = new long[1];
         // first with undefined type (should be the first one with least # of caps)
         TunerDemuxRequest request = tunerDemuxRequest(client0.getId(), Filter.TYPE_UNDEFINED);
         assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
                 .isTrue();
-        assertThat(demuxHandle0[0]).isEqualTo(1);
+        assertThat(demuxHandle0[0]).isEqualTo(1L);
         DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
         mTunerResourceManagerService.releaseDemuxInternal(dr);
 
@@ -813,20 +809,20 @@
         demuxHandle0[0] = -1;
         assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
                 .isFalse();
-        assertThat(demuxHandle0[0]).isEqualTo(-1);
+        assertThat(demuxHandle0[0]).isEqualTo(-1L);
 
         // now with TS (should be the one with least # of caps that supports TS)
         request.desiredFilterTypes = Filter.TYPE_TS;
         assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
                 .isTrue();
-        assertThat(demuxHandle0[0]).isEqualTo(2);
+        assertThat(demuxHandle0[0]).isEqualTo(2L);
 
         // request for another TS
         TunerClient client1 = new TunerClient();
         client1.register("1" /*sessionId*/,
                         TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
-        int[] demuxHandle1 = new int[1];
+        long[] demuxHandle1 = new long[1];
         TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_TS);
         assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1))
                 .isTrue();
@@ -865,14 +861,14 @@
 
         // let client0(prio:100) request for IP - should succeed
         TunerDemuxRequest request0 = tunerDemuxRequest(client0.getId(), Filter.TYPE_IP);
-        int[] demuxHandle0 = new int[1];
+        long[] demuxHandle0 = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request0, demuxHandle0)).isTrue();
         assertThat(demuxHandle0[0]).isEqualTo(0);
 
         // let client1(prio:50) request for IP - should fail
         TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_IP);
-        int[] demuxHandle1 = new int[1];
+        long[] demuxHandle1 = new long[1];
         demuxHandle1[0] = -1;
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request1, demuxHandle1)).isFalse();
@@ -892,7 +888,7 @@
 
         // let client2(prio:50) request for TS - should succeed
         TunerDemuxRequest request2 = tunerDemuxRequest(client2.getId(), Filter.TYPE_TS);
-        int[] demuxHandle2 = new int[1];
+        long[] demuxHandle2 = new long[1];
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request2, demuxHandle2)).isTrue();
         assertThat(demuxHandle2[0]).isEqualTo(0);
@@ -917,7 +913,7 @@
         client0.register("0" /*sessionId*/,
                         TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
-        int[] desHandle = new int[1];
+        long[] desHandle = new long[1];
         TunerDescramblerRequest request = new TunerDescramblerRequest();
         request.clientId = client0.getId();
         assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
@@ -980,7 +976,7 @@
                 1 /*exclusiveGroupId*/);
 
         /**** Init Lnb Resources ****/
-        int[] lnbHandles = {1};
+        long[] lnbHandles = {1};
         mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         // Update frontend list in TRM
@@ -989,7 +985,7 @@
         /**** Request Frontend ****/
 
         // Predefined frontend request and array to save returned frontend handle
-        int[] frontendHandle = new int[1];
+        long[] frontendHandle = new long[1];
         TunerFrontendRequest request = tunerFrontendRequest(
                 ownerClient0.getId() /*clientId*/,
                 FrontendSettings.TYPE_DVBT);
@@ -1000,7 +996,7 @@
                 .isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
         assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
 
@@ -1030,15 +1026,15 @@
                         shareClient1.getId())));
         // Verify in use frontend list in all the primary owner and share owner clients
         assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
         assertThat(shareClient0.getProfile().getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
         assertThat(shareClient1.getProfile().getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
 
@@ -1052,12 +1048,12 @@
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         shareClient0.getId())));
         assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
         assertThat(shareClient0.getProfile()
                 .getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
 
@@ -1080,7 +1076,7 @@
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
                 .getOwnerClientId()).isEqualTo(ownerClient1.getId());
         assertThat(ownerClient1.getProfile().getInUseFrontendHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
         assertThat(ownerClient0.getProfile().getInUseFrontendHandles()
@@ -1127,7 +1123,7 @@
         // Predefined Lnb request and handle array
         TunerLnbRequest requestLnb = new TunerLnbRequest();
         requestLnb.clientId = shareClient0.getId();
-        int[] lnbHandle = new int[1];
+        long[] lnbHandle = new long[1];
 
         // Request for an Lnb
         assertThat(mTunerResourceManagerService
@@ -1155,7 +1151,7 @@
                 .isEmpty())
                 .isTrue();
         assertThat(shareClient0.getProfile().getInUseLnbHandles())
-                .isEqualTo(new HashSet<Integer>(Arrays.asList(
+                .isEqualTo(new HashSet<Long>(Arrays.asList(
                         lnbHandles[0])));
 
         ownerClient0.unregister();
@@ -1163,7 +1159,7 @@
     }
 
     private TunerFrontendInfo tunerFrontendInfo(
-            int handle, int frontendType, int exclusiveGroupId) {
+            long handle, int frontendType, int exclusiveGroupId) {
         TunerFrontendInfo info = new TunerFrontendInfo();
         info.handle = handle;
         info.type = frontendType;
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 89056cc..9a7abd4 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -93,7 +93,10 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -111,6 +114,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -185,6 +189,9 @@
 
     private static final Random sRandom = new Random();
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private MyInjector mInjector;
     private AppStandbyController mController;
 
@@ -894,7 +901,8 @@
     }
 
     @Test
-    public void testScreenTimeAndBuckets() throws Exception {
+    @DisableFlags(Flags.FLAG_SCREEN_TIME_BYPASS)
+    public void testScreenTimeAndBuckets_Legacy() throws Exception {
         mInjector.setDisplayOn(false);
 
         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
@@ -917,6 +925,31 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_SCREEN_TIME_BYPASS)
+    public void testScreenTimeAndBuckets_Bypass() throws Exception {
+        mInjector.setDisplayOn(false);
+
+        assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
+
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+
+        // ACTIVE bucket
+        assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
+
+        // WORKING_SET bucket
+        assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
+
+        // RARE bucket, should failed due to timeout hasn't reached yet.
+        mInjector.mElapsedRealtime = RARE_THRESHOLD - 1;
+        mController.checkIdleStates(USER_ID);
+        waitAndAssertNotBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
+
+        mInjector.setDisplayOn(true);
+        // screen on time doesn't count.
+        assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
+    }
+
+    @Test
     public void testForcedIdle() throws Exception {
         mController.forceIdleState(PACKAGE_1, USER_ID, true);
         waitAndAssertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index c247c08..3b0cb4a 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -68,6 +68,7 @@
 
 import android.Manifest;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.Flags;
 import android.app.IOnProjectionStateChangedListener;
@@ -247,6 +248,8 @@
         mInjector = spy(new TestInjector());
         mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
                 mTwilightManager, mInjector);
+        // Initialize the current user.
+        mUiManagerService.setCurrentUser(ActivityManager.getCurrentUser());
         try {
             mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         } catch (SecurityException e) {/* ignore for permission denial */}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
index 4af96ef..05210ac 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
@@ -19,13 +19,25 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.app.Application;
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -35,16 +47,24 @@
 import com.android.server.pm.PackageManagerService;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.List;
 
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 @RunWithLooper
 public class EventConditionProviderTest extends UiServiceTestCase {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
-    EventConditionProvider mService;
+    private EventConditionProvider mService;
+    @Mock private UserManager mUserManager;
 
     @Before
     public void setUp() throws Exception {
@@ -65,6 +85,18 @@
         service.onCreate();
         service.onBind(startIntent);
         mService = spy(service);
+        mService.mContext = this.getContext();
+
+        mContext.addMockSystemService(UserManager.class, mUserManager);
+        when(mUserManager.getProfiles(eq(mUserId))).thenReturn(
+                List.of(new UserInfo(mUserId, "mUserId", 0)));
+
+        doAnswer((Answer<Context>) invocationOnMock -> {
+            Context mockUserContext = mock(Context.class);
+            UserHandle userHandle = invocationOnMock.getArgument(2);
+            when(mockUserContext.getUserId()).thenReturn(userHandle.getIdentifier());
+            return mockUserContext;
+        }).when(mContext).createPackageContextAsUser(any(), anyInt(), any());
     }
 
     @Test
@@ -78,4 +110,52 @@
         PendingIntent pi = mService.getPendingIntent(1000);
         assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage());
     }
+
+    @Test
+    @DisableFlags(android.app.Flags.FLAG_MODES_HSUM)
+    public void onBootComplete_flagOff_loadsTrackers() {
+        when(mUserManager.getUserProfiles()).thenReturn(List.of(UserHandle.of(mUserId)));
+
+        mService.onBootComplete();
+
+        assertThat(mService.getTrackers().size()).isEqualTo(1);
+        assertThat(mService.getTrackers().keyAt(0)).isEqualTo(mUserId);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_HSUM)
+    public void onBootComplete_waitsForUserSwitched() {
+        mService.onBootComplete();
+        assertThat(mService.getTrackers().size()).isEqualTo(0);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_HSUM)
+    public void onUserSwitched_reloadsTrackers() {
+        UserHandle someUser = UserHandle.of(42);
+        when(mUserManager.getProfiles(eq(42))).thenReturn(List.of(new UserInfo(42, "user 42", 0)));
+
+        mService.onUserSwitched(someUser);
+
+        assertThat(mService.getTrackers().size()).isEqualTo(1);
+        assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42);
+        assertThat(mService.getTrackers().valueAt(0).getUserId()).isEqualTo(42);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_HSUM)
+    public void onUserSwitched_reloadsTrackersIncludingProfiles() {
+        UserHandle anotherUser = UserHandle.of(42);
+        when(mUserManager.getProfiles(eq(42))).thenReturn(List.of(
+                new UserInfo(42, "user 42", 0),
+                new UserInfo(43, "profile 43", 0)));
+
+        mService.onUserSwitched(anotherUser);
+
+        assertThat(mService.getTrackers().size()).isEqualTo(2);
+        assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42);
+        assertThat(mService.getTrackers().valueAt(0).getUserId()).isEqualTo(42);
+        assertThat(mService.getTrackers().keyAt(1)).isEqualTo(43);
+        assertThat(mService.getTrackers().valueAt(1).getUserId()).isEqualTo(43);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 3bbc6b2..b5724b5c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -21,8 +21,10 @@
 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
 
+import static com.android.server.notification.Flags.FLAG_NOTIFICATION_NLS_REBIND;
 import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT;
 import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE;
 import static com.android.server.notification.NotificationManagerService.privateSpaceFlagsEnabled;
@@ -68,6 +70,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.testing.TestableLooper;
 import android.text.TextUtils;
@@ -86,6 +89,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -108,6 +112,8 @@
 
 
 public class ManagedServicesTest extends UiServiceTestCase {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
     @Mock
     private IPackageManager mIpm;
@@ -1064,6 +1070,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
     public void registerService_bindingDied_rebindIsClearedOnUserSwitch() throws Exception {
         Context context = mock(Context.class);
         PackageManager pm = mock(PackageManager.class);
@@ -1293,6 +1300,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
     public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
         Context context = spy(getContext());
         doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
@@ -1439,6 +1447,90 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
+    public void testSetPackageOrComponentEnabled_pkgInstalledAfterEnabling() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+
+        final int userId = 0;
+        final String validComponent = "again/M4";
+        ArrayList<String> expectedEnabled = Lists.newArrayList("package/Comp", "package/C2",
+                validComponent);
+
+        PackageManager pm = mock(PackageManager.class);
+        when(getContext().getPackageManager()).thenReturn(pm);
+        service = spy(service);
+
+        // Component again/M4 is a valid service and the package is available
+        doReturn(true).when(service)
+                .isValidService(ComponentName.unflattenFromString(validComponent), userId);
+        when(pm.isPackageAvailable("again")).thenReturn(true);
+
+        // "package" is not available and its services are not valid
+        doReturn(false).when(service)
+                .isValidService(ComponentName.unflattenFromString("package/Comp"), userId);
+        doReturn(false).when(service)
+                .isValidService(ComponentName.unflattenFromString("package/C2"), userId);
+        when(pm.isPackageAvailable("package")).thenReturn(false);
+
+        // Enable all components
+        for (String component: expectedEnabled) {
+            service.setPackageOrComponentEnabled(component, userId, true, true);
+        }
+
+        // Verify everything added is approved
+        for (String component: expectedEnabled) {
+            assertTrue("Not allowed: user: " + userId + " entry: " + component
+                    + " for approval level " + APPROVAL_BY_COMPONENT,
+                    service.isPackageOrComponentAllowed(component, userId));
+        }
+
+        // Add missing package "package"
+        service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+        // Check that component of "package" are not enabled
+        assertFalse(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString("package/Comp")));
+        assertFalse(service.isPackageOrComponentAllowed("package/Comp", userId));
+
+        assertFalse(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString("package/C2")));
+        assertFalse(service.isPackageOrComponentAllowed("package/C2", userId));
+
+        // Check that the valid components are still enabled
+        assertTrue(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString(validComponent)));
+        assertTrue(service.isPackageOrComponentAllowed(validComponent, userId));
+    }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
+    public void testSetPackageOrComponentEnabled_invalidComponent() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+
+        final int userId = 0;
+        final String invalidComponent = "package/Comp";
+
+        PackageManager pm = mock(PackageManager.class);
+        when(getContext().getPackageManager()).thenReturn(pm);
+        service = spy(service);
+
+        // Component is an invalid service and the package is available
+        doReturn(false).when(service)
+                .isValidService(ComponentName.unflattenFromString(invalidComponent), userId);
+        when(pm.isPackageAvailable("package")).thenReturn(true);
+        service.setPackageOrComponentEnabled(invalidComponent, userId, true, true);
+
+        // Verify that the component was not enabled
+        assertFalse("Not allowed: user: " + userId + " entry: " + invalidComponent
+                    + " for approval level " + APPROVAL_BY_COMPONENT,
+                service.isPackageOrComponentAllowed(invalidComponent, userId));
+        assertFalse(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString(invalidComponent)));
+    }
+
+    @Test
     public void testGetAllowedPackages_byUser() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 7e4ae67..2c645e0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -16,6 +16,9 @@
 package com.android.server.notification;
 
 import static android.os.UserHandle.USER_ALL;
+import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+
+import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -146,7 +149,8 @@
         mContext.setMockPackageManager(mPm);
         mContext.addMockSystemService(Context.USER_SERVICE, mUm);
         mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.string.config_defaultAssistantAccessComponent, "a/a");
+                com.android.internal.R.string.config_defaultAssistantAccessComponent,
+                mCn.flattenToString());
         mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
         when(mNm.getBinderService()).thenReturn(mINm);
         mContext.ensureTestableResources();
@@ -210,7 +214,8 @@
 
         writeXmlAndReload(USER_ALL);
 
-        ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0);
+        ArrayMap<Boolean, ArraySet<String>> approved =
+                mAssistants.mApproved.get(ActivityManager.getCurrentUser());
         // approved should not be null
         assertNotNull(approved);
         assertEquals(new ArraySet<>(), approved.get(true));
@@ -220,6 +225,35 @@
     }
 
     @Test
+    public void testWriteXml_userTurnedOffNAS_backup() throws Exception {
+        int userId = 10;
+
+        mAssistants.loadDefaultsFromConfig(true);
+
+        mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
+                true, true);
+
+        ComponentName current = CollectionUtils.firstOrNull(
+                mAssistants.getAllowedComponents(userId));
+        mAssistants.setUserSet(userId, true);
+        mAssistants.setPackageOrComponentEnabled(current.flattenToString(), userId, true, false,
+                true);
+        assertTrue(mAssistants.mIsUserChanged.get(userId));
+        assertThat(mAssistants.getApproved(userId, true)).isEmpty();
+
+        writeXmlAndReload(userId);
+
+        ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(userId);
+        // approved should not be null
+        assertNotNull(approved);
+        assertEquals(new ArraySet<>(), approved.get(true));
+
+        // user set is maintained
+        assertTrue(mAssistants.mIsUserChanged.get(userId));
+        assertThat(mAssistants.getApproved(userId, true)).isEmpty();
+    }
+
+    @Test
     public void testReadXml_userDisabled() throws Exception {
         String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
                 + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
@@ -610,4 +644,50 @@
 
         assertThat(mAssistants.getUnsupportedAdjustments(userId).size()).isEqualTo(0);
     }
+
+    @Test
+    @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public void testDisallowAdjustmentType() {
+        mAssistants.disallowAdjustmentType(Adjustment.KEY_RANKING_SCORE);
+        assertThat(mAssistants.getAllowedAssistantAdjustments())
+                .doesNotContain(Adjustment.KEY_RANKING_SCORE);
+        assertThat(mAssistants.getAllowedAssistantAdjustments()).contains(Adjustment.KEY_TYPE);
+    }
+
+    @Test
+    @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public void testAllowAdjustmentType() {
+        mAssistants.disallowAdjustmentType(Adjustment.KEY_RANKING_SCORE);
+        assertThat(mAssistants.getAllowedAssistantAdjustments())
+                .doesNotContain(Adjustment.KEY_RANKING_SCORE);
+        mAssistants.allowAdjustmentType(Adjustment.KEY_RANKING_SCORE);
+        assertThat(mAssistants.getAllowedAssistantAdjustments())
+                .contains(Adjustment.KEY_RANKING_SCORE);
+    }
+
+    @Test
+    @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public void testDisallowAdjustmentType_readWriteXml_entries() throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+
+        mAssistants.loadDefaultsFromConfig(true);
+        mAssistants.disallowAdjustmentType(KEY_IMPORTANCE);
+
+        writeXmlAndReload(USER_ALL);
+
+        assertThat(mAssistants.getAllowedAssistantAdjustments()).contains(
+                Adjustment.KEY_NOT_CONVERSATION);
+        assertThat(mAssistants.getAllowedAssistantAdjustments()).doesNotContain(
+                KEY_IMPORTANCE);
+    }
+
+    @Test
+    public void testDefaultAllowedAdjustments_readWriteXml_entries() throws Exception {
+        mAssistants.loadDefaultsFromConfig(true);
+
+        writeXmlAndReload(USER_ALL);
+
+        assertThat(mAssistants.getAllowedAssistantAdjustments())
+                .containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENTS);
+    }
 }
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 983e694..b34b1fb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -835,7 +835,7 @@
     }
 
     @Test
-    public void testListenerPost_UpdateLifetimeExtended() throws Exception {
+    public void testListenerPostLifetimeExtended_UpdatesOnlySysui() throws Exception {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
 
         // Create original notification, with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY.
@@ -856,31 +856,51 @@
         Notification.Builder nb2 = new Notification.Builder(mContext, channel.getId())
                 .setContentTitle("new title")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, false);
+                .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true);
         StatusBarNotification sbn2 = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
                 nb2.build(), userHandle, null, 0);
         NotificationRecord toPost = new NotificationRecord(mContext, sbn2, channel);
 
         // Create system ui-like service.
-        ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo(
+        ManagedServices.ManagedServiceInfo sysuiInfo = mListeners.new ManagedServiceInfo(
                 null, new ComponentName("a", "a"), sbn2.getUserId(), false, null, 33, 33);
-        info.isSystemUi = true;
-        INotificationListener l1 = mock(INotificationListener.class);
-        info.service = l1;
-        List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info);
+        sysuiInfo.isSystemUi = true;
+        INotificationListener sysuiListener = mock(INotificationListener.class);
+        sysuiInfo.service = sysuiListener;
+
+        // Create two non-system ui-like services.
+        ManagedServices.ManagedServiceInfo otherInfo1 = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("b", "b"), sbn2.getUserId(), false, null, 33, 33);
+        otherInfo1.isSystemUi = false;
+        INotificationListener otherListener1 = mock(INotificationListener.class);
+        otherInfo1.service = otherListener1;
+
+        ManagedServices.ManagedServiceInfo otherInfo2 = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("c", "c"), sbn2.getUserId(), false, null, 33, 33);
+        otherInfo2.isSystemUi = false;
+        INotificationListener otherListener2 = mock(INotificationListener.class);
+        otherInfo2.service = otherListener2;
+
+        List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(otherInfo1, sysuiInfo,
+                otherInfo2);
         when(mListeners.getServices()).thenReturn(services);
 
         FieldSetter.setField(mNm,
                 NotificationManagerService.class.getDeclaredField("mHandler"),
                 mock(NotificationManagerService.WorkerHandler.class));
         doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any());
-        doReturn(mock(NotificationRankingUpdate.class)).when(mNm).makeRankingUpdateLocked(info);
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(sysuiInfo);
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(otherInfo1);
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(otherInfo2);
         doReturn(false).when(mNm).isInLockDownMode(anyInt());
         doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt());
         doReturn(sbn2).when(mListeners).redactStatusBarNotification(sbn2);
         doReturn(sbn2).when(mListeners).redactStatusBarNotification(any());
 
-        // The notification change is posted to the service listener.
+        // Post notification change to the service listeners.
         mListeners.notifyPostedLocked(toPost, old);
 
         // Verify that the post occcurs with the updated notification value.
@@ -889,11 +909,190 @@
         runnableCaptor.getValue().run();
         ArgumentCaptor<IStatusBarNotificationHolder> sbnCaptor =
                 ArgumentCaptor.forClass(IStatusBarNotificationHolder.class);
-        verify(l1, times(1)).onNotificationPosted(sbnCaptor.capture(), any());
+        verify(sysuiListener, times(1)).onNotificationPosted(sbnCaptor.capture(), any());
         StatusBarNotification sbnResult = sbnCaptor.getValue().get();
         assertThat(sbnResult.getNotification()
                 .extras.getCharSequence(Notification.EXTRA_TITLE).toString())
                 .isEqualTo("new title");
+
+        verify(otherListener1, never()).onNotificationPosted(any(), any());
+        verify(otherListener2, never()).onNotificationPosted(any(), any());
+    }
+
+    @Test
+    public void testListenerPostLifeimteExtension_postsToAppropriateListeners() throws Exception {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+        // Create original notification, with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY.
+        String pkg = "pkg";
+        int uid = 9;
+        UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true);
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
+                nb.build(), userHandle, null, 0);
+        NotificationRecord leRecord = new NotificationRecord(mContext, sbn, channel);
+
+        // Creates updated notification (without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY)
+        Notification.Builder nb2 = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("new title")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, false);
+        StatusBarNotification sbn2 = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
+                nb2.build(), userHandle, null, 0);
+        NotificationRecord nonLeRecord = new NotificationRecord(mContext, sbn2, channel);
+
+        // Create system ui-like service.
+        ManagedServices.ManagedServiceInfo sysuiInfo = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("a", "a"), sbn2.getUserId(), false, null, 33, 33);
+        sysuiInfo.isSystemUi = true;
+        INotificationListener sysuiListener = mock(INotificationListener.class);
+        sysuiInfo.service = sysuiListener;
+
+        // Create two non-system ui-like services.
+        ManagedServices.ManagedServiceInfo otherInfo1 = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("b", "b"), sbn2.getUserId(), false, null, 33, 33);
+        otherInfo1.isSystemUi = false;
+        INotificationListener otherListener1 = mock(INotificationListener.class);
+        otherInfo1.service = otherListener1;
+
+        ManagedServices.ManagedServiceInfo otherInfo2 = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("c", "c"), sbn2.getUserId(), false, null, 33, 33);
+        otherInfo2.isSystemUi = false;
+        INotificationListener otherListener2 = mock(INotificationListener.class);
+        otherInfo2.service = otherListener2;
+
+        List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(otherInfo1, sysuiInfo,
+                otherInfo2);
+        when(mListeners.getServices()).thenReturn(services);
+
+        FieldSetter.setField(mNm,
+                NotificationManagerService.class.getDeclaredField("mHandler"),
+                mock(NotificationManagerService.WorkerHandler.class));
+        doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any());
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(sysuiInfo);
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(otherInfo1);
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(otherInfo2);
+        doReturn(false).when(mNm).isInLockDownMode(anyInt());
+        doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt());
+        doReturn(sbn2).when(mListeners).redactStatusBarNotification(sbn2);
+        doReturn(sbn2).when(mListeners).redactStatusBarNotification(any());
+
+        // The notification change is posted to the service listener.
+        // NonLE to LE should never happen, as LE can't be set in an update by the app.
+        // So we just want to test LE to NonLE.
+        mListeners.notifyPostedLocked(nonLeRecord /*=toPost*/, leRecord /*=old*/);
+
+        // Verify that the post occcurs with the updated notification value.
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mNm.mHandler, times(3)).post(runnableCaptor.capture());
+        List<Runnable> capturedRunnable = runnableCaptor.getAllValues();
+        for (Runnable r : capturedRunnable) {
+            r.run();
+        }
+
+        ArgumentCaptor<IStatusBarNotificationHolder> sbnCaptor =
+                ArgumentCaptor.forClass(IStatusBarNotificationHolder.class);
+        verify(sysuiListener, times(1)).onNotificationPosted(sbnCaptor.capture(), any());
+        StatusBarNotification sbnResult = sbnCaptor.getValue().get();
+        assertThat(sbnResult.getNotification()
+                .extras.getCharSequence(Notification.EXTRA_TITLE).toString())
+                .isEqualTo("new title");
+
+        verify(otherListener1, times(1)).onNotificationPosted(any(), any());
+        verify(otherListener2, times(1)).onNotificationPosted(any(), any());
+    }
+
+    @Test
+    public void testNotifyPostedLocked_postsToAppropriateListeners() throws Exception {
+        // Create original notification
+        String pkg = "pkg";
+        int uid = 9;
+        UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
+                nb.build(), userHandle, null, 0);
+        NotificationRecord oldRecord = new NotificationRecord(mContext, sbn, channel);
+
+        // Creates updated notification
+        Notification.Builder nb2 = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("new title")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn2 = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
+                nb2.build(), userHandle, null, 0);
+        NotificationRecord newRecord = new NotificationRecord(mContext, sbn2, channel);
+
+        // Create system ui-like service.
+        ManagedServices.ManagedServiceInfo sysuiInfo = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("a", "a"), sbn2.getUserId(), false, null, 33, 33);
+        sysuiInfo.isSystemUi = true;
+        INotificationListener sysuiListener = mock(INotificationListener.class);
+        sysuiInfo.service = sysuiListener;
+
+        // Create two non-system ui-like services.
+        ManagedServices.ManagedServiceInfo otherInfo1 = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("b", "b"), sbn2.getUserId(), false, null, 33, 33);
+        otherInfo1.isSystemUi = false;
+        INotificationListener otherListener1 = mock(INotificationListener.class);
+        otherInfo1.service = otherListener1;
+
+        ManagedServices.ManagedServiceInfo otherInfo2 = mListeners.new ManagedServiceInfo(
+                null, new ComponentName("c", "c"), sbn2.getUserId(), false, null, 33, 33);
+        otherInfo2.isSystemUi = false;
+        INotificationListener otherListener2 = mock(INotificationListener.class);
+        otherInfo2.service = otherListener2;
+
+        List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(otherInfo1, sysuiInfo,
+                otherInfo2);
+        when(mListeners.getServices()).thenReturn(services);
+
+        FieldSetter.setField(mNm,
+                NotificationManagerService.class.getDeclaredField("mHandler"),
+                mock(NotificationManagerService.WorkerHandler.class));
+        doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any());
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(sysuiInfo);
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(otherInfo1);
+        doReturn(mock(NotificationRankingUpdate.class)).when(mNm)
+                .makeRankingUpdateLocked(otherInfo2);
+        doReturn(false).when(mNm).isInLockDownMode(anyInt());
+        doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt());
+        doReturn(sbn2).when(mListeners).redactStatusBarNotification(sbn2);
+        doReturn(sbn2).when(mListeners).redactStatusBarNotification(any());
+
+        // The notification change is posted to the service listeners.
+        mListeners.notifyPostedLocked(newRecord, oldRecord);
+
+        // Verify that the post occcurs with the updated notification value.
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mNm.mHandler, times(3)).post(runnableCaptor.capture());
+        List<Runnable> capturedRunnable = runnableCaptor.getAllValues();
+        for (Runnable r : capturedRunnable) {
+            r.run();
+        }
+
+        ArgumentCaptor<IStatusBarNotificationHolder> sbnCaptor =
+                ArgumentCaptor.forClass(IStatusBarNotificationHolder.class);
+        verify(sysuiListener, times(1)).onNotificationPosted(sbnCaptor.capture(), any());
+        StatusBarNotification sbnResult = sbnCaptor.getValue().get();
+        assertThat(sbnResult.getNotification()
+                .extras.getCharSequence(Notification.EXTRA_TITLE).toString())
+                .isEqualTo("new title");
+
+        verify(otherListener1, times(1)).onNotificationPosted(any(), any());
+        verify(otherListener2, times(1)).onNotificationPosted(any(), any());
     }
 
     /**
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 1349ee0..e845d80 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -201,6 +201,7 @@
 import android.app.RemoteInputHistoryItem;
 import android.app.StatsManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.app.backup.BackupRestoreEventLogger;
 import android.app.job.JobScheduler;
 import android.app.role.RoleManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -4837,7 +4838,7 @@
                 null, mPkg, Process.myUserHandle());
 
         verify(mPreferencesHelper, times(1)).getNotificationChannels(
-                anyString(), anyInt(), anyBoolean());
+                anyString(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     @Test
@@ -4855,7 +4856,7 @@
         }
 
         verify(mPreferencesHelper, never()).getNotificationChannels(
-                anyString(), anyInt(), anyBoolean());
+                anyString(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     @Test
@@ -4870,7 +4871,7 @@
                 null, mPkg, Process.myUserHandle());
 
         verify(mPreferencesHelper, times(1)).getNotificationChannels(
-                anyString(), anyInt(), anyBoolean());
+                anyString(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     @Test
@@ -4890,7 +4891,7 @@
         }
 
         verify(mPreferencesHelper, never()).getNotificationChannels(
-                anyString(), anyInt(), anyBoolean());
+                anyString(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     @Test
@@ -4912,7 +4913,7 @@
         }
 
         verify(mPreferencesHelper, never()).getNotificationChannels(
-                anyString(), anyInt(), anyBoolean());
+                anyString(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     @Test
@@ -6340,7 +6341,7 @@
         mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
                 false,
-                UserHandle.USER_ALL);
+                UserHandle.USER_ALL, null);
         verify(mListeners, times(1)).readXml(any(), any(), anyBoolean(), anyInt());
         verify(mConditionProviders, times(1)).readXml(any(), any(), anyBoolean(), anyInt());
         verify(mAssistants, times(1)).readXml(any(), any(), anyBoolean(), anyInt());
@@ -6360,7 +6361,7 @@
         mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
                 false,
-                UserHandle.USER_ALL);
+                UserHandle.USER_ALL, null);
         verify(mSnoozeHelper, times(1)).readXml(any(TypedXmlPullParser.class), anyLong());
     }
 
@@ -6372,7 +6373,7 @@
         mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
                 false,
-                UserHandle.USER_ALL);
+                UserHandle.USER_ALL, null);
         verify(mListeners, never()).readXml(any(), any(), anyBoolean(), anyInt());
         verify(mConditionProviders, never()).readXml(any(), any(), anyBoolean(), anyInt());
         verify(mAssistants, never()).readXml(any(), any(), anyBoolean(), anyInt());
@@ -6404,7 +6405,7 @@
         mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())),
                 true,
-                10);
+                10, null);
         verify(mListeners, never()).readXml(any(), any(), eq(true), eq(10));
         verify(mConditionProviders, never()).readXml(any(), any(), eq(true), eq(10));
         verify(mAssistants, never()).readXml(any(), any(), eq(true), eq(10));
@@ -6430,7 +6431,7 @@
         mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())),
                 true,
-                10);
+                10, null);
         verify(mListeners, never()).readXml(any(), any(), eq(true), eq(10));
         verify(mConditionProviders, never()).readXml(any(), any(), eq(true), eq(10));
         verify(mAssistants, never()).readXml(any(), any(), eq(true), eq(10));
@@ -6458,7 +6459,7 @@
         mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())),
                 true,
-                10);
+                10, null);
         verify(mListeners, never()).readXml(any(), any(), eq(true), eq(10));
         verify(mConditionProviders, never()).readXml(any(), any(), eq(true), eq(10));
         verify(mAssistants, never()).readXml(any(), any(), eq(true), eq(10));
@@ -6485,7 +6486,7 @@
         mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())),
                 true,
-                10);
+                10, null);
         verify(mListeners, times(1)).readXml(any(), any(), eq(true), eq(10));
         verify(mConditionProviders, times(1)).readXml(any(), any(), eq(true), eq(10));
         verify(mAssistants, times(1)).readXml(any(), any(), eq(true), eq(10));
@@ -17061,4 +17062,20 @@
         assertThat(mService.hasFlag(captor.getValue().getNotification().flags,
                 FLAG_PROMOTED_ONGOING)).isFalse();
     }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+    public void testAppCannotUseReservedBundleChannels() throws Exception {
+        mBinderService.getBubblePreferenceForPackage(mPkg, mUid);
+        NotificationChannel news = mBinderService.getNotificationChannel(
+                mPkg, mContext.getUserId(), mPkg, NEWS_ID);
+        assertThat(news).isNotNull();
+
+        NotificationRecord nr = generateNotificationRecord(news);
+        mBinderService.enqueueNotificationWithTag(mPkg, mPkg, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        assertThat(mService.mNotificationList).isEmpty();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 50a5f65..4391152 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -546,7 +546,8 @@
     }
 
     @Test
-    @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI)
+    @EnableFlags(com.android.server.notification.Flags
+            .FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI_FOR_CHANNEL)
     public void testVibration_customVibrationForSound_withVibrationUri() throws IOException {
         defaultChannel.enableVibration(true);
         VibrationInfo vibration = getTestingVibration(mVibrator);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d64b9e8..b92bdb5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -18,6 +18,7 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.Flags.FLAG_MODES_UI;
 import static android.app.Notification.VISIBILITY_PRIVATE;
 import static android.app.Notification.VISIBILITY_SECRET;
 import static android.app.NotificationChannel.ALLOW_BUBBLE_ON;
@@ -81,6 +82,7 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -248,7 +250,7 @@
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
         return FlagsParameterization.allCombinationsOf(
-                FLAG_NOTIFICATION_CLASSIFICATION);
+                FLAG_NOTIFICATION_CLASSIFICATION, FLAG_MODES_UI);
     }
 
     public PreferencesHelperTest(FlagsParameterization flags) {
@@ -2545,7 +2547,7 @@
 
         // Returns only non-deleted channels
         List<NotificationChannel> channels =
-                mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList();
+                mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList();
         // Default channel + non-deleted channel + system defaults
         assertEquals(notificationClassification() ? 6 : 2, channels.size());
         for (NotificationChannel nc : channels) {
@@ -2555,7 +2557,7 @@
         }
 
         // Returns deleted channels too
-        channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList();
+        channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList();
         // Includes system channel(s)
         assertEquals(notificationClassification() ? 7 : 3, channels.size());
         for (NotificationChannel nc : channels) {
@@ -2701,7 +2703,11 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+        } else {
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         // create notification channel that can bypass dnd
@@ -2711,18 +2717,30 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                 uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         // delete channels
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+        } else {
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2738,7 +2756,11 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+        } else {
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         // Recreate a channel & now the app has dnd access granted and can set the bypass dnd field
@@ -2748,7 +2770,11 @@
                 uid, false);
 
         assertTrue(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2764,7 +2790,11 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+        } else {
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         // create notification channel that can bypass dnd, using local app level settings
@@ -2774,18 +2804,30 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                 uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         // delete channels
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
         assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+        } else {
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2812,7 +2854,11 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2834,7 +2880,11 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2856,7 +2906,11 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2872,7 +2926,11 @@
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
                 uid, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+        } else {
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         // update channel so it CAN bypass dnd:
@@ -2880,7 +2938,11 @@
         channel.setBypassDnd(true);
         mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
         assertTrue(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(true));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
 
         // update channel so it can't bypass dnd:
@@ -2888,7 +2950,11 @@
         channel.setBypassDnd(false);
         mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2901,7 +2967,11 @@
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
         mHelper.syncChannelsBypassingDnd();
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(false));
+        } else {
+            verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -2911,7 +2981,11 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
         assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        if (android.app.Flags.modesUi()) {
+            verify(mMockZenModeHelper, never()).updateHasPriorityChannels(anyBoolean());
+        } else {
+            verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
+        }
         resetZenModeHelper();
     }
 
@@ -3125,7 +3199,8 @@
         mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1);
 
         // Only default channel remains
-        assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
+        assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true)
+                .getList().size());
     }
 
     @Test
@@ -3241,12 +3316,12 @@
         // user 0 records remain
         for (int i = 0; i < user0Uids.length; i++) {
             assertEquals(notificationClassification() ? 5 : 1,
-                    mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false)
+                    mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true)
                             .getList().size());
         }
         // user 1 records are gone
         for (int i = 0; i < user1Uids.length; i++) {
-            assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false)
+            assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false, true)
                     .getList().size());
         }
     }
@@ -3263,7 +3338,7 @@
                 new int[]{UID_N_MR1}));
 
         assertEquals(0, mHelper.getNotificationChannels(
-                PKG_N_MR1, UID_N_MR1, true).getList().size());
+                PKG_N_MR1, UID_N_MR1, true, true).getList().size());
 
         // Not deleted
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
@@ -3272,7 +3347,8 @@
         assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM,
                 new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}));
         assertEquals(notificationClassification() ? 6 : 2,
-                mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+                mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
+                        .getList().size());
     }
 
     @Test
@@ -3331,7 +3407,7 @@
         assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
         assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
         assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
-        assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true).getList().size());
+        assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size());
         assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size());
 
         NotificationChannel channel = getChannel();
@@ -3345,7 +3421,8 @@
     public void testRecordDefaults() throws Exception {
         assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
         assertEquals(notificationClassification() ? 5 : 1,
-                mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+                mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
+                        .getList().size());
     }
 
     @Test
@@ -6289,6 +6366,15 @@
 
     @Test
     @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+    public void testGetNotificationChannels_omitBundleChannels() {
+        // do something that triggers settings creation for an app
+        mHelper.setShowBadge(PKG_O, UID_O, true);
+
+        assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty();
+    }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
     public void testNotificationBundles() {
         // do something that triggers settings creation for an app
         mHelper.setShowBadge(PKG_O, UID_O, true);
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 294027b..8b3ac2b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -7027,6 +7027,29 @@
                 ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
     }
 
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void updateHasPriorityChannels_keepsChannelSettings() {
+        setupZenConfig();
+
+        // Set priority channels setting on manual mode to confirm that it is unaffected by changes
+        // to the state describing the existence of such channels.
+        mZenModeHelper.mConfig.manualRule.zenPolicy =
+                new ZenPolicy.Builder(mZenModeHelper.mConfig.manualRule.zenPolicy)
+                        .allowPriorityChannels(false)
+                        .build();
+
+        mZenModeHelper.updateHasPriorityChannels(true);
+        assertThat(mZenModeHelper.getNotificationPolicy().hasPriorityChannels()).isTrue();
+
+        // getNotificationPolicy() gets its policy from the manual rule; channels not permitted
+        assertThat(mZenModeHelper.getNotificationPolicy().allowPriorityChannels()).isFalse();
+
+        mZenModeHelper.updateHasPriorityChannels(false);
+        assertThat(mZenModeHelper.getNotificationPolicy().hasPriorityChannels()).isFalse();
+        assertThat(mZenModeHelper.getNotificationPolicy().allowPriorityChannels()).isFalse();
+    }
+
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
             @Nullable ZenPolicy zenPolicy) {
         ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
index 1493253..d7ae046 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
@@ -35,7 +35,9 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.SparseArray;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -63,6 +65,8 @@
 
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Mock
     private PackageManagerInternal mPackageManagerInternalMock;
@@ -186,6 +190,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testStepAndRampSegments_withValidFreqMapping_returnsClippedValuesOnlyInRamps() {
         VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
                 // Individual step without frequency control, will not use PWLE composition
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index f7127df..3b2f532 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -453,20 +453,7 @@
     }
 
     @Test
-    public void testVibrationAttribute_scrollFeedback_inputCustomizedFlag_useTouchUsage() {
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
-        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
-
-        for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = provider.getVibrationAttributes(effectId, /* flags */
-                    0, /* privFlags */ 0);
-            assertWithMessage("Expected USAGE_TOUCH for scroll effect " + effectId
-                    + ", if no input customization").that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
-        }
-    }
-
-    @Test
-    public void testVibrationAttribute_scrollFeedback_noInputCustomizedFlag_useHardwareFeedback() {
+    public void testVibrationAttribute_scrollFeedback_useHardwareFeedback() {
         HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/RampToStepAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/RampToStepAdapterTest.java
index 8103682..96f0fda2 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/RampToStepAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/RampToStepAdapterTest.java
@@ -26,8 +26,11 @@
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -49,6 +52,9 @@
 
     private RampToStepAdapter mAdapter;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mAdapter = new RampToStepAdapter(TEST_STEP_DURATION);
@@ -87,6 +93,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testRampSegments_withoutPwleCapability_convertsRampsToSteps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10),
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/SplitSegmentsAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/SplitSegmentsAdapterTest.java
index f2c3726..53e49e0 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/SplitSegmentsAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/SplitSegmentsAdapterTest.java
@@ -26,8 +26,11 @@
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -52,6 +55,9 @@
 
     private SplitSegmentsAdapter mAdapter;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mAdapter = new SplitSegmentsAdapter();
@@ -97,6 +103,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testRampSegments_withPwleDurationLimit_splitsLongRampsAndPreserveOtherSegments() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/StepToRampAdapterTest.java
index d501dba..fae634d 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/StepToRampAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -26,8 +26,11 @@
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -48,6 +51,9 @@
 
     private StepToRampAdapter mAdapter;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mAdapter = new StepToRampAdapter();
@@ -134,6 +140,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void testStepSegments_withPwleCapabilityAndFrequency_convertsStepsToRamps() {
         List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
                 new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 100, /* duration= */ 10),
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 7536f5f..58a1e84 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -63,9 +63,11 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.util.SparseArray;
 
@@ -113,6 +115,8 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
     @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
@@ -780,6 +784,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() {
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
         fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
@@ -870,6 +875,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void vibrate_singleVibratorPwle_runsComposePwle() {
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
         fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
@@ -1724,6 +1730,7 @@
     }
 
     @Test
+    @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
     public void vibrate_pwleWithRampDown_doesNotAddRampDown() {
         when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index b782162..7f5da41 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.vibrator;
 
-import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -28,6 +26,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -89,17 +88,14 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
-import android.view.flags.Flags;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -168,9 +164,7 @@
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Mock
     private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@@ -800,7 +794,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS)
     public void vibrate_thenDeniedAppOps_getsCancelled() throws Throwable {
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
@@ -894,7 +888,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
+    @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
     public void vibrate_thenFgUserRequestsMute_getsCancelled() throws Throwable {
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
@@ -1331,6 +1325,37 @@
     }
 
     @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_VIBRATION_PIPELINE_ENABLED)
+    public void vibrate_withPipelineFlagEnabledAndShortEffect_continuesOngoingEffect()
+            throws Exception {
+        assumeTrue(mVibrationConfig.getVibrationPipelineMaxDurationMs() > 0);
+
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        fakeVibrator.setSupportedPrimitives(
+                VibrationEffect.Composition.PRIMITIVE_CLICK,
+                VibrationEffect.Composition.PRIMITIVE_THUD);
+        fakeVibrator.setPrimitiveDuration(
+                mVibrationConfig.getVibrationPipelineMaxDurationMs() - 1);
+        VibratorManagerService service = createSystemReadyService();
+
+        HalVibration firstVibration = vibrateWithUid(service, /* uid= */ 123,
+                VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose(), HAPTIC_FEEDBACK_ATTRS);
+        HalVibration secondVibration = vibrateWithUid(service, /* uid= */ 456,
+                VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+                        .compose(), HAPTIC_FEEDBACK_ATTRS);
+        secondVibration.waitForEnd();
+
+        assertThat(fakeVibrator.getAllEffectSegments()).hasSize(2);
+        assertThat(firstVibration.getStatus()).isEqualTo(Status.FINISHED);
+        assertThat(secondVibration.getStatus()).isEqualTo(Status.FINISHED);
+    }
+
+    @Test
     public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1512,6 +1537,7 @@
     }
 
     @Test
+    @EnableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API)
     public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception {
         // Deny permissions that would have been required for regular vibrations, and check that
         // the vibration proceed as expected to verify that haptic feedback does not need these
@@ -1520,8 +1546,6 @@
         denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
         denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
         denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
-        // Flag override to enable the scroll feedack constants to bypass interruption policies.
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
         mHapticFeedbackVibrationMap.put(
                 HapticFeedbackConstants.SCROLL_TICK,
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -1544,6 +1568,10 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedbackForInputDevice_doesNotRequireVibrateOrBypassPermissions()
             throws Exception {
         // Deny permissions that would have been required for regular vibrations, and check that
@@ -1553,9 +1581,6 @@
         denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
         denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
         denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
-        // Flag override to enable the scroll feedback constants to bypass interruption policies.
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         mHapticFeedbackVibrationMapSourceRotary.put(
                 HapticFeedbackConstants.SCROLL_TICK,
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -1628,12 +1653,14 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedbackForInputDevice_restrictedConstantsWithoutPermission_doesNotVibrate()
             throws Exception {
         // Deny permission to vibrate with restricted constants
         denyPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         // Public constant, no permission required
         mHapticFeedbackVibrationMapSourceRotary.put(
                 HapticFeedbackConstants.CONFIRM,
@@ -1697,9 +1724,9 @@
     }
 
     @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED)
     public void performHapticFeedbackForInputDevice_restrictedConstantsWithPermission_playsVibration()
             throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         // Grant permission to vibrate with restricted constants
         grantPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
         // Public constant, no permission required
@@ -1732,9 +1759,11 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         denyPermission(android.Manifest.permission.VIBRATE);
         mHapticFeedbackVibrationMap.put(
                 HapticFeedbackConstants.KEYBOARD_TAP,
@@ -1767,9 +1796,11 @@
     }
 
     @Test
+    @EnableFlags({
+            android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+            android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+    })
     public void performHapticFeedback_doesNotVibrateForInvalidConstant() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
-        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
         denyPermission(android.Manifest.permission.VIBRATE);
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
@@ -1791,7 +1822,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
     public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception {
         // Deny permission to vibrate with vendor effects
         denyPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
@@ -1816,7 +1847,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
     public void vibrate_vendorEffectsWithPermission_successful() throws Exception {
         // Grant permission to vibrate with vendor effects
         grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
@@ -1904,7 +1935,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void vibrate_withAdaptiveHaptics_appliesCorrectAdaptiveScales() throws Exception {
         // Keep user settings the same as device default so only adaptive scale is applied.
         setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
@@ -1947,7 +1978,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled({
+    @EnableFlags({
             android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
             android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
     })
@@ -2418,7 +2449,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales() {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
@@ -2465,7 +2496,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @DisableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone() {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
@@ -2585,7 +2616,7 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
+    @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
     public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -3105,9 +3136,20 @@
         return vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
     }
 
+    private HalVibration vibrateWithUid(VibratorManagerService service, int uid,
+            VibrationEffect effect, VibrationAttributes attrs) {
+        return vibrateWithUidAndDevice(service, uid, Context.DEVICE_ID_DEFAULT,
+                CombinedVibration.createParallel(effect), attrs);
+    }
+
     private HalVibration vibrateWithDevice(VibratorManagerService service, int deviceId,
             CombinedVibration effect, VibrationAttributes attrs) {
-        HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect,
+        return vibrateWithUidAndDevice(service, UID, deviceId, effect, attrs);
+    }
+
+    private HalVibration vibrateWithUidAndDevice(VibratorManagerService service, int uid,
+            int deviceId, CombinedVibration effect, VibrationAttributes attrs) {
+        HalVibration vib = service.vibrateWithPermissionCheck(uid, deviceId, PACKAGE_NAME, effect,
                 attrs, "some reason", service);
         if (vib != null) {
             mPendingVibrations.add(vib);
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index f96177d..75a9cedf 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -76,7 +76,11 @@
     private float mFrequencyResolution = Float.NaN;
     private float mQFactor = Float.NaN;
     private float[] mMaxAmplitudes;
+
+    private float[] mFrequenciesHz;
+    private float[] mOutputAccelerationsGs;
     private long mVendorEffectDuration = EFFECT_DURATION;
+    private long mPrimitiveDuration = EFFECT_DURATION;
 
     void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) {
         mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment);
@@ -168,7 +172,7 @@
             }
             long duration = 0;
             for (PrimitiveSegment primitive : primitives) {
-                duration += EFFECT_DURATION + primitive.getDelay();
+                duration += mPrimitiveDuration + primitive.getDelay();
                 recordEffectSegment(vibrationId, primitive);
             }
             applyLatency(mOnLatency);
@@ -220,6 +224,9 @@
             infoBuilder.setQFactor(mQFactor);
             infoBuilder.setFrequencyProfileLegacy(new VibratorInfo.FrequencyProfileLegacy(
                     mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes));
+            infoBuilder.setFrequencyProfile(
+                    new VibratorInfo.FrequencyProfile(mResonantFrequency, mFrequenciesHz,
+                            mOutputAccelerationsGs));
             infoBuilder.setMaxEnvelopeEffectSize(mMaxEnvelopeEffectSize);
             infoBuilder.setMinEnvelopeEffectControlPointDurationMillis(
                     mMinEnvelopeEffectControlPointDurationMillis);
@@ -360,11 +367,26 @@
         mMaxAmplitudes = maxAmplitudes;
     }
 
+    /** Set the list of available frequencies. */
+    public void setFrequenciesHz(float[] frequenciesHz) {
+        mFrequenciesHz = frequenciesHz;
+    }
+
+    /** Set the max output acceleration achievable by the supported frequencies. */
+    public void setOutputAccelerationsGs(float[] outputAccelerationsGs) {
+        mOutputAccelerationsGs = outputAccelerationsGs;
+    }
+
     /** Set the duration of vendor effects in fake vibrator hardware. */
     public void setVendorEffectDuration(long durationMs) {
         mVendorEffectDuration = durationMs;
     }
 
+    /** Set the duration of primitives in fake vibrator hardware. */
+    public void setPrimitiveDuration(long primitiveDuration) {
+        mPrimitiveDuration = primitiveDuration;
+    }
+
     /**
      * Set the maximum number of envelope effects control points supported in fake vibrator
      * hardware.
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index ab00bfd..1f16776 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -71,6 +71,7 @@
         "CtsSurfaceValidatorLib",
         "service-sdksandbox.impl",
         "com.android.window.flags.window-aconfig-java",
+        "android.view.inputmethod.flags-aconfig-java",
         "flag-junit",
     ],
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index d0080d2..d5f86b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1261,6 +1261,33 @@
     }
 
     @Test
+    public void testLaunchAdjacentDisabled() {
+        final ActivityStarter starter =
+                prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */);
+        final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        final ActivityRecord[] outActivity = new ActivityRecord[1];
+
+        // Activity must not land on split-screen task if currently not in split-screen mode.
+        starter.setActivityOptions(options.toBundle())
+                .setReason("testLaunchAdjacentDisabled")
+                .setOutActivity(outActivity).execute();
+        assertThat(outActivity[0].inMultiWindowMode()).isFalse();
+
+        // Move activity to split-screen-primary task and make sure it has the focus.
+        TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent());
+        top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
+        top.getRootTask().moveToFront("testLaunchAdjacentDisabled");
+        top.getRootTask().setLaunchAdjacentDisabled(true);
+
+        // Ensure activity does not launch into split-screen-secondary when launch adjacent is
+        // disabled
+        startActivityInner(starter, outActivity[0], top, options, null /* inTask */,
+                null /* taskFragment*/);
+        assertThat(outActivity[0].isDescendantOf(splitOrg.mSecondary)).isFalse();
+    }
+
+    @Test
     public void testTransientLaunchWithKeyguard() {
         final ActivityStarter starter = prepareStarter(0 /* flags */);
         final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 3910904..7509681 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -683,6 +683,41 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+    public void testRealCaller_sawPermission() {
+        int callingUid = REGULAR_UID_1;
+        int callingPid = REGULAR_PID_1;
+        final String callingPackage = REGULAR_PACKAGE_1;
+        int realCallingUid = REGULAR_UID_2;
+        int realCallingPid = REGULAR_PID_2;
+
+        // setup state
+        when(mService.hasSystemAlertWindowPermission(eq(realCallingUid), eq(realCallingPid),
+                any())).thenReturn(true);
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions =
+                mCheckedOptions.setPendingIntentBackgroundActivityStartMode(
+                        MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, null,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+                balState);
+        balState.setResultForCaller(callerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+                BAL_ALLOW_SAW_PERMISSION);
+    }
+
+    @Test
     public void testCaller_isRecents() {
         int callingUid = REGULAR_UID_1;
         int callingPid = REGULAR_PID_1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index e443696..c51261f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -52,6 +52,7 @@
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.ContentRecordingSession;
+import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.SurfaceControl;
@@ -93,9 +94,11 @@
     private boolean mHandleAnisotropicDisplayMirroring = false;
 
     @Before public void setUp() {
+        mDisplayInfo.type = Display.TYPE_VIRTUAL;
         MockitoAnnotations.initMocks(this);
 
         doReturn(INVALID_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
+        doReturn(false).when(mWm.mDisplayManagerInternal).isDisplayReadyForMirroring(anyInt());
 
         // Skip unnecessary operations of relayout.
         spyOn(mWm.mWindowPlacerLocked);
@@ -163,6 +166,25 @@
     }
 
     @Test
+    public void testUpdateRecording_externalDisplayWithoutUserConfirmation() {
+        mDisplayInfo.type = Display.TYPE_EXTERNAL;
+        defaultInit();
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+    }
+
+    @Test
+    public void testUpdateRecording_externalDisplayWithUserConfirmation() {
+        doReturn(true).when(mWm.mDisplayManagerInternal).isDisplayReadyForMirroring(anyInt());
+        mDisplayInfo.type = Display.TYPE_EXTERNAL;
+        defaultInit();
+        mContentRecorder.setContentRecordingSession(mDisplaySession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+    }
+
+    @Test
     public void testUpdateRecording_display_invalidDisplayIdToMirror() {
         defaultInit();
         ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java
index 5e8f347..c8fc482 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java
@@ -26,7 +26,6 @@
 
 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.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertFalse;
@@ -73,7 +72,6 @@
         when(mMockWindowState.getRequestedVisibleTypes()).thenReturn(0);
         when(mMockActivityRecord.findMainWindow()).thenReturn(mMockWindowState);
 
-        spy(mDisplayContent);
         doReturn(mMockActivityRecord).when(mDisplayContent).topRunningActivity();
         when(mDisplayContent.getIgnoreOrientationRequest()).thenReturn(true);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index d2cf03d..ee56210 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -41,11 +41,13 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.StatusBarManager;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.view.InsetsFrameProvider;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
@@ -525,6 +527,59 @@
         assertTrue(win1.getWindowFrames().hasInsetsChanged());
     }
 
+    /**
+     * This test verifies that after setting {@link WindowContainer#mExcludeInsetsTypes}, the IME
+     * insets have a height of zero (applied in {@link InsetsPolicy#adjustVisibilityForIme}).
+     */
+    @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+    @SetupWindows(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testExcludeImeInsets() {
+        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+        final InsetsSource imeSource = new InsetsSource(ID_IME, ime());
+        imeSource.setVisible(true);
+        mImeWindow.mHasSurface = true;
+
+        final WindowState win = addWindow(TYPE_APPLICATION, "win1");
+        win.setRequestedVisibleTypes(0, ime());
+
+        win.mAboveInsetsState.addSource(imeSource);
+        win.mHasSurface = true;
+
+        DisplayContentTests.performLayout(mDisplayContent);
+        // IME should cover half of the app's window
+        final var winFrame = win.getFrame();
+        imeSource.setFrame(winFrame.left, winFrame.bottom / 2, winFrame.right, winFrame.bottom);
+        imeSource.setVisibleFrame(imeSource.getFrame());
+        DisplayContentTests.performLayout(mDisplayContent);
+
+        assertTrue(mImeWindow.isVisible());
+        assertTrue(win.isVisible());
+
+        displayPolicy.beginPostLayoutPolicyLw();
+        displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+        displayPolicy.finishPostLayoutPolicyLw();
+
+        final var imeInsetsShown = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+                true);
+        assertEquals(new Rect(0, 0, 0, winFrame.bottom / 2), imeInsetsShown.toRect());
+
+
+        // Now we're setting the excludedInsetsTypes for the IME. The IME is still showing, but
+        // in this case, InsetsPolicy#adjustVisibilityForIme will override and dispatch IME
+        // insets with zero height.
+        win.setExcludeInsetsTypes(ime());
+
+        displayPolicy.beginPostLayoutPolicyLw();
+        displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+        displayPolicy.finishPostLayoutPolicyLw();
+
+        final var imeInsetsHidden = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+                true);
+        assertEquals(Insets.NONE, imeInsetsHidden);
+    }
+
+
     private WindowState addNavigationBar() {
         final Binder owner = new Binder();
         final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 4570588..79967b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -30,6 +30,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Insets;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.InsetsSource;
@@ -260,6 +261,27 @@
     }
 
     @Test
+    public void testUpdateInsetsControlPosition() {
+        final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+
+        final WindowState ime1 = createWindow(null, TYPE_INPUT_METHOD, "ime1");
+        ime1.getFrame().set(new Rect(0, 0, 0, 0));
+        mImeProvider.setWindowContainer(ime1, null, null);
+        mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
+        ime1.getFrame().set(new Rect(0, 400, 500, 500));
+        mImeProvider.updateInsetsControlPosition(ime1);
+        assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition());
+
+        final WindowState ime2 = createWindow(null, TYPE_INPUT_METHOD, "ime2");
+        ime2.getFrame().set(new Rect(0, 0, 0, 0));
+        mImeProvider.setWindowContainer(ime2, null, null);
+        mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
+        ime2.getFrame().set(new Rect(0, 400, 500, 500));
+        mImeProvider.updateInsetsControlPosition(ime2);
+        assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition());
+    }
+
+    @Test
     public void testSetRequestedVisibleTypes() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 72f4fa91..c30f70e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -183,15 +183,32 @@
         DeviceConfig.setProperties(mInitialConstrainDisplayApisFlags);
     }
 
-    private void setUpApp(DisplayContent display) {
-        mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
-        mActivity = mTask.getTopNonFinishingActivity();
-        doReturn(false).when(mActivity).isImmersiveMode(any());
+    private ActivityRecord setUpApp(DisplayContent display) {
+        return setUpApp(display, null /* appBuilder */);
     }
 
-    private void setUpDisplaySizeWithApp(int dw, int dh) {
+    private ActivityRecord setUpApp(DisplayContent display, ActivityBuilder appBuilder) {
+        // Use the real package name (com.android.frameworks.wmtests) so that
+        // EnableCompatChanges/DisableCompatChanges can take effect.
+        // Otherwise the fake WindowTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME will make
+        // PlatformCompat#isChangeEnabledByPackageName always return default value.
+        final ComponentName componentName = ComponentName.createRelative(
+                mContext, SizeCompatTests.class.getName());
+        mTask = new TaskBuilder(mSupervisor).setDisplay(display).setComponent(componentName)
+                .build();
+        final ActivityBuilder builder = appBuilder != null ? appBuilder : new ActivityBuilder(mAtm);
+        mActivity = builder.setTask(mTask).setComponent(componentName).build();
+        doReturn(false).when(mActivity).isImmersiveMode(any());
+        return mActivity;
+    }
+
+    private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh) {
+        return setUpDisplaySizeWithApp(dw, dh, null /* appBuilder */);
+    }
+
+    private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh, ActivityBuilder appBuilder) {
         final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm, dw, dh);
-        setUpApp(builder.build());
+        return setUpApp(builder.build(), appBuilder);
     }
 
     @Test
@@ -352,15 +369,13 @@
                 .setCanRotate(false)
                 .setNotch(cutoutHeight)
                 .build();
-        setUpApp(display);
         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         mWm.mAppCompatConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
         mWm.mAppCompatConfiguration.setIsVerticalReachabilityEnabled(true);
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
-                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
-                .build();
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
 
         doReturn(true).when(activity).isImmersiveMode(any());
         addWindowToActivity(activity);
@@ -424,17 +439,16 @@
     @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
-        final int notchHeight = 100;
-        setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
-
-        final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
         final float aspectRatio = 1.2f;
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm)
                 .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
                 .setMinAspectRatio(aspectRatio)
                 .setMaxAspectRatio(aspectRatio)
-                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
-                .build();
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+        final int notchHeight = 100;
+        final ActivityRecord activity = setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800)
+                .setNotch(notchHeight).build(), activityBuilder);
+        final Rect displayBounds = activity.mDisplayContent.getWindowConfiguration().getBounds();
         final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
 
         // The parent configuration doesn't change since the first resolved configuration, so the
@@ -1327,12 +1341,8 @@
     public void testOverrideMinAspectRatioSmall_overridden() {
         final int dh = 1200;
         final int dw = 1000;
-        setUpDisplaySizeWithApp(dw, dh);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         final Rect bounds = activity.getBounds();
         assertEquals(dh, bounds.height());
@@ -1346,13 +1356,9 @@
     public void testOverrideMinAspectRatioSmall_notOverridden() {
         final int dh = 1200;
         final int dw = 1000;
-        setUpDisplaySizeWithApp(dw, dh);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
 
         // Activity's requested aspect ratio is larger than OVERRIDE_MIN_ASPECT_RATIO_SMALL,
         // so OVERRIDE_MIN_ASPECT_RATIO_SMALL is ignored.
@@ -1366,12 +1372,8 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioMedium() {
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1388,13 +1390,8 @@
         final int notchHeight = 200;
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh)
                 .setNotch(notchHeight).setSystemDecorations(true).build();
-        mTask = new TaskBuilder(mSupervisor).setDisplay(display).build();
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setMinAspectRatio(2f)
-                .build();
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).setMinAspectRatio(2f));
 
         // The per-package override should have no effect, because the manifest aspect ratio is
         // larger (2:1)
@@ -1411,13 +1408,9 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatioLargerThanManifest() {
-        setUpDisplaySizeWithApp(1400, 1600);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                        .setMinAspectRatio(1.1f));
 
         // The per-package override should have no effect, because the manifest aspect ratio is
         // larger (2:1)
@@ -1430,12 +1423,8 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatioLarge() {
-        setUpDisplaySizeWithApp(1500, 1600);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1500, 1600,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 16:9 aspect ratio
         assertEquals(1600, activity.getBounds().height());
@@ -1449,13 +1438,8 @@
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatio_Both() {
         // If multiple override aspect ratios are set, we should use the largest one
-
-        setUpDisplaySizeWithApp(1400, 1600);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 16:9 aspect ratio
         assertEquals(1600, activity.getBounds().height());
@@ -1470,11 +1454,7 @@
     public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
         // isn't applied.
-
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200);
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1497,13 +1477,8 @@
     public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
         // isn't applied.
-
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1524,12 +1499,8 @@
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1550,10 +1521,7 @@
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
     public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200);
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1568,13 +1536,8 @@
     public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
         // isn't applied.
-
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
@@ -1588,12 +1551,8 @@
         // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
         // OVERRIDE_MIN_ASPECT_RATIO being also set.
 
-        setUpDisplaySizeWithApp(1000, 1200);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1604,12 +1563,8 @@
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
     public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() {
-        setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800,
+                new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         final TestSplitOrganizer organizer =
                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -2462,10 +2417,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
         final int displayWidth = 1400;
         final int displayHeight = 1600;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2483,10 +2436,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() {
         final int displayWidth = 1600;
         final int displayHeight = 1400;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2505,10 +2456,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() {
         final int displayWidth = 1400;
         final int displayHeight = 1600;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2527,10 +2476,8 @@
     public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() {
         final int displayWidth = 1600;
         final int displayHeight = 1400;
-        setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setMinAspectRatio(1.1f)
-                .build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+                new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2549,8 +2496,7 @@
         mAtm.mDevEnableNonResizableMultiWindow = true;
         final int screenWidth = 1800;
         final int screenHeight = 1000;
-        setUpDisplaySizeWithApp(screenWidth, screenHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight);
 
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate real display with top insets.
@@ -2585,8 +2531,7 @@
         mAtm.mDevEnableNonResizableMultiWindow = true;
         final int screenWidth = 1000;
         final int screenHeight = 1800;
-        setUpDisplaySizeWithApp(screenWidth, screenHeight);
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight);
 
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate real display with top insets.
@@ -2616,18 +2561,18 @@
 
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
     public void testOverrideMinAspectRatioExcludePortraitFullscreen() {
-        setUpDisplaySizeWithApp(2600, 1600);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
-
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        resizeDisplay(mDisplayContent, 2600, 1600);
+        mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
 
         // Non-resizable portrait activity
-        prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        final ActivityRecord activity = setUpApp(mDisplayContent, new ActivityBuilder(mAtm)
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // At first, OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply, because the
         // display is in landscape
@@ -2645,16 +2590,16 @@
 
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
     public void testOverrideMinAspectRatioExcludePortraitFullscreenNotApplied() {
         // In this test, the activity is not in fullscreen, so the override is not applied
-        setUpDisplaySizeWithApp(2600, 1600);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
+        resizeDisplay(mDisplayContent, 2600, 1600);
+        mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
 
-        // Create a size compat activity on the same task.
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = setUpApp(mDisplayContent);
 
         final TestSplitOrganizer organizer =
                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -2983,6 +2928,7 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
     public void testDisplayIgnoreOrientationRequest_orientationChangedToUnspecified() {
         // Set up a display in landscape and ignoring orientation request.
         setUpDisplaySizeWithApp(2800, 1400);
@@ -3437,6 +3383,7 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
     public void testSupportsNonResizableInSplitScreen_letterboxForAspectRatioRestriction() {
         // Support non resizable in multi window
         mAtm.mDevEnableNonResizableMultiWindow = true;
@@ -4050,9 +3997,9 @@
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
     public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() {
+        final DisplayContent display = mDisplayContent;
         // Set up portrait close to square display.
-        setUpDisplaySizeWithApp(2200, 2280);
-        final DisplayContent display = mActivity.mDisplayContent;
+        resizeDisplay(display, 2200, 2280);
         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape.
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
@@ -4067,9 +4014,8 @@
             display.sendNewConfiguration();
         }
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         // Activity should not be letterboxed and should have portrait app bounds even though
         // orientation is not respected with insets as insets have been decoupled.
@@ -4085,9 +4031,9 @@
     @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() {
+        final DisplayContent display = mDisplayContent;
         // Set up portrait close to square display
-        setUpDisplaySizeWithApp(2200, 2280);
-        final DisplayContent display = mActivity.mDisplayContent;
+        resizeDisplay(display, 2200, 2280);
         display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
@@ -4102,9 +4048,8 @@
             display.sendNewConfiguration();
         }
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
 
         final Rect bounds = activity.getBounds();
         // Activity should be letterboxed and should have portrait app bounds
@@ -4116,9 +4061,9 @@
     @Test
     @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testFixedAspectRatioAppInPortraitCloseToSquareDisplay_notInSizeCompat() {
-        setUpDisplaySizeWithApp(2200, 2280);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        final DisplayContent dc = mActivity.mDisplayContent;
+        final DisplayContent dc = mDisplayContent;
+        resizeDisplay(dc, 2200, 2280);
+        dc.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
         final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
                 "navbar");
@@ -4132,11 +4077,10 @@
             dc.sendNewConfiguration();
         }
 
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(dc, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
-                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
-                .build();
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
         // To force config to update again but with the same landscape orientation.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
 
@@ -4174,13 +4118,10 @@
                 .setCutout(0, 100, 0, 150)
                 .build();
 
-        setUpApp(display);
-
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setMaxAspectRatio(2.1f)
-                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
-                .build();
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
 
         // The activity height is 2100 and the display's app bounds height is 2050, so the activity
         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
@@ -4196,13 +4137,10 @@
                 .setCutout(100, 0, 150, 0)
                 .build();
 
-        setUpApp(display);
-
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setMaxAspectRatio(2.1f)
-                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
-                .build();
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
         // The activity width is 2100 and the display's app bounds width is 2250, so the activity
         // can be aligned inside parentAppBounds
         assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000));
@@ -4216,12 +4154,10 @@
                 .setCutout(100, 0, 150, 0)
                 .build();
 
-        setUpApp(display);
-        final ActivityRecord activity = getActivityBuilderOnSameTask()
+        final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
                 .setMaxAspectRatio(2.1f)
-                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
-                .build();
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
         // The activity width is 2100 and the display's app bounds width is 2050, so the activity
         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
         assertEquals(activity.getBounds(), display.getBounds());
@@ -4883,7 +4819,7 @@
 
 
     @Test
-    @EnableCompatChanges({ActivityRecord.UNIVERSAL_RESIZABLE_BY_DEFAULT})
+    @EnableCompatChanges({ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT})
     public void testUniversalResizeableByDefault() {
         mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT);
         mDisplayContent.setIgnoreOrientationRequest(false);
@@ -5032,17 +4968,13 @@
      */
     private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode,
             @ScreenOrientation int screenOrientation) {
-        return getActivityBuilderOnSameTask()
+        return getActivityBuilderWithoutTask().setTask(mTask)
                 .setResizeMode(resizeMode)
                 .setSupportsSizeChanges(supportsSizeChanges)
                 .setScreenOrientation(screenOrientation)
                 .build();
     }
 
-    private ActivityBuilder getActivityBuilderOnSameTask() {
-        return getActivityBuilderWithoutTask().setTask(mTask);
-    }
-
     private ActivityBuilder getActivityBuilderWithoutTask() {
         return new ActivityBuilder(mAtm)
                 .setComponent(ComponentName.createRelative(mContext,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 5b3fd53..7196acc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2933,6 +2933,11 @@
 
         controller.requestStartTransition(transit, task, null, null);
         player.start();
+        // always include config-at-end activity since it is considered "independent" due to
+        // changing at a different time.
+        assertTrue(player.mLastReady.getChanges().stream()
+                .anyMatch((change -> change.getActivityComponent() != null
+                        && (change.getFlags() & TransitionInfo.FLAG_CONFIG_AT_END) != 0)));
         assertTrue(activity.isConfigurationDispatchPaused());
         player.finish();
         assertFalse(activity.isConfigurationDispatchPaused());
@@ -2962,6 +2967,11 @@
 
         controller.requestStartTransition(transit, task, null, null);
         player.start();
+        // always include config-at-end activity since it is considered "independent" due to
+        // changing at a different time.
+        assertTrue(player.mLastReady.getChanges().stream()
+                .anyMatch((change -> change.getActivityComponent() != null
+                        && (change.getFlags() & TransitionInfo.FLAG_CONFIG_AT_END) != 0)));
         assertTrue(activity.isConfigurationDispatchPaused());
         player.finish();
         assertFalse(activity.isConfigurationDispatchPaused());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 6111a65..8bbba1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -94,6 +94,7 @@
 import android.view.ContentRecordingSession;
 import android.view.IWindow;
 import android.view.InputChannel;
+import android.view.InputDevice;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.Surface;
@@ -1275,6 +1276,48 @@
     }
 
     @Test
+    public void testInputDeviceNotifyConfigurationChanged() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_FILTER_IRRELEVANT_INPUT_DEVICE_CHANGE);
+        spyOn(mDisplayContent);
+        doReturn(false).when(mDisplayContent).sendNewConfiguration();
+        final InputDevice deviceA = mock(InputDevice.class);
+        final InputDevice deviceB = mock(InputDevice.class);
+        doReturn("deviceA").when(deviceA).getDescriptor();
+        doReturn("deviceB").when(deviceB).getDescriptor();
+        final InputDevice[] devices1 = { deviceA };
+        final InputDevice[] devices2 = { deviceB, deviceA };
+        final Runnable verifySendNewConfiguration = () -> {
+            clearInvocations(mDisplayContent);
+            mWm.mInputManagerCallback.notifyConfigurationChanged();
+            verify(mDisplayContent).sendNewConfiguration();
+        };
+        doReturn(devices1).when(mWm.mInputManager).getInputDevices();
+        verifySendNewConfiguration.run();
+
+        doReturn(devices2).when(mWm.mInputManager).getInputDevices();
+        verifySendNewConfiguration.run();
+
+        doReturn(true).when(deviceB).isEnabled();
+        verifySendNewConfiguration.run();
+
+        doReturn(true).when(deviceA).isExternal();
+        verifySendNewConfiguration.run();
+
+        doReturn(1).when(deviceA).getSources();
+        verifySendNewConfiguration.run();
+
+        doReturn(1).when(deviceA).getAssociatedDisplayId();
+        verifySendNewConfiguration.run();
+
+        doReturn(1).when(deviceA).getKeyboardType();
+        verifySendNewConfiguration.run();
+
+        clearInvocations(mDisplayContent);
+        mWm.mInputManagerCallback.notifyConfigurationChanged();
+        verify(mDisplayContent, never()).sendNewConfiguration();
+    }
+
+    @Test
     public void testReportSystemGestureExclusionChanged_invalidWindow() {
         final Session session = mock(Session.class);
         final IWindow window = mock(IWindow.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index f56825f..42e31de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.app.ActivityManager.START_CANCELED;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -28,6 +29,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
@@ -37,6 +40,7 @@
 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.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -61,6 +65,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.quality.Strictness.LENIENT;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -69,6 +74,7 @@
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IRequestFinishCallback;
 import android.app.PictureInPictureParams;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
@@ -87,6 +93,7 @@
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskOrganizer;
 import android.window.IWindowContainerTransactionCallback;
+import android.window.RemoteTransition;
 import android.window.StartingWindowInfo;
 import android.window.StartingWindowRemovalInfo;
 import android.window.TaskAppearedInfo;
@@ -102,6 +109,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.MockitoSession;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -638,6 +646,66 @@
     }
 
     @Test
+    public void testStartActivityInTaskFragment_checkCallerPermission() {
+        final ActivityStartController activityStartController =
+                mWm.mAtmService.getActivityStartController();
+        spyOn(activityStartController);
+        final ArgumentCaptor<SafeActivityOptions> activityOptionsCaptor =
+                ArgumentCaptor.forClass(SafeActivityOptions.class);
+
+        final int uid = Binder.getCallingUid();
+        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+        final WindowContainerTransaction t = new WindowContainerTransaction();
+        final TaskFragmentOrganizer organizer =
+                createTaskFragmentOrganizer(t, true /* isSystemOrganizer */);
+        final IBinder token = new Binder();
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(rootTask)
+                .setFragmentToken(token)
+                .setOrganizer(organizer)
+                .createActivityCount(1)
+                .build();
+        mWm.mAtmService.mWindowOrganizerController.mLaunchTaskFragments.put(token, taskFragment);
+        final ActivityRecord ownerActivity = taskFragment.getTopMostActivity();
+
+        // Start Activity in TaskFragment with remote transition.
+        final RemoteTransition transition = mock(RemoteTransition.class);
+        final ActivityOptions options = ActivityOptions.makeRemoteTransition(transition);
+        final Intent intent = new Intent();
+        t.startActivityInTaskFragment(token, ownerActivity.token, intent, options.toBundle());
+        mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+                t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN,
+                false /* shouldApplyIndependently */, null /* remoteTransition */);
+
+        // Get the ActivityOptions.
+        verify(activityStartController).startActivityInTaskFragment(
+                eq(taskFragment), eq(intent), activityOptionsCaptor.capture(),
+                eq(ownerActivity.token), eq(uid), anyInt(), any());
+        final SafeActivityOptions safeActivityOptions = activityOptionsCaptor.getValue();
+
+        final MockitoSession session =
+                mockitoSession().strictness(LENIENT).spyStatic(ActivityTaskManagerService.class)
+                        .startMocking();
+        try {
+            // Without the CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission, start activity with
+            // remote transition is not allowed.
+            doReturn(PERMISSION_DENIED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS), anyInt(), eq(uid)));
+            assertThrows(SecurityException.class,
+                    () -> safeActivityOptions.getOptions(mWm.mAtmService.mTaskSupervisor));
+
+            // With the CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission, start activity with
+            // remote transition is allowed.
+            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS), anyInt(), eq(uid)));
+            safeActivityOptions.getOptions(mWm.mAtmService.mTaskSupervisor);
+        } finally {
+            session.finishMocking();
+        }
+    }
+
+    @Test
     public void testTaskFragmentChangeHidden_throwsWhenNotSystemOrganizer() {
         // Non-system organizers are not allow to update the hidden state.
         testTaskFragmentChangesWithoutSystemOrganizerThrowException(
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index f825f55..678c7ac 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -3,7 +3,6 @@
 
 mwachens@google.com
 varunshah@google.com
-yamasani@google.com
 guanxin@google.com
 
 per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
diff --git a/services/usb/java/com/android/server/usb/UsbManagerInternal.java b/services/usb/java/com/android/server/usb/UsbManagerInternal.java
index c97df6b..31c5986 100644
--- a/services/usb/java/com/android/server/usb/UsbManagerInternal.java
+++ b/services/usb/java/com/android/server/usb/UsbManagerInternal.java
@@ -34,9 +34,11 @@
 public abstract class UsbManagerInternal {
 
   public static final int OS_USB_DISABLE_REASON_AAPM = 0;
+  public static final int OS_USB_DISABLE_REASON_LOCKDOWN_MODE = 1;
 
   @Retention(RetentionPolicy.SOURCE)
-  @IntDef(value = {OS_USB_DISABLE_REASON_AAPM})
+  @IntDef(value = {OS_USB_DISABLE_REASON_AAPM,
+    OS_USB_DISABLE_REASON_LOCKDOWN_MODE})
   public @interface OsUsbDisableReason {
   }
 
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index ba9dff6..ec4f7e1 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -1527,8 +1527,11 @@
             }
             mLockdownModeStatus = lockDownTriggeredByUser;
             for (UsbPort port: mPortManager.getPorts()) {
-                enableUsbData(port.getId(), !lockDownTriggeredByUser, STRONG_AUTH_OPERATION_ID,
-                        new IUsbOperationInternal.Default());
+                enableUsbDataInternal(port.getId(), !lockDownTriggeredByUser,
+                    STRONG_AUTH_OPERATION_ID,
+                    new IUsbOperationInternal.Default(),
+                    UsbManagerInternal.OS_USB_DISABLE_REASON_LOCKDOWN_MODE,
+                    true);
             }
         }
     }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 1a42e80..19a6ddc 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -504,6 +504,11 @@
             pw.println("\n##Sound Model Stats dump:\n");
             mSoundModelStatTracker.dump(pw);
         }
+
+        @Override
+        protected void onUnhandledException(int code, int flags, Exception e) {
+            Slog.wtf(TAG, "Unhandled exception code: " + code, e);
+        }
     }
 
     class SoundTriggerSessionStub extends ISoundTriggerSession.Stub {
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index e2fb601..ad0d4f4 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -16,7 +16,6 @@
 
 package android.telecom.Logging;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -24,8 +23,13 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.telecom.flags.Flags;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Stores information about a thread's point of entry into that should persist until that thread
@@ -55,7 +59,7 @@
      * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
      * if the Session is canceled.
      */
-    public static final int UNDEFINED = -1;
+    public static final long UNDEFINED = -1;
 
     public static class Info implements Parcelable {
         public final String sessionId;
@@ -129,46 +133,39 @@
         }
     }
 
-    private String mSessionId;
-    private String mShortMethodName;
+    private final String mSessionId;
+    private volatile String mShortMethodName;
     private long mExecutionStartTimeMs;
     private long mExecutionEndTimeMs = UNDEFINED;
-    private Session mParentSession;
-    private ArrayList<Session> mChildSessions;
+    private volatile Session mParentSession;
+    private final ArrayList<Session> mChildSessions = new ArrayList<>(5);
     private boolean mIsCompleted = false;
-    private boolean mIsExternal = false;
-    private int mChildCounter = 0;
+    private final boolean mIsExternal;
+    private final AtomicInteger mChildCounter = new AtomicInteger(0);
     // True if this is a subsession that has been started from the same thread as the parent
     // session. This can happen if Log.startSession(...) is called multiple times on the same
     // thread in the case of one Telecom entry point method calling another entry point method.
     // In this case, we can just make this subsession "invisible," but still keep track of it so
     // that the Log.endSession() calls match up.
-    private boolean mIsStartedFromActiveSession = false;
+    private final boolean mIsStartedFromActiveSession;
     // Optionally provided info about the method/class/component that started the session in order
     // to make Logging easier. This info will be provided in parentheses along with the session.
-    private String mOwnerInfo;
+    private final String mOwnerInfo;
     // Cache Full Method path so that recursive population of the full method path only needs to
     // be calculated once.
-    private String mFullMethodPathCache;
+    private volatile String mFullMethodPathCache;
 
     public Session(String sessionId, String shortMethodName, long startTimeMs,
-            boolean isStartedFromActiveSession, String ownerInfo) {
-        setSessionId(sessionId);
+            boolean isStartedFromActiveSession, boolean isExternal, String ownerInfo) {
+        mSessionId = (sessionId != null) ? sessionId : "???";
         setShortMethodName(shortMethodName);
         mExecutionStartTimeMs = startTimeMs;
         mParentSession = null;
-        mChildSessions = new ArrayList<>(5);
         mIsStartedFromActiveSession = isStartedFromActiveSession;
+        mIsExternal = isExternal;
         mOwnerInfo = ownerInfo;
     }
 
-    public void setSessionId(@NonNull String sessionId) {
-        if (sessionId == null) {
-            mSessionId = "?";
-        }
-        mSessionId = sessionId;
-    }
-
     public String getShortMethodName() {
         return mShortMethodName;
     }
@@ -180,10 +177,6 @@
         mShortMethodName = shortMethodName;
     }
 
-    public void setIsExternal(boolean isExternal) {
-        mIsExternal = isExternal;
-    }
-
     public boolean isExternal() {
         return mIsExternal;
     }
@@ -193,13 +186,15 @@
     }
 
     public void addChild(Session childSession) {
-        if (childSession != null) {
+        if (childSession == null) return;
+        synchronized (mChildSessions) {
             mChildSessions.add(childSession);
         }
     }
 
     public void removeChild(Session child) {
-        if (child != null) {
+        if (child == null) return;
+        synchronized (mChildSessions) {
             mChildSessions.remove(child);
         }
     }
@@ -217,7 +212,9 @@
     }
 
     public ArrayList<Session> getChildSessions() {
-        return mChildSessions;
+        synchronized (mChildSessions) {
+            return new ArrayList<>(mChildSessions);
+        }
     }
 
     public boolean isSessionCompleted() {
@@ -259,17 +256,41 @@
         return mExecutionEndTimeMs - mExecutionStartTimeMs;
     }
 
-    public synchronized String getNextChildId() {
-        return String.valueOf(mChildCounter++);
+    public String getNextChildId() {
+        return String.valueOf(mChildCounter.getAndIncrement());
     }
 
-    // Builds full session id recursively
+    // Builds full session ID, which incliudes the optional external indicators (E),
+    // base session ID, and the optional sub-session IDs (_X): @[E-]...[ID][_X][_Y]...
     private String getFullSessionId() {
-        return getFullSessionId(0);
+        if (!Flags.endSessionImprovements()) return getFullSessionIdRecursive(0);
+        int currParentCount = 0;
+        StringBuilder id = new StringBuilder();
+        Session currSession = this;
+        while (currSession != null) {
+            Session parentSession = currSession.getParentSession();
+            if (parentSession != null) {
+                if (currParentCount >= SESSION_RECURSION_LIMIT) {
+                    id.insert(0, getSessionId());
+                    id.insert(0, TRUNCATE_STRING);
+                    android.util.Slog.w(LOG_TAG, "getFullSessionId: Hit iteration limit!");
+                    return id.toString();
+                }
+                if (Log.VERBOSE) {
+                    id.insert(0, currSession.getSessionId());
+                    id.insert(0, SESSION_SEPARATION_CHAR_CHILD);
+                }
+            } else {
+                id.insert(0, currSession.getSessionId());
+            }
+            currSession = parentSession;
+            currParentCount++;
+        }
+        return id.toString();
     }
 
     // keep track of calls and bail if we hit the recursion limit
-    private String getFullSessionId(int parentCount) {
+    private String getFullSessionIdRecursive(int parentCount) {
         if (parentCount >= SESSION_RECURSION_LIMIT) {
             // Don't use Telecom's Log.w here or it will cause infinite recursion because it will
             // try to add session information to this logging statement, which will cause it to hit
@@ -286,12 +307,12 @@
             return mSessionId;
         } else {
             if (Log.VERBOSE) {
-                return parentSession.getFullSessionId(parentCount + 1)
+                return parentSession.getFullSessionIdRecursive(parentCount + 1)
                         // Append "_X" to subsession to show subsession designation.
                         + SESSION_SEPARATION_CHAR_CHILD + mSessionId;
             } else {
                 // Only worry about the base ID at the top of the tree.
-                return parentSession.getFullSessionId(parentCount + 1);
+                return parentSession.getFullSessionIdRecursive(parentCount + 1);
             }
 
         }
@@ -300,16 +321,18 @@
     private Session getRootSession(String callingMethod) {
         int currParentCount = 0;
         Session topNode = this;
-        while (topNode.getParentSession() != null) {
+        Session parentNode = topNode.getParentSession();
+        while (parentNode != null) {
             if (currParentCount >= SESSION_RECURSION_LIMIT) {
                 // Don't use Telecom's Log.w here or it will cause infinite recursion because it
                 // will try to add session information to this logging statement, which will cause
                 // it to hit this condition again and so on...
-                android.util.Slog.w(LOG_TAG, "getRootSession: Hit recursion limit from "
+                android.util.Slog.w(LOG_TAG, "getRootSession: Hit iteration limit from "
                         + callingMethod);
                 break;
             }
-            topNode = topNode.getParentSession();
+            topNode = parentNode;
+            parentNode = topNode.getParentSession();
             currParentCount++;
         }
         return topNode;
@@ -320,14 +343,40 @@
         return getRootSession("printFullSessionTree").printSessionTree();
     }
 
-    // Recursively move down session tree using DFS, but print out each node when it is reached.
     private String printSessionTree() {
         StringBuilder sb = new StringBuilder();
-        printSessionTree(0, sb, 0);
+        if (!Flags.endSessionImprovements()) {
+            printSessionTreeRecursive(0, sb, 0);
+            return sb.toString();
+        }
+        int depth = 0;
+        ArrayDeque<Session> deque = new ArrayDeque<>();
+        deque.add(this);
+        while (!deque.isEmpty()) {
+            Session node = deque.pollFirst();
+            sb.append("\t".repeat(depth));
+            sb.append(node.toString());
+            sb.append("\n");
+            if (depth >= SESSION_RECURSION_LIMIT) {
+                sb.append(TRUNCATE_STRING);
+                depth -= 1;
+                continue;
+            }
+            List<Session> childSessions = node.getChildSessions().reversed();
+            if (!childSessions.isEmpty()) {
+                depth += 1;
+                for (Session child : childSessions) {
+                    deque.addFirst(child);
+                }
+            } else {
+                depth -= 1;
+            }
+        }
         return sb.toString();
     }
 
-    private void printSessionTree(int tabI, StringBuilder sb, int currChildCount) {
+    // Recursively move down session tree using DFS, but print out each node when it is reached.
+    private void printSessionTreeRecursive(int tabI, StringBuilder sb, int currChildCount) {
         // Prevent infinite recursion.
         if (currChildCount >= SESSION_RECURSION_LIMIT) {
             // Don't use Telecom's Log.w here or it will cause infinite recursion because it will
@@ -343,26 +392,85 @@
             for (int i = 0; i <= tabI; i++) {
                 sb.append("\t");
             }
-            child.printSessionTree(tabI + 1, sb, currChildCount + 1);
+            child.printSessionTreeRecursive(tabI + 1, sb, currChildCount + 1);
         }
     }
 
-    // Recursively concatenate mShortMethodName with the parent Sessions to create full method
-    // path. if truncatePath is set to true, all other external sessions (except for the most
-    // recent) will be truncated to "..."
+    //
+
+    /**
+     * Concatenate the short method name with the parent Sessions to create full method path.
+     * @param truncatePath if truncatePath is set to true, all other external sessions (except for
+     *                     the most recent) will be truncated to "..."
+     * @return The full method path associated with this Session.
+     */
+    @VisibleForTesting
     public String getFullMethodPath(boolean truncatePath) {
         StringBuilder sb = new StringBuilder();
-        getFullMethodPath(sb, truncatePath, 0);
+        if (!Flags.endSessionImprovements()) {
+            getFullMethodPathRecursive(sb, truncatePath, 0);
+            return sb.toString();
+        }
+        // Check to see if the session has been renamed yet. If it has not, then the session
+        // has not been continued.
+        Session parentSession = getParentSession();
+        boolean isSessionStarted = parentSession == null
+                || !getShortMethodName().equals(parentSession.getShortMethodName());
+        int depth = 0;
+        Session currSession = this;
+        while (currSession != null) {
+            String cache = currSession.mFullMethodPathCache;
+            // Return cached value for method path. When returning the truncated path, recalculate
+            // the full path without using the cached value.
+            if (!TextUtils.isEmpty(cache) && !truncatePath) {
+                sb.insert(0, cache);
+                return sb.toString();
+            }
+
+            parentSession = currSession.getParentSession();
+            // Encapsulate the external session's method name so it is obvious what part of the
+            // session is external or truncate it if we do not want the entire history.
+            if (currSession.isExternal()) {
+                if (truncatePath) {
+                    sb.insert(0, TRUNCATE_STRING);
+                } else {
+                    sb.insert(0, ")");
+                    sb.insert(0, currSession.getShortMethodName());
+                    sb.insert(0, "(");
+                }
+            } else {
+                sb.insert(0, currSession.getShortMethodName());
+            }
+            if (parentSession != null) {
+                sb.insert(0, SUBSESSION_SEPARATION_CHAR);
+            }
+
+            if (depth >= SESSION_RECURSION_LIMIT) {
+                // Don't use Telecom's Log.w here or it will cause infinite recursion because it
+                // will try to add session information to this logging statement, which will cause
+                // it to hit this condition again and so on...
+                android.util.Slog.w(LOG_TAG, "getFullMethodPath: Hit iteration limit!");
+                sb.insert(0, TRUNCATE_STRING);
+                return sb.toString();
+            }
+            currSession = parentSession;
+            depth++;
+        }
+        if (isSessionStarted && !truncatePath) {
+            // Cache the full method path for this node so that we do not need to calculate it
+            // again in the future.
+            mFullMethodPathCache = sb.toString();
+        }
         return sb.toString();
     }
 
-    private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath,
+    private synchronized void getFullMethodPathRecursive(StringBuilder sb, boolean truncatePath,
             int parentCount) {
         if (parentCount >= SESSION_RECURSION_LIMIT) {
             // Don't use Telecom's Log.w here or it will cause infinite recursion because it will
             // try to add session information to this logging statement, which will cause it to hit
             // this condition again and so on...
-            android.util.Slog.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!");
+            android.util.Slog.w(LOG_TAG, "getFullMethodPathRecursive: Hit recursion limit!");
             sb.append(TRUNCATE_STRING);
             return;
         }
@@ -378,7 +486,7 @@
             // Check to see if the session has been renamed yet. If it has not, then the session
             // has not been continued.
             isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
-            parentSession.getFullMethodPath(sb, truncatePath, parentCount + 1);
+            parentSession.getFullMethodPathRecursive(sb, truncatePath, parentCount + 1);
             sb.append(SUBSESSION_SEPARATION_CHAR);
         }
         // Encapsulate the external session's method name so it is obvious what part of the session
@@ -409,14 +517,14 @@
 
     @Override
     public int hashCode() {
-        int result = mSessionId != null ? mSessionId.hashCode() : 0;
-        result = 31 * result + (mShortMethodName != null ? mShortMethodName.hashCode() : 0);
-        result = 31 * result + (int) (mExecutionStartTimeMs ^ (mExecutionStartTimeMs >>> 32));
-        result = 31 * result + (int) (mExecutionEndTimeMs ^ (mExecutionEndTimeMs >>> 32));
+        int result = mSessionId.hashCode();
+        result = 31 * result + mShortMethodName.hashCode();
+        result = 31 * result + Long.hashCode(mExecutionStartTimeMs);
+        result = 31 * result + Long.hashCode(mExecutionEndTimeMs);
         result = 31 * result + (mParentSession != null ? mParentSession.hashCode() : 0);
-        result = 31 * result + (mChildSessions != null ? mChildSessions.hashCode() : 0);
+        result = 31 * result + mChildSessions.hashCode();
         result = 31 * result + (mIsCompleted ? 1 : 0);
-        result = 31 * result + mChildCounter;
+        result = 31 * result + mChildCounter.hashCode();
         result = 31 * result + (mIsStartedFromActiveSession ? 1 : 0);
         result = 31 * result + (mOwnerInfo != null ? mOwnerInfo.hashCode() : 0);
         return result;
@@ -432,23 +540,13 @@
         if (mExecutionStartTimeMs != session.mExecutionStartTimeMs) return false;
         if (mExecutionEndTimeMs != session.mExecutionEndTimeMs) return false;
         if (mIsCompleted != session.mIsCompleted) return false;
-        if (mChildCounter != session.mChildCounter) return false;
+        if (!(mChildCounter.get() == session.mChildCounter.get())) return false;
         if (mIsStartedFromActiveSession != session.mIsStartedFromActiveSession) return false;
-        if (mSessionId != null ?
-                !mSessionId.equals(session.mSessionId) : session.mSessionId != null)
-            return false;
-        if (mShortMethodName != null ? !mShortMethodName.equals(session.mShortMethodName)
-                : session.mShortMethodName != null)
-            return false;
-        if (mParentSession != null ? !mParentSession.equals(session.mParentSession)
-                : session.mParentSession != null)
-            return false;
-        if (mChildSessions != null ? !mChildSessions.equals(session.mChildSessions)
-                : session.mChildSessions != null)
-            return false;
-        return mOwnerInfo != null ? mOwnerInfo.equals(session.mOwnerInfo)
-                : session.mOwnerInfo == null;
-
+        if (!Objects.equals(mSessionId, session.mSessionId)) return false;
+        if (!Objects.equals(mShortMethodName, session.mShortMethodName)) return false;
+        if (!Objects.equals(mParentSession, session.mParentSession)) return false;
+        if (!Objects.equals(mChildSessions, session.mChildSessions)) return false;
+        return Objects.equals(mOwnerInfo, session.mOwnerInfo);
     }
 
     @Override
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 9d17219..00e344c 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -27,6 +27,7 @@
 import android.util.Base64;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.telecom.flags.Flags;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -36,10 +37,16 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * TODO: Create better Sessions Documentation
+ * SessionManager manages the active sessions in a HashMap, which maps the active thread(s) to the
+ * associated {@link Session}s.
+ * <p>
+ * Note: Sessions assume that session structure modification is synchronized on this object - only
+ * one thread can modify the structure of any Session at one time. Printing the current session to
+ * the log is not synchronized because we should not clean up a session chain while printing from
+ * another Thread. Either the Session chain is still active and can not be cleaned up yet, or the
+ * Session chain has ended and we are cleaning up.
  * @hide
  */
-
 public class SessionManager {
 
     // Currently using 3 letters, So don't exceed 64^3
@@ -54,11 +61,11 @@
     private Context mContext;
 
     @VisibleForTesting
-    public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100);
+    public final ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(64);
     @VisibleForTesting
     public java.lang.Runnable mCleanStaleSessions = () ->
             cleanupStaleSessions(getSessionCleanupTimeoutMs());
-    private Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
+    private final Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
 
     // Overridden in LogTest to skip query to ContentProvider
     private interface ISessionCleanupTimeoutMs {
@@ -83,7 +90,7 @@
     };
 
     // Usage is synchronized on this class.
-    private List<ISessionListener> mSessionListeners = new ArrayList<>();
+    private final List<ISessionListener> mSessionListeners = new ArrayList<>();
 
     public interface ISessionListener {
         /**
@@ -110,10 +117,19 @@
     }
 
     private synchronized void resetStaleSessionTimer() {
-        mSessionCleanupHandler.removeCallbacksAndMessages(null);
-        // Will be null in Log Testing
-        if (mCleanStaleSessions != null) {
-            mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs());
+        if (!Flags.endSessionImprovements()) {
+            mSessionCleanupHandler.removeCallbacksAndMessages(null);
+            // Will be null in Log Testing
+            if (mCleanStaleSessions != null) {
+                mSessionCleanupHandler.postDelayed(mCleanStaleSessions,
+                        getSessionCleanupTimeoutMs());
+            }
+        } else {
+            if (mCleanStaleSessions != null
+                    && !mSessionCleanupHandler.hasCallbacks(mCleanStaleSessions)) {
+                mSessionCleanupHandler.postDelayed(mCleanStaleSessions,
+                        getSessionCleanupTimeoutMs());
+            }
         }
     }
 
@@ -147,13 +163,11 @@
             Session childSession = createSubsession(true);
             continueSession(childSession, shortMethodName);
             return;
-        } else {
-            // Only Log that we are starting the parent session.
-            Log.d(LOGGING_TAG, Session.START_SESSION);
         }
         Session newSession = new Session(getNextSessionID(), shortMethodName,
-                System.currentTimeMillis(), false, callerIdentification);
+                System.currentTimeMillis(), false, false, callerIdentification);
         mSessionMapper.put(threadId, newSession);
+        Log.d(LOGGING_TAG, Session.START_SESSION);
     }
 
     /**
@@ -179,17 +193,16 @@
         }
 
         // Create Session from Info and add to the sessionMapper under this ID.
-        Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION);
         Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId,
-                sessionInfo.methodPath, System.currentTimeMillis(),
-                false /*isStartedFromActiveSession*/, sessionInfo.ownerInfo);
-        externalSession.setIsExternal(true);
+                sessionInfo.methodPath, System.currentTimeMillis(), false, true,
+                sessionInfo.ownerInfo);
         // Mark the external session as already completed, since we have no way of knowing when
         // the external session actually has completed.
         externalSession.markSessionCompleted(Session.UNDEFINED);
         // Track the external session with the SessionMapper so that we can create and continue
         // an active subsession based on it.
         mSessionMapper.put(threadId, externalSession);
+        Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION);
         // Create a subsession from this external Session parent node
         Session childSession = createSubsession();
         continueSession(childSession, shortMethodName);
@@ -226,13 +239,12 @@
         // Start execution time of the session will be overwritten in continueSession(...).
         Session newSubsession = new Session(threadSession.getNextChildId(),
                 threadSession.getShortMethodName(), System.currentTimeMillis(),
-                isStartedFromActiveSession, threadSession.getOwnerInfo());
+                isStartedFromActiveSession, false, threadSession.getOwnerInfo());
         threadSession.addChild(newSubsession);
         newSubsession.setParentSession(threadSession);
 
         if (!isStartedFromActiveSession) {
-            Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
-                    newSubsession.toString());
+            Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION);
         } else {
             Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
                     " (Invisible subsession)");
@@ -273,7 +285,7 @@
         }
 
         subsession.markSessionCompleted(Session.UNDEFINED);
-        endParentSessions(subsession);
+        cleanupSessionTreeAndNotify(subsession);
     }
 
     /**
@@ -328,7 +340,7 @@
         // Remove after completed so that reference still exists for logging the end events
         Session parentSession = completedSession.getParentSession();
         mSessionMapper.remove(threadId);
-        endParentSessions(completedSession);
+        cleanupSessionTreeAndNotify(completedSession);
         // If this subsession was started from a parent session using Log.startSession, return the
         // ThreadID back to the parent after completion.
         if (parentSession != null && !parentSession.isSessionCompleted() &&
@@ -337,8 +349,49 @@
         }
     }
 
+    /**
+     * Move up the session tree and remove completed sessions until we either hit a session that was
+     * not completed yet or we reach the root node. Once we reach the root node, we will report the
+     * session times to session complete listeners.
+     * @param session The Session to clean up.
+     */
+    private void cleanupSessionTreeAndNotify(Session session) {
+        if (session == null) return;
+        if (!Flags.endSessionImprovements()) {
+            endParentSessionsRecursive(session);
+            return;
+        }
+        Session currSession = session;
+        // Traverse upwards and unlink until we either hit the root node or a node that isn't
+        // complete yet.
+        while (currSession != null) {
+            if (!currSession.isSessionCompleted() || !currSession.getChildSessions().isEmpty()) {
+                // We will return once the active session is completed.
+                return;
+            }
+            Session parentSession = currSession.getParentSession();
+            currSession.setParentSession(null);
+            // The session is either complete when we have reached the top node or we have reached
+            // the node where the parent is external. We only want to report the time it took to
+            // complete the local session, so for external nodes, report finished when the sub-node
+            // completes.
+            boolean reportSessionComplete =
+                    (parentSession == null && !currSession.isExternal())
+                            || (parentSession != null && parentSession.isExternal());
+            if (parentSession != null) parentSession.removeChild(currSession);
+            if (reportSessionComplete) {
+                long fullSessionTimeMs = System.currentTimeMillis()
+                        - currSession.getExecutionStartTimeMilliseconds();
+                Log.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
+                        + " ms): " + currSession);
+                notifySessionCompleteListeners(currSession.getShortMethodName(), fullSessionTimeMs);
+            }
+            currSession = parentSession;
+        }
+    }
+
     // Recursively deletes all complete parent sessions of the current subsession if it is a leaf.
-    private void endParentSessions(Session subsession) {
+    private void endParentSessionsRecursive(Session subsession) {
         // Session is not completed or not currently a leaf, so we can not remove because a child is
         // still running
         if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) {
@@ -355,7 +408,7 @@
                         System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
                 notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
             }
-            endParentSessions(parentSession);
+            endParentSessionsRecursive(parentSession);
         } else {
             // All of the subsessions have been completed and it is time to report on the full
             // running time of the session.
@@ -370,8 +423,10 @@
     }
 
     private void notifySessionCompleteListeners(String methodName, long sessionTimeMs) {
-        for (ISessionListener l : mSessionListeners) {
-            l.sessionComplete(methodName, sessionTimeMs);
+        synchronized (mSessionListeners) {
+            for (ISessionListener l : mSessionListeners) {
+                l.sessionComplete(methodName, sessionTimeMs);
+            }
         }
     }
 
@@ -380,8 +435,8 @@
         return currentSession != null ? currentSession.toString() : "";
     }
 
-    public synchronized void registerSessionListener(ISessionListener l) {
-        if (l != null) {
+    public void registerSessionListener(ISessionListener l) {
+        synchronized (mSessionListeners) {
             mSessionListeners.add(l);
         }
     }
@@ -425,25 +480,30 @@
 
     @VisibleForTesting
     public synchronized void cleanupStaleSessions(long timeoutMs) {
-        String logMessage = "Stale Sessions Cleaned:\n";
+        StringBuilder logMessage = new StringBuilder("Stale Sessions Cleaned:");
         boolean isSessionsStale = false;
         long currentTimeMs = System.currentTimeMillis();
         // Remove references that are in the Session Mapper (causing GC to occur) on
-        // sessions that are lasting longer than LOGGING_SESSION_TIMEOUT_MS.
+        // sessions that are lasting longer than DEFAULT_SESSION_TIMEOUT_MS.
         // If this occurs, then there is most likely a Session active that never had
         // Log.endSession called on it.
         for (Iterator<ConcurrentHashMap.Entry<Integer, Session>> it =
              mSessionMapper.entrySet().iterator(); it.hasNext(); ) {
             ConcurrentHashMap.Entry<Integer, Session> entry = it.next();
             Session session = entry.getValue();
-            if (currentTimeMs - session.getExecutionStartTimeMilliseconds() > timeoutMs) {
+            long runTime = currentTimeMs - session.getExecutionStartTimeMilliseconds();
+            if (runTime > timeoutMs) {
                 it.remove();
-                logMessage += session.printFullSessionTree() + "\n";
+                logMessage.append("\n");
+                logMessage.append("[");
+                logMessage.append(runTime);
+                logMessage.append("ms] ");
+                logMessage.append(session.printFullSessionTree());
                 isSessionsStale = true;
             }
         }
         if (isSessionsStale) {
-            Log.w(LOGGING_TAG, logMessage);
+            Log.w(LOGGING_TAG, logMessage.toString());
         } else {
             Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
         }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 47f6764..02999c8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -253,7 +253,6 @@
      *
      * The default value is true.
      */
-    @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
     public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL =
             "additional_settings_caller_id_visibility_bool";
 
@@ -263,7 +262,6 @@
      *
      * The default value is true.
      */
-    @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
     public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL =
             "additional_settings_call_waiting_visibility_bool";
 
@@ -10558,7 +10556,6 @@
      * @see SubscriptionInfo#getServiceCapabilities()
      * @see SubscriptionManager.OnSubscriptionsChangedListener
      */
-    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY =
             "cellular_service_capabilities_int_array";
    /**
@@ -11110,7 +11107,7 @@
         sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
         sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
         sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
-        sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, true);
+        sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false);
         sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL, false);
         /* Default value is 1 hour. */
         sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 845449e..02030a1 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -19,6 +19,7 @@
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -32,6 +33,7 @@
 import android.os.Build;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.Sms7BitEncodingTranslator;
@@ -1208,9 +1210,9 @@
     /**
      * Returns the recipient address(receiver) of this SMS message in String form or null if
      * unavailable.
-     * {@hide}
      */
     @Nullable
+    @FlaggedApi(Flags.FLAG_SUPPORT_SMS_OVER_IMS_APIS)
     public String getRecipientAddress() {
         return mWrappedSmsMessage.getRecipientAddress();
     }
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 1089602..d164c88 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -951,7 +951,6 @@
      * @see SubscriptionManager#SERVICE_CAPABILITY_DATA
      */
     @NonNull
-    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() {
         return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities);
     }
@@ -1829,7 +1828,6 @@
          * @throws IllegalArgumentException when any capability is not supported.
          */
         @NonNull
-        @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
         public Builder setServiceCapabilities(
                 @NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) {
             int combinedCapabilities = 0;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 6faef7e..377e5f2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1422,7 +1422,6 @@
      *
      * @see TelephonyManager#isDeviceVoiceCapable()
      */
-    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public static final int SERVICE_CAPABILITY_VOICE = 1;
 
     /**
@@ -1440,13 +1439,11 @@
      *
      * @see TelephonyManager#isDeviceSmsCapable()
      */
-    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public static final int SERVICE_CAPABILITY_SMS = 2;
 
     /**
      * Represents a value indicating the data calling capabilities of a subscription.
      */
-    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public static final int SERVICE_CAPABILITY_DATA = 3;
 
     /**
@@ -3474,14 +3471,62 @@
     @SystemApi
     public boolean canManageSubscription(@NonNull SubscriptionInfo info,
             @NonNull String packageName) {
+        if (Flags.hsumPackageManager()) {
+            return canManageSubscriptionAsUser(info, packageName, mContext.getUser());
+        } else {
+            if (info == null || info.getAccessRules() == null || packageName == null) {
+                return false;
+            }
+            PackageManager packageManager = mContext.getPackageManager();
+            PackageInfo packageInfo;
+            try {
+                packageInfo = packageManager.getPackageInfo(packageName,
+                        PackageManager.GET_SIGNING_CERTIFICATES);
+            } catch (PackageManager.NameNotFoundException e) {
+                logd("Unknown package: " + packageName);
+                return false;
+            }
+            for (UiccAccessRule rule : info.getAccessRules()) {
+                if (rule.getCarrierPrivilegeStatus(packageInfo)
+                        == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Checks whether the given app is authorized to manage the given subscription for given user.
+     *
+     * <p>An app can only be authorized if it is available to the given user and included in the
+     * {@link android.telephony.UiccAccessRule} of the {@link android.telephony.SubscriptionInfo}
+     * with the access status.
+     *
+     * <p>Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
+     * true). To check for permissions for non-embedded subscription as well,
+     * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
+     *
+     * @param info        The subscription to check.
+     * @param packageName Package name of the app to check.
+     * @param user        UserHandle to check
+     * @return whether the app is authorized to manage this subscription per its access rules.
+     *
+     * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+     * @hide
+     */
+    public boolean canManageSubscriptionAsUser(@NonNull SubscriptionInfo info,
+            @NonNull String packageName, @NonNull UserHandle user) {
         if (info == null || info.getAccessRules() == null || packageName == null) {
             return false;
         }
-        PackageManager packageManager = mContext.getPackageManager();
+        PackageManager pm = mContext.getUser().equals(user)
+                ? mContext.getPackageManager()
+                : mContext.createContextAsUser(user, 0).getPackageManager();
         PackageInfo packageInfo;
         try {
-            packageInfo = packageManager.getPackageInfo(packageName,
-                PackageManager.GET_SIGNING_CERTIFICATES);
+            packageInfo = pm.getPackageInfo(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES);
         } catch (PackageManager.NameNotFoundException e) {
             logd("Unknown package: " + packageName);
             return false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f01cfc1..a7fe0cb 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2127,7 +2127,6 @@
      * <p>On some devices, this settings activity may not exist. Callers should ensure that this
      * case is appropriately handled.
      */
-    @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS)
     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS =
             "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
@@ -6934,7 +6933,6 @@
      *
      * @see SubscriptionInfo#getServiceCapabilities()
      */
-    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public boolean isDeviceVoiceCapable() {
         return isVoiceCapable();
     }
@@ -6974,7 +6972,6 @@
      *
      * @see SubscriptionInfo#getServiceCapabilities()
      */
-    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public boolean isDeviceSmsCapable() {
         return isSmsCapable();
     }
@@ -8781,13 +8778,14 @@
      *   Authentication error, no memory space available in EFMUK
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given
+     *          authType.
      */
     // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
     // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
     // it's not public API.
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
-    public String getIccAuthentication(int appType,@AuthType int authType, String data) {
+    public String getIccAuthentication(int appType, @AuthType int authType, String data) {
         return getIccAuthentication(getSubId(), appType, authType, data);
     }
 
@@ -8812,10 +8810,14 @@
      *   Key freshness failure
      *   Authentication error, no memory space available
      *   Authentication error, no memory space available in EFMUK
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given
+     *          authType.
      * @hide
      */
     @UnsupportedAppUsage
-    public String getIccAuthentication(int subId, int appType,@AuthType int authType, String data) {
+    public String getIccAuthentication(int subId, int appType, @AuthType int authType,
+            String data) {
         try {
             IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null)
@@ -19180,15 +19182,19 @@
     public @interface EmergencyCallbackModeType {}
 
     /**
-     * The callback mode is due to emergency call.
+     * The emergency callback mode is due to emergency call.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int EMERGENCY_CALLBACK_MODE_CALL = 1;
 
     /**
-     * The callback mode is due to emergency SMS.
+     * The emergency callback mode is due to emergency SMS.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int EMERGENCY_CALLBACK_MODE_SMS = 2;
 
     /**
@@ -19209,45 +19215,64 @@
     public @interface EmergencyCallbackModeStopReason {}
 
     /**
-     * unknown reason.
+     * Indicates that emergency callback mode has been stopped for an unknown reason.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int STOP_REASON_UNKNOWN = 0;
 
     /**
-     * The call back mode is exited due to a new normal call is originated.
+     * Indicates that emergency callback mode has been stopped because a new non-emergency call was
+     * initiated.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1;
 
     /**
-     * The call back mode is exited due to a new normal SMS is originated.
+     * Indicates that emergency callback mode has been stopped because a new non-emergency SMS was
+     * sent.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int STOP_REASON_NORMAL_SMS_SENT = 2;
 
     /**
-     * The call back mode is exited due to a new emergency call is originated.
+     * Indicates that emergency callback mode has been stopped because a new outgoing emergency
+     * call was initiated.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3;
 
     /**
-     * The call back mode is exited due to a new emergency SMS is originated.
+     * Indicates that emergency callback mode has been stopped because a new emergency SMS was sent.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4;
 
     /**
-     * The call back mode is exited due to timer expiry.
+     * Indicates that emergency callback mode has been stopped due to the emergency callback mode
+     * timer expiry.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int STOP_REASON_TIMER_EXPIRED = 5;
 
     /**
-     * The call back mode is exited due to user action.
+     * Indicates that emergency callback mode has been stopped due to user ending the emergency
+     * mode by clicking the notification.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    @SystemApi
     public static final int STOP_REASON_USER_ACTION = 6;
 
     /**
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 49ca6f3..44de65a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -1965,13 +1965,14 @@
     }
 
     /**
-     * Inform whether the device is aligned with the satellite for demo mode.
+     * Inform whether the device is aligned with the satellite in both real and demo mode.
      *
-     * Framework can send datagram to modem only when device is aligned with the satellite.
-     * This method helps framework to simulate the experience of sending datagram over satellite.
+     * In demo mode, framework will send datagram to modem only when device is aligned with
+     * the satellite. This method helps framework to simulate the experience of sending datagram
+     * over satellite.
      *
-     * @param isAligned {@true} Device is aligned with the satellite for demo mode
-     *                  {@false} Device is not aligned with the satellite for demo mode
+     * @param isAligned {code @true} Device is aligned with the satellite
+     *                  {code @false} Device is not aligned with the satellite
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 61f0146..231c8f5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2977,10 +2977,10 @@
     void requestTimeForNextSatelliteVisibility(in ResultReceiver receiver);
 
     /**
-     * Inform whether the device is aligned with the satellite within in margin for demo mode.
+     * Inform whether the device is aligned with the satellite in both real and demo mode.
      *
-     * @param isAligned {@true} Device is aligned with the satellite for demo mode
-     *                  {@false} Device is not aligned with the satellite for demo mode
+     * @param isAligned {@true} Device is aligned with the satellite.
+     *                  {@false} Device is not aligned with the satellite.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
diff --git a/tests/BootImageProfileTest/Android.bp b/tests/BootImageProfileTest/Android.bp
index 9fb5aa2..dbdc4b4 100644
--- a/tests/BootImageProfileTest/Android.bp
+++ b/tests/BootImageProfileTest/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_art_mainline",
 }
 
 java_test_host {
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index c77413b..c6855b4 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -59,6 +59,11 @@
         BOTTOM
     }
 
+    enum class AppProperty {
+        STANDARD,
+        NON_RESIZABLE
+    }
+
     /** Wait for an app moved to desktop to finish its transition. */
     private fun waitForAppToMoveToDesktop(wmHelper: WindowManagerStateHelper) {
         wmHelper
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
index 634b6ee..fd13d14 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -33,9 +33,9 @@
 @JvmOverloads
 constructor(
     instr: Instrumentation,
-    launcherName: String = ActivityOptions.NonResizeablePortraitActivity.LABEL,
+    launcherName: String = ActivityOptions.NonResizeableFixedAspectRatioPortraitActivity.LABEL,
     component: ComponentNameMatcher =
-        ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent()
+        ActivityOptions.NonResizeableFixedAspectRatioPortraitActivity.COMPONENT.toFlickerComponent()
 ) : StandardAppHelper(instr, launcherName, component) {
 
     private val gestureHelper: GestureHelper = GestureHelper(instrumentation)
@@ -128,6 +128,6 @@
     }
 
     companion object {
-        private const val BOUNDS_OFFSET: Int = 100
+        private const val BOUNDS_OFFSET: Int = 50
     }
 }
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index f891606..9ce8e80 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -115,6 +115,19 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".NonResizeableFixedAspectRatioPortraitActivity"
+            android:theme="@style/CutoutNever"
+            android:resizeableActivity="false"
+            android:screenOrientation="portrait"
+            android:minAspectRatio="1.77"
+            android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableFixedAspectRatioPortraitActivity"
+            android:label="NonResizeableFixedAspectRatioPortraitActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
         <activity android:name=".StartMediaProjectionActivity"
             android:theme="@style/CutoutNever"
             android:resizeableActivity="false"
@@ -143,6 +156,7 @@
         <activity android:name=".LaunchTransparentActivity"
                   android:resizeableActivity="false"
                   android:screenOrientation="portrait"
+                  android:minAspectRatio="1.77"
                   android:theme="@style/OptOutEdgeToEdge"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
                   android:label="LaunchTransparentActivity"
@@ -338,7 +352,8 @@
                   android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
                   android:theme="@style/CutoutShortEdges"
                   android:label="SplitScreenPrimaryActivity"
-                  android:exported="true">
+                  android:exported="true"
+                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
@@ -349,7 +364,8 @@
                   android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity"
                   android:theme="@style/CutoutShortEdges"
                   android:label="SplitScreenSecondaryActivity"
-                  android:exported="true">
+                  android:exported="true"
+                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index e4de2c5..73625da 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -85,6 +85,12 @@
                 FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
     }
 
+    public static class NonResizeableFixedAspectRatioPortraitActivity {
+        public static final String LABEL = "NonResizeableFixedAspectRatioPortraitActivity";
+        public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+                FLICKER_APP_PACKAGE + ".NonResizeableFixedAspectRatioPortraitActivity");
+    }
+
     public static class StartMediaProjectionActivity {
         public static final String LABEL = "StartMediaProjectionActivity";
         public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableFixedAspectRatioPortraitActivity.java
similarity index 64%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
copy to tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableFixedAspectRatioPortraitActivity.java
index f4d281d..be38c25 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableFixedAspectRatioPortraitActivity.java
@@ -14,10 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.server.wm.flicker.testapp;
 
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
+import android.app.Activity;
+import android.os.Bundle;
 
-val Kosmos.fixedColumnsSizeInteractor by
-    Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
+public class NonResizeableFixedAspectRatioPortraitActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.activity_non_resizeable);
+    }
+}
diff --git a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
index 90dff47..a1e1655 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
@@ -24,17 +24,16 @@
 import android.platform.test.annotations.Presubmit
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
+import com.android.test.input.MockInputManagerRule
 import java.util.concurrent.Executor
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
-import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.doAnswer
@@ -61,9 +60,8 @@
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
 
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Before
     fun setUp() {
@@ -72,7 +70,6 @@
         executor = HandlerExecutor(Handler(testLooper.looper))
         registeredListener = null
         monitoredDevices.clear()
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -92,7 +89,7 @@
             monitoredDevices.add(deviceId)
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerBatteryListener(anyInt(), any())
+        }.`when`(inputManagerRule.mock).registerBatteryListener(anyInt(), any())
 
         // Handle battery listener being unregistered.
         doAnswer {
@@ -108,14 +105,7 @@
             if (monitoredDevices.isEmpty()) {
                 registeredListener = null
             }
-        }.`when`(iInputManagerMock).unregisterBatteryListener(anyInt(), any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterBatteryListener(anyInt(), any())
     }
 
     private fun notifyBatteryStateChanged(
diff --git a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 080186e..3fc9ce1 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -45,15 +45,14 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.test.input.MockInputManagerRule;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -73,23 +72,22 @@
     private static final int DEVICE_ID = 1000;
     private static final int PLAYER_ID = 3;
 
-    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+    @Rule
+    public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule();
 
     private InputManager mInputManager;
 
-    @Mock private IInputManager mIInputManagerMock;
     private InputManagerGlobal.TestSession mInputManagerGlobalSession;
 
     @Before
     public void setUp() throws Exception {
         final Context context = spy(
                 new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
-        when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+        when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
 
-        when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+        when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn(
                 createInputDevice(DEVICE_ID));
 
-        mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
         mInputManager = new InputManager(context);
         when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
 
@@ -102,7 +100,7 @@
                 lightStatesById.put(lightIds[i], lightStates[i]);
             }
             return null;
-        }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID),
+        }).when(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID),
                 any(int[].class), any(LightState[].class), any(IBinder.class));
 
         doAnswer(invocation -> {
@@ -111,7 +109,7 @@
                 return lightStatesById.get(lightId);
             }
             return new LightState(0);
-        }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt());
+        }).when(mInputManagerRule.getMock()).getLightState(eq(DEVICE_ID), anyInt());
     }
 
     @After
@@ -130,7 +128,7 @@
 
     private void mockLights(Light[] lights) throws Exception {
         // Mock the Lights returned form InputManagerService
-        when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn(
+        when(mInputManagerRule.getMock().getLights(eq(DEVICE_ID))).thenReturn(
                 new ArrayList(Arrays.asList(lights)));
     }
 
@@ -151,7 +149,7 @@
 
         LightsManager lightsManager = device.getLightsManager();
         List<Light> lights = lightsManager.getLights();
-        verify(mIInputManagerMock).getLights(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getLights(eq(DEVICE_ID));
         assertEquals(lights, Arrays.asList(mockedLights));
     }
 
@@ -185,9 +183,9 @@
                 .build());
         IBinder token = session.getToken();
 
-        verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+        verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID),
                 any(String.class), eq(token));
-        verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
+        verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
                 eq(states), eq(token));
 
         // Then all 3 should turn on.
@@ -204,7 +202,7 @@
 
         // close session
         session.close();
-        verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+        verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token));
     }
 
     @Test
@@ -232,9 +230,9 @@
                 .build());
         IBinder token = session.getToken();
 
-        verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+        verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID),
                 any(String.class), eq(token));
-        verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
+        verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
                 eq(states), eq(token));
 
         // Verify the light state
@@ -245,7 +243,7 @@
 
         // close session
         session.close();
-        verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+        verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token));
     }
 
     @Test
diff --git a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
index 0e3c200..3057f5d 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
+++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -41,16 +41,13 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.test.input.MockInputManagerRule;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
 
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
@@ -70,43 +67,34 @@
 
     private static final int DEVICE_ID = 1000;
 
-    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+    @Rule
+    public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule();
 
     private InputManager mInputManager;
     private IInputSensorEventListener mIInputSensorEventListener;
     private final Object mLock = new Object();
 
-    @Mock private IInputManager mIInputManagerMock;
-    private InputManagerGlobal.TestSession mInputManagerGlobalSession;
-
     @Before
     public void setUp() throws Exception {
         final Context context = spy(
                 new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
-        mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
         mInputManager = new InputManager(context);
         when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
 
-        when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+        when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
 
-        when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+        when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn(
                 createInputDeviceWithSensor(DEVICE_ID));
 
-        when(mIInputManagerMock.getSensorList(eq(DEVICE_ID))).thenReturn(new InputSensorInfo[] {
-                createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
-                createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
+        when(mInputManagerRule.getMock().getSensorList(eq(DEVICE_ID))).thenReturn(
+                new InputSensorInfo[]{
+                        createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
+                        createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
 
-        when(mIInputManagerMock.enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
+        when(mInputManagerRule.getMock().enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
                 .thenReturn(true);
 
-        when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true);
-    }
-
-    @After
-    public void tearDown() {
-        if (mInputManagerGlobalSession != null) {
-            mInputManagerGlobalSession.close();
-        }
+        when(mInputManagerRule.getMock().registerSensorListener(any())).thenReturn(true);
     }
 
     private class InputTestSensorEventListener implements SensorEventListener {
@@ -175,13 +163,13 @@
 
         SensorManager sensorManager = device.getSensorManager();
         List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(1, accelList.size());
         assertEquals(DEVICE_ID, accelList.get(0).getId());
         assertEquals(Sensor.TYPE_ACCELEROMETER, accelList.get(0).getType());
 
         List<Sensor> gyroList = sensorManager.getSensorList(Sensor.TYPE_GYROSCOPE);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(1, gyroList.size());
         assertEquals(DEVICE_ID, gyroList.get(0).getId());
         assertEquals(Sensor.TYPE_GYROSCOPE, gyroList.get(0).getType());
@@ -197,11 +185,11 @@
 
         List<Sensor> gameRotationList = sensorManager.getSensorList(
                 Sensor.TYPE_GAME_ROTATION_VECTOR);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(0, gameRotationList.size());
 
         List<Sensor> gravityList = sensorManager.getSensorList(Sensor.TYPE_GRAVITY);
-        verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+        verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
         assertEquals(0, gravityList.size());
     }
 
@@ -218,13 +206,13 @@
             mIInputSensorEventListener = invocation.getArgument(0);
             assertNotNull(mIInputSensorEventListener);
             return true;
-        }).when(mIInputManagerMock).registerSensorListener(any());
+        }).when(mInputManagerRule.getMock()).registerSensorListener(any());
 
         InputTestSensorEventListener listener = new InputTestSensorEventListener();
         assertTrue(sensorManager.registerListener(listener, sensor,
                 SensorManager.SENSOR_DELAY_NORMAL));
-        verify(mIInputManagerMock).registerSensorListener(any());
-        verify(mIInputManagerMock).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
+        verify(mInputManagerRule.getMock()).registerSensorListener(any());
+        verify(mInputManagerRule.getMock()).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
                 anyInt(), anyInt());
 
         float[] values = new float[] {0.12f, 9.8f, 0.2f};
@@ -240,7 +228,7 @@
         }
 
         sensorManager.unregisterListener(listener);
-        verify(mIInputManagerMock).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
+        verify(mInputManagerRule.getMock()).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
     }
 
 }
diff --git a/tests/Input/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt
index 152dde9..4c6bb84 100644
--- a/tests/Input/src/android/hardware/input/InputManagerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt
@@ -23,18 +23,16 @@
 import android.view.DisplayInfo
 import android.view.InputDevice
 import androidx.test.core.app.ApplicationProvider
-import org.junit.After
-import org.junit.Assert.assertNotNull
+import com.android.test.input.MockInputManagerRule
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoJUnitRunner
 
 /**
@@ -54,35 +52,23 @@
     }
 
     @get:Rule
-    val rule = MockitoJUnit.rule()!!
+    val inputManagerRule = MockInputManagerRule()
 
     private lateinit var devicesChangedListener: IInputDevicesChangedListener
     private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         inputManager = InputManager(context)
         `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
-        `when`(iInputManager.inputDeviceIds).then {
+        `when`(inputManagerRule.mock.inputDeviceIds).then {
             deviceGenerationMap.keys.toIntArray()
         }
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun notifyDeviceChanged(
         deviceId: Int,
         associatedDisplayId: Int,
@@ -92,7 +78,7 @@
             ?: throw IllegalArgumentException("Device $deviceId was never added!")
         deviceGenerationMap[deviceId] = generation
 
-        `when`(iInputManager.getInputDevice(deviceId))
+        `when`(inputManagerRule.mock.getInputDevice(deviceId))
             .thenReturn(createInputDevice(deviceId, associatedDisplayId, usiVersion, generation))
         val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
         if (::devicesChangedListener.isInitialized) {
@@ -125,7 +111,7 @@
     fun testUsiVersionFallBackToDisplayConfig() {
         addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null)
 
-        `when`(iInputManager.getHostUsiVersionFromDisplayConfig(eq(42)))
+        `when`(inputManagerRule.mock.getHostUsiVersionFromDisplayConfig(eq(42)))
             .thenReturn(HostUsiVersion(9, 8))
         val usiVersion = inputManager.getHostUsiVersion(createDisplay(42))
         assertEquals(HostUsiVersion(9, 8), usiVersion)
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
index 072341d..e99c814 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
@@ -18,20 +18,17 @@
 
 import android.content.Context
 import android.content.ContextWrapper
-import android.os.Handler
 import android.os.IBinder
-import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.platform.test.flag.junit.SetFlagsRule
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.`when`
@@ -69,20 +66,16 @@
 
     @get:Rule
     val rule = SetFlagsRule()
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
-    private val testLooper = TestLooper()
     private var registeredListener: IKeyGestureHandler? = null
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -97,7 +90,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerKeyGestureHandler(any())
+        }.`when`(inputManagerRule.mock).registerKeyGestureHandler(any())
 
         // Handle key gesture handler being unregistered.
         doAnswer {
@@ -108,14 +101,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterKeyGestureHandler(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(any())
     }
 
     private fun handleKeyGestureEvent(event: KeyGestureEvent) {
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
index ca9de60..cf0bfcc 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
@@ -26,20 +26,19 @@
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnitRunner
+import com.android.test.input.MockInputManagerRule
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
 
 /**
  * Tests for [InputManager.KeyGestureEventListener].
@@ -63,21 +62,18 @@
 
     @get:Rule
     val rule = SetFlagsRule()
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     private val testLooper = TestLooper()
     private val executor = HandlerExecutor(Handler(testLooper.looper))
     private var registeredListener: IKeyGestureEventListener? = null
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -92,7 +88,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerKeyGestureEventListener(any())
+        }.`when`(inputManagerRule.mock).registerKeyGestureEventListener(any())
 
         // Handle key gesture event listener being unregistered.
         doAnswer {
@@ -103,14 +99,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterKeyGestureEventListener(any())
     }
 
     private fun notifyKeyGestureEvent(event: KeyGestureEvent) {
diff --git a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
index 23135b2..d25dee1 100644
--- a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
@@ -24,22 +24,20 @@
 import android.platform.test.annotations.Presubmit
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoJUnitRunner
+import com.android.test.input.MockInputManagerRule
 import java.util.concurrent.Executor
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
 
 /**
  * Tests for [InputManager.KeyboardBacklightListener].
@@ -50,23 +48,19 @@
 @Presubmit
 @RunWith(MockitoJUnitRunner::class)
 class KeyboardBacklightListenerTest {
+
     @get:Rule
-    val rule = MockitoJUnit.rule()!!
+    val inputManagerRule = MockInputManagerRule()
 
     private lateinit var testLooper: TestLooper
     private var registeredListener: IKeyboardBacklightListener? = null
     private lateinit var executor: Executor
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         testLooper = TestLooper()
         executor = HandlerExecutor(Handler(testLooper.looper))
         registeredListener = null
@@ -84,7 +78,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerKeyboardBacklightListener(any())
+        }.`when`(inputManagerRule.mock).registerKeyboardBacklightListener(any())
 
         // Handle keyboard backlight listener being unregistered.
         doAnswer {
@@ -95,14 +89,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterKeyboardBacklightListener(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterKeyboardBacklightListener(any())
     }
 
     private fun notifyKeyboardBacklightChanged(
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index bcd56ad..1c2a053 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -27,21 +27,20 @@
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.server.testutils.any
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnitRunner
+import com.android.test.input.MockInputManagerRule
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
 
 /**
  * Tests for [InputManager.StickyModifierStateListener].
@@ -59,21 +58,18 @@
 
     @get:Rule
     val rule = SetFlagsRule()
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     private val testLooper = TestLooper()
     private val executor = HandlerExecutor(Handler(testLooper.looper))
     private var registeredListener: IStickyModifierStateListener? = null
     private lateinit var context: Context
     private lateinit var inputManager: InputManager
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
-    @Mock
-    private lateinit var iInputManagerMock: IInputManager
 
     @Before
     fun setUp() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
         inputManager = InputManager(context)
         `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
                 .thenReturn(inputManager)
@@ -88,7 +84,7 @@
             }
             registeredListener = listener
             null
-        }.`when`(iInputManagerMock).registerStickyModifierStateListener(any())
+        }.`when`(inputManagerRule.mock).registerStickyModifierStateListener(any())
 
         // Handle sticky modifier state listener being unregistered.
         doAnswer {
@@ -99,14 +95,7 @@
             }
             registeredListener = null
             null
-        }.`when`(iInputManagerMock).unregisterStickyModifierStateListener(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        }.`when`(inputManagerRule.mock).unregisterStickyModifierStateListener(any())
     }
 
     private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) {
diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
index f2724e6..044f11d 100644
--- a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
@@ -27,7 +27,6 @@
 import android.hardware.input.IInputDeviceBatteryListener
 import android.hardware.input.IInputDeviceBatteryState
 import android.hardware.input.IInputDevicesChangedListener
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.os.Binder
@@ -42,13 +41,13 @@
 import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS
 import com.android.server.input.BatteryController.UEventBatteryListener
 import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS
+import com.android.test.input.MockInputManagerRule
 import org.hamcrest.Description
 import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.hamcrest.Matchers
 import org.hamcrest.TypeSafeMatcher
 import org.hamcrest.core.IsEqual.equalTo
-import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -184,12 +183,12 @@
 
     @get:Rule
     val rule = MockitoJUnit.rule()!!
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
     private lateinit var native: NativeInputManagerService
     @Mock
-    private lateinit var iInputManager: IInputManager
-    @Mock
     private lateinit var uEventManager: UEventManager
     @Mock
     private lateinit var bluetoothBatteryManager: BluetoothBatteryManager
@@ -205,10 +204,9 @@
     fun setup() {
         context = TestableContext(ApplicationProvider.getApplicationContext())
         testLooper = TestLooper()
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         val inputManager = InputManager(context)
         context.addMockSystemService(InputManager::class.java, inputManager)
-        `when`(iInputManager.inputDeviceIds).then {
+        `when`(inputManagerRule.mock.inputDeviceIds).then {
             deviceGenerationMap.keys.toIntArray()
         }
         addInputDevice(DEVICE_ID)
@@ -218,18 +216,11 @@
             bluetoothBatteryManager)
         batteryController.systemRunning()
         val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java)
-        verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture())
+        verify(inputManagerRule.mock).registerInputDevicesChangedListener(listenerCaptor.capture())
         devicesChangedListener = listenerCaptor.value
         testLooper.dispatchAll()
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun notifyDeviceChanged(
             deviceId: Int,
         hasBattery: Boolean = true,
@@ -239,7 +230,7 @@
             ?: throw IllegalArgumentException("Device $deviceId was never added!")
         deviceGenerationMap[deviceId] = generation
 
-        `when`(iInputManager.getInputDevice(deviceId))
+        `when`(inputManagerRule.mock.getInputDevice(deviceId))
             .thenReturn(createInputDevice(deviceId, hasBattery, supportsUsi, generation))
         val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
         if (::devicesChangedListener.isInitialized) {
@@ -657,9 +648,9 @@
 
     @Test
     fun testRegisterBluetoothListenerForMonitoredBluetoothDevices() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn("11:22:33:44:55:66")
         addInputDevice(BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -686,7 +677,7 @@
         batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID)
         verify(bluetoothBatteryManager, never()).removeBatteryListener(any())
 
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn(null)
         notifyDeviceChanged(SECOND_BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -695,7 +686,7 @@
 
     @Test
     fun testNotifiesBluetoothBatteryChanges() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         addInputDevice(BT_DEVICE_ID)
@@ -716,7 +707,7 @@
     @Test
     fun testBluetoothBatteryIsPrioritized() {
         `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
@@ -745,7 +736,7 @@
     @Test
     fun testFallBackToNativeBatteryStateWhenBluetoothStateInvalid() {
         `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
@@ -776,9 +767,9 @@
 
     @Test
     fun testRegisterBluetoothMetadataListenerForMonitoredBluetoothDevices() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn("11:22:33:44:55:66")
         addInputDevice(BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -811,7 +802,7 @@
         verify(bluetoothBatteryManager)
             .removeMetadataListener("AA:BB:CC:DD:EE:FF", metadataListener1.value)
 
-        `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
             .thenReturn(null)
         notifyDeviceChanged(SECOND_BT_DEVICE_ID)
         testLooper.dispatchNext()
@@ -821,7 +812,7 @@
 
     @Test
     fun testNotifiesBluetoothMetadataBatteryChanges() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
                 BluetoothDevice.METADATA_MAIN_BATTERY))
@@ -861,7 +852,7 @@
 
     @Test
     fun testBluetoothMetadataBatteryIsPrioritized() {
-        `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+        `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
             .thenReturn("AA:BB:CC:DD:EE:FF")
         `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
         `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 351ec463..927958e 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -93,14 +93,12 @@
         )
     }
 
-    @JvmField
-    @Rule
+    @get:Rule
     val extendedMockitoRule =
         ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java)
             .mockStatic(PermissionChecker::class.java).build()!!
 
-    @JvmField
-    @Rule
+    @get:Rule
     val setFlagsRule = SetFlagsRule()
 
     @get:Rule
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 4ae06a4..7526737 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -46,6 +46,7 @@
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import junitparams.JUnitParamsRunner
 import junitparams.Parameters
+import org.junit.After
 import org.junit.Assert.assertArrayEquals
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
@@ -128,6 +129,13 @@
         currentPid = Process.myPid()
     }
 
+    @After
+    fun teardown() {
+        if (this::inputManagerGlobalSession.isInitialized) {
+            inputManagerGlobalSession.close()
+        }
+    }
+
     private fun setupBehaviors() {
         Mockito.`when`(
             resources.getBoolean(
diff --git a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
index f74fd72..4f4c97be 100644
--- a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
@@ -18,16 +18,18 @@
 
 import android.content.Context
 import android.content.ContextWrapper
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.provider.Settings
 import android.view.InputDevice
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Rule
@@ -35,10 +37,6 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.junit.MockitoJUnit
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
 
 private fun createKeyboard(deviceId: Int): InputDevice =
     InputDevice.Builder()
@@ -73,15 +71,15 @@
     @get:Rule
     val rule = MockitoJUnit.rule()!!
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
+
     @Mock
     private lateinit var native: NativeInputManagerService
     private lateinit var mKeyRemapper: KeyRemapper
     private lateinit var context: Context
     private lateinit var dataStore: PersistentDataStore
     private lateinit var testLooper: TestLooper
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
 
     @Before
     fun setup() {
@@ -104,25 +102,17 @@
             dataStore,
             testLooper.looper
         )
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         val inputManager = InputManager(context)
         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
             .thenReturn(inputManager)
-        Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
+        Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
     }
 
     @Test
     fun testKeyRemapping_whenRemappingEnabled() {
         ModifierRemappingFlag(true).use {
             val keyboard = createKeyboard(DEVICE_ID)
-            Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+            Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
 
             for (i in REMAPPABLE_KEYS.indices) {
                 val fromKeyCode = REMAPPABLE_KEYS[i]
@@ -160,7 +150,7 @@
     fun testKeyRemapping_whenRemappingDisabled() {
         ModifierRemappingFlag(false).use {
             val keyboard = createKeyboard(DEVICE_ID)
-            Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+            Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
 
             mKeyRemapper.remapKey(REMAPPABLE_KEYS[0], REMAPPABLE_KEYS[1])
             testLooper.dispatchAll()
diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
index 59aa96c..58fb4e1 100644
--- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
@@ -20,11 +20,9 @@
 import android.content.Context
 import android.content.ContextWrapper
 import android.graphics.Color
-import android.hardware.input.IInputManager
 import android.hardware.input.IKeyboardBacklightListener
 import android.hardware.input.IKeyboardBacklightState
 import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
 import android.hardware.lights.Light
 import android.os.UEventObserver
 import android.os.test.TestLooper
@@ -35,7 +33,11 @@
 import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL
 import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS
 import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotEquals
@@ -52,10 +54,6 @@
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
 
 private fun createKeyboard(deviceId: Int): InputDevice =
     InputDevice.Builder()
@@ -100,10 +98,10 @@
 
     @get:Rule
     val rule = MockitoJUnit.rule()!!
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
-    private lateinit var iInputManager: IInputManager
-    @Mock
     private lateinit var native: NativeInputManagerService
     @Mock
     private lateinit var uEventManager: UEventManager
@@ -111,7 +109,6 @@
     private lateinit var context: Context
     private lateinit var dataStore: PersistentDataStore
     private lateinit var testLooper: TestLooper
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
     private var lightColorMap: HashMap<Int, Int> = HashMap()
     private var lastBacklightState: KeyboardBacklightState? = null
     private var sysfsNodeChanges = 0
@@ -134,10 +131,9 @@
         testLooper = TestLooper()
         keyboardBacklightController = KeyboardBacklightController(context, native, dataStore,
                 testLooper.looper, FakeAnimatorFactory(), uEventManager)
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         val inputManager = InputManager(context)
         `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
-        `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+        `when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
         `when`(native.setLightColor(anyInt(), anyInt(), anyInt())).then {
             val args = it.arguments
             lightColorMap.put(args[1] as Int, args[2] as Int)
@@ -152,13 +148,6 @@
         }
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     @Test
     fun testKeyboardBacklightIncrementDecrement() {
         KeyboardBacklightFlags(
@@ -168,8 +157,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -186,8 +176,9 @@
         ).use {
             val keyboardWithoutBacklight = createKeyboard(DEVICE_ID)
             val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithoutBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             incrementKeyboardBacklight(DEVICE_ID)
@@ -205,8 +196,9 @@
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
             val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(
                 listOf(
                     keyboardBacklight,
                     keyboardInputLight
@@ -230,8 +222,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
 
             for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) {
                 dataStore.setKeyboardBacklightBrightness(
@@ -263,7 +256,8 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
                 LIGHT_ID,
@@ -278,7 +272,7 @@
                 lightColorMap.isEmpty()
             )
 
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceChanged(DEVICE_ID)
             keyboardBacklightController.notifyUserActivity()
             testLooper.dispatchNext()
@@ -300,8 +294,9 @@
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
             val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             // Register backlight listener
@@ -352,8 +347,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
                 LIGHT_ID,
@@ -388,8 +384,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
                 LIGHT_ID,
@@ -482,8 +479,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             incrementKeyboardBacklight(DEVICE_ID)
@@ -511,8 +509,9 @@
             val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -531,8 +530,9 @@
             val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) }
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -551,8 +551,9 @@
             val suggestedLevels = intArrayOf(22, 63, 135, 196)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             // Framework will add the lowest and maximum levels if not provided via config
@@ -572,8 +573,10 @@
             val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
                     suggestedLevels)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID))
+                .thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
 
             // Framework will drop out of bound levels in the config
@@ -591,8 +594,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
 
             dataStore.setKeyboardBacklightBrightness(
                 keyboardWithBacklight.descriptor,
@@ -619,8 +623,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
 
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             incrementKeyboardBacklight(DEVICE_ID)
@@ -642,8 +647,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             sendAmbientBacklightValue(1)
             assertEquals(
@@ -671,8 +677,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             sendAmbientBacklightValue(254)
             assertEquals(
@@ -701,8 +708,9 @@
         ).use {
             val keyboardWithBacklight = createKeyboard(DEVICE_ID)
             val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
-            `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
-            `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+            `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+                .thenReturn(keyboardWithBacklight)
+            `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
             keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
             incrementKeyboardBacklight(DEVICE_ID)
             assertEquals(
diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
index c073c7a..ff8a9ba 100644
--- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
@@ -23,9 +23,7 @@
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.content.pm.ServiceInfo
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyGlyphMap.KeyCombination
 import android.os.Bundle
 import android.os.test.TestLooper
@@ -36,8 +34,8 @@
 import android.view.KeyEvent
 import androidx.test.core.app.ApplicationProvider
 import com.android.hardware.input.Flags
+import com.android.test.input.MockInputManagerRule
 import com.android.test.input.R
-import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
@@ -65,30 +63,24 @@
         const val RECEIVER_NAME = "DummyReceiver"
     }
 
-    @JvmField
-    @Rule(order = 0)
+    @get:Rule
     val setFlagsRule = SetFlagsRule()
-
-    @JvmField
-    @Rule(order = 1)
+    @get:Rule
     val mockitoRule = MockitoJUnit.rule()!!
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
     private lateinit var packageManager: PackageManager
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
-
     private lateinit var keyboardGlyphManager: KeyboardGlyphManager
     private lateinit var context: Context
     private lateinit var testLooper: TestLooper
-    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
     private lateinit var keyboardDevice: InputDevice
 
     @Before
     fun setup() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         testLooper = TestLooper()
         keyboardGlyphManager = KeyboardGlyphManager(context, testLooper.looper)
 
@@ -98,21 +90,14 @@
         testLooper.dispatchAll()
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun setupInputDevices() {
         val inputManager = InputManager(context)
         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
             .thenReturn(inputManager)
 
         keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "")
-        Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
-        Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+        Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
     }
 
     private fun setupBroadcastReceiver() {
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 301c0e6..d6654cc 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -24,11 +24,10 @@
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.content.pm.ServiceInfo
-import android.hardware.input.KeyboardLayoutSelectionResult
-import android.hardware.input.IInputManager
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyboardLayout
+import android.hardware.input.KeyboardLayoutSelectionResult
 import android.icu.util.ULocale
 import android.os.Bundle
 import android.os.test.TestLooper
@@ -42,8 +41,12 @@
 import com.android.internal.os.KeyboardConfiguredProto
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.test.input.MockInputManagerRule
 import com.android.test.input.R
-import org.junit.After
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
@@ -53,10 +56,6 @@
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
 
 fun createKeyboard(
     deviceId: Int,
@@ -120,8 +119,8 @@
     val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
             .mockStatic(FrameworkStatsLog::class.java).build()!!
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
+    @get:Rule
+    val inputManagerRule = MockInputManagerRule()
 
     @Mock
     private lateinit var native: NativeInputManagerService
@@ -148,7 +147,6 @@
     @Before
     fun setup() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
             override fun openRead(): InputStream? {
                 throw FileNotFoundException()
@@ -171,13 +169,6 @@
         setupIme()
     }
 
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
     private fun setupInputDevices() {
         val inputManager = InputManager(context)
         Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
@@ -191,19 +182,19 @@
                 DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "dvorak")
         englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID,
                 DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "qwerty")
-        Mockito.`when`(iInputManager.inputDeviceIds)
+        Mockito.`when`(inputManagerRule.mock.inputDeviceIds)
             .thenReturn(intArrayOf(
                 DEVICE_ID,
                 VENDOR_SPECIFIC_DEVICE_ID,
                 ENGLISH_DVORAK_DEVICE_ID,
                 ENGLISH_QWERTY_DEVICE_ID
             ))
-        Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
-        Mockito.`when`(iInputManager.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
             .thenReturn(vendorSpecificKeyboardDevice)
-        Mockito.`when`(iInputManager.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
             .thenReturn(englishDvorakKeyboardDevice)
-        Mockito.`when`(iInputManager.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
+        Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
                 .thenReturn(englishQwertyKeyboardDevice)
     }
 
diff --git a/tests/Input/src/com/android/test/input/MockInputManagerRule.kt b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
new file mode 100644
index 0000000..cef9856
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.input
+
+import android.hardware.input.IInputManager
+import android.hardware.input.InputManagerGlobal
+import org.junit.rules.ExternalResource
+import org.mockito.Mockito
+
+/**
+ * A test rule that temporarily replaces the [IInputManager] connection to the server with a mock
+ * to be used for testing.
+ */
+class MockInputManagerRule : ExternalResource() {
+
+    private lateinit var session: InputManagerGlobal.TestSession
+
+    val mock: IInputManager = Mockito.mock(IInputManager::class.java)
+
+    override fun before() {
+        session = InputManagerGlobal.createTestSession(mock)
+    }
+
+    override fun after() {
+        if (this::session.isInitialized) {
+            session.close()
+        }
+    }
+}
diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
index 9f4df90..c61a250 100644
--- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
+++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
@@ -39,7 +39,6 @@
 import junit.framework.Assert.fail
 import org.hamcrest.Matchers.allOf
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TestName
@@ -108,7 +107,6 @@
         parser = InputJsonParser(instrumentation.context)
     }
 
-    @Ignore("b/366602644")
     @Test
     fun testEvemuRecording() {
         VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>(
diff --git a/tests/JobSchedulerPerfTests/OWNERS b/tests/JobSchedulerPerfTests/OWNERS
index 6f207fb1..c8345f7 100644
--- a/tests/JobSchedulerPerfTests/OWNERS
+++ b/tests/JobSchedulerPerfTests/OWNERS
@@ -1 +1 @@
-include /apex/jobscheduler/OWNERS
+include /apex/jobscheduler/JOB_OWNERS
diff --git a/tests/JobSchedulerTestApp/OWNERS b/tests/JobSchedulerTestApp/OWNERS
index 6f207fb1..c8345f7 100644
--- a/tests/JobSchedulerTestApp/OWNERS
+++ b/tests/JobSchedulerTestApp/OWNERS
@@ -1 +1 @@
-include /apex/jobscheduler/OWNERS
+include /apex/jobscheduler/JOB_OWNERS
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 0375f66..d9295dd 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -515,33 +515,27 @@
                 Install.single(APEX_V2));
     }
 
-    @Test
-    public void testGetStagedModuleNames() throws Exception {
-        // Before staging a session
-        String[] result = getPackageManagerNative().getStagedApexModuleNames();
-        assertThat(result).hasLength(0);
-        // Stage an apex
-        int sessionId = Install.single(APEX_V2).setStaged().commit();
-        result = getPackageManagerNative().getStagedApexModuleNames();
-        assertThat(result).hasLength(1);
-        assertThat(result).isEqualTo(new String[]{SHIM_APEX_PACKAGE_NAME});
-        // Abandon the session
-        InstallUtils.openPackageInstallerSession(sessionId).abandon();
-        result = getPackageManagerNative().getStagedApexModuleNames();
-        assertThat(result).hasLength(0);
+    private StagedApexInfo findStagedApexInfo(StagedApexInfo[] infos, String moduleName) {
+        for (StagedApexInfo info: infos) {
+            if (info.moduleName.equals(moduleName)) {
+                return info;
+            }
+        }
+        return null;
     }
 
     @Test
-    public void testGetStagedApexInfo() throws Exception {
-        // Ask for non-existing module
-        StagedApexInfo result = getPackageManagerNative().getStagedApexInfo("not found");
-        assertThat(result).isNull();
+    public void testGetStagedApexInfos() throws Exception {
+        // Not found before staging
+        StagedApexInfo[] result = getPackageManagerNative().getStagedApexInfos();
+        assertThat(findStagedApexInfo(result, TEST_APEX_PACKAGE_NAME)).isNull();
         // Stage an apex
         int sessionId = Install.single(TEST_APEX_CLASSPATH).setStaged().commit();
         // Query proper module name
-        result = getPackageManagerNative().getStagedApexInfo(TEST_APEX_PACKAGE_NAME);
-        assertThat(result.moduleName).isEqualTo(TEST_APEX_PACKAGE_NAME);
-        assertThat(result.hasClassPathJars).isTrue();
+        result = getPackageManagerNative().getStagedApexInfos();
+        StagedApexInfo found = findStagedApexInfo(result, TEST_APEX_PACKAGE_NAME);
+        assertThat(found).isNotNull();
+        assertThat(found.hasClassPathJars).isTrue();
         InstallUtils.openPackageInstallerSession(sessionId).abandon();
     }
 
@@ -573,14 +567,15 @@
         int sessionId = Install.single(APEX_V2).setStaged().commit();
         ArgumentCaptor<ApexStagedEvent> captor = ArgumentCaptor.forClass(ApexStagedEvent.class);
         verify(observer, timeout(5000)).onApexStaged(captor.capture());
-        assertThat(captor.getValue().stagedApexModuleNames).isEqualTo(
-                new String[] {SHIM_APEX_PACKAGE_NAME});
+        StagedApexInfo found =
+                findStagedApexInfo(captor.getValue().stagedApexInfos, SHIM_APEX_PACKAGE_NAME);
+        assertThat(found).isNotNull();
 
         // Abandon and verify observer is called
         Mockito.clearInvocations(observer);
         InstallUtils.openPackageInstallerSession(sessionId).abandon();
         verify(observer, timeout(5000)).onApexStaged(captor.capture());
-        assertThat(captor.getValue().stagedApexModuleNames).hasLength(0);
+        assertThat(captor.getValue().stagedApexInfos).hasLength(0);
     }
 
     @Test
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index f1fc503..97abcd7 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -592,23 +592,15 @@
     }
 
     @Test
-    public void testGetStagedModuleNames() throws Exception {
-        assumeTrue("Device does not support updating APEX",
-                mHostUtils.isApexUpdateSupported());
-
-        runPhase("testGetStagedModuleNames");
-    }
-
-    @Test
     @LargeTest
-    public void testGetStagedApexInfo() throws Exception {
+    public void testGetStagedApexInfos() throws Exception {
         assumeTrue("Device does not support updating APEX",
                 mHostUtils.isApexUpdateSupported());
 
         pushTestApex(APEXD_TEST_APEX);
         getDevice().reboot();
 
-        runPhase("testGetStagedApexInfo");
+        runPhase("testGetStagedApexInfos");
     }
 
     @Test
diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
index 28d7b42..d78ced1 100644
--- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
@@ -121,4 +121,12 @@
         assertNull(mConfig.getViewerString(4));
         assertNull(mConfig.getViewerString(5));
     }
+
+    @Test
+    public void loadUnloadAndReloadViewerConfig() {
+        loadViewerConfig();
+        unloadViewerConfig();
+        loadViewerConfig();
+        unloadViewerConfig();
+    }
 }
diff --git a/tests/testables/Android.bp b/tests/testables/Android.bp
index f211185..17cc0b2 100644
--- a/tests/testables/Android.bp
+++ b/tests/testables/Android.bp
@@ -35,4 +35,8 @@
         "androidx.test.rules",
         "mockito-target-inline-minus-junit4",
     ],
+    static_libs: [
+        "PlatformMotionTesting",
+        "kotlinx_coroutines_test",
+    ],
 }
diff --git a/tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt b/tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt
new file mode 100644
index 0000000..b27b826
--- /dev/null
+++ b/tests/testables/src/android/animation/AnimatorTestRuleToolkit.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation
+
+import android.animation.AnimatorTestRuleToolkit.Companion.TAG
+import android.util.Log
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import platform.test.motion.MotionTestRule
+import platform.test.motion.RecordedMotion
+import platform.test.motion.RecordedMotion.Companion.create
+import platform.test.motion.golden.DataPoint
+import platform.test.motion.golden.Feature
+import platform.test.motion.golden.FrameId
+import platform.test.motion.golden.TimeSeries
+import platform.test.motion.golden.TimeSeriesCaptureScope
+import platform.test.motion.golden.TimestampFrameId
+
+class AnimatorTestRuleToolkit(val animatorTestRule: AnimatorTestRule, val testScope: TestScope) {
+    internal companion object {
+        const val TAG = "AnimatorRuleToolkit"
+    }
+}
+
+/**
+ * Controls the timing of the motion recording.
+ *
+ * The time series is recorded while the [recording] function is running.
+ */
+class MotionControl(val recording: MotionControlFn)
+
+typealias MotionControlFn = suspend MotionControlScope.() -> Unit
+
+interface MotionControlScope {
+    /** Waits until [check] returns true. Invoked on each frame. */
+    suspend fun awaitCondition(check: () -> Boolean)
+
+    /** Waits for [count] frames to be processed. */
+    suspend fun awaitFrames(count: Int = 1)
+}
+
+/** Defines the sampling of features during a test run. */
+data class AnimatorRuleRecordingSpec<T>(
+    /** The root `observing` object, available in [timeSeriesCapture]'s [TimeSeriesCaptureScope]. */
+    val captureRoot: T,
+
+    /** The timing for the recording. */
+    val motionControl: MotionControl,
+
+    /** Time interval between frame captures, in milliseconds. */
+    val frameDurationMs: Long = 16L,
+
+    /**  Produces the time-series, invoked on each animation frame. */
+    val timeSeriesCapture: TimeSeriesCaptureScope<T>.() -> Unit,
+)
+
+/** Records the time-series of the features specified in [recordingSpec]. */
+fun <T> MotionTestRule<AnimatorTestRuleToolkit>.recordMotion(
+    recordingSpec: AnimatorRuleRecordingSpec<T>,
+): RecordedMotion {
+    with(toolkit.animatorTestRule) {
+        val frameIdCollector = mutableListOf<FrameId>()
+        val propertyCollector = mutableMapOf<String, MutableList<DataPoint<*>>>()
+
+        fun recordFrame(frameId: FrameId) {
+            Log.i(TAG, "recordFrame($frameId)")
+            frameIdCollector.add(frameId)
+            recordingSpec.timeSeriesCapture.invoke(
+                TimeSeriesCaptureScope(recordingSpec.captureRoot, propertyCollector)
+            )
+        }
+
+        val motionControl =
+            MotionControlImpl(
+                toolkit.animatorTestRule,
+                toolkit.testScope,
+                recordingSpec.frameDurationMs,
+                recordingSpec.motionControl,
+            )
+
+        Log.i(TAG, "recordMotion() begin recording")
+
+        val startFrameTime = currentTime
+        while (!motionControl.recordingEnded) {
+            recordFrame(TimestampFrameId(currentTime - startFrameTime))
+            motionControl.nextFrame()
+        }
+
+        Log.i(TAG, "recordMotion() end recording")
+
+        val timeSeries =
+            TimeSeries(
+                frameIdCollector.toList(),
+                propertyCollector.entries.map { entry -> Feature(entry.key, entry.value) },
+            )
+
+        return create(timeSeries, null)
+    }
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+private class MotionControlImpl(
+    val animatorTestRule: AnimatorTestRule,
+    val testScope: TestScope,
+    val frameMs: Long,
+    motionControl: MotionControl,
+) : MotionControlScope {
+    private val recordingJob = motionControl.recording.launch()
+
+    private val frameEmitter = MutableStateFlow<Long>(0)
+    private val onFrame = frameEmitter.asStateFlow()
+
+    var recordingEnded: Boolean = false
+
+    fun nextFrame() {
+        animatorTestRule.advanceTimeBy(frameMs)
+
+        frameEmitter.tryEmit(animatorTestRule.currentTime)
+        testScope.runCurrent()
+
+        if (recordingJob.isCompleted) {
+            recordingEnded = true
+        }
+    }
+
+    override suspend fun awaitCondition(check: () -> Boolean) {
+        onFrame.takeWhile { !check() }.collect {}
+    }
+
+    override suspend fun awaitFrames(count: Int) {
+        onFrame.take(count).collect {}
+    }
+
+    private fun MotionControlFn.launch(): Job {
+        val function = this
+        return testScope.launch { function() }
+    }
+}
diff --git a/tests/testables/src/android/testing/TestWithLooperRule.java b/tests/testables/src/android/testing/TestWithLooperRule.java
index 10df17f..6a8e142 100644
--- a/tests/testables/src/android/testing/TestWithLooperRule.java
+++ b/tests/testables/src/android/testing/TestWithLooperRule.java
@@ -100,6 +100,9 @@
                     case "ExpectException":
                         next = this.getNextStatement(next, "next");
                         break;
+                    case "UiThreadStatement":
+                        next = this.getNextStatement(next, "base");
+                        break;
                     default:
                         throw new Exception(
                                 String.format("Unexpected Statement received: [%s]",
diff --git a/tests/testables/tests/Android.bp b/tests/testables/tests/Android.bp
index c23f41a..7110564 100644
--- a/tests/testables/tests/Android.bp
+++ b/tests/testables/tests/Android.bp
@@ -29,13 +29,17 @@
         "src/**/*.kt",
         "src/**/I*.aidl",
     ],
+    asset_dirs: ["goldens"],
     resource_dirs: ["res"],
     static_libs: [
+        "PlatformMotionTesting",
         "androidx.core_core-animation",
         "androidx.core_core-ktx",
+        "androidx.test.ext.junit",
         "androidx.test.rules",
         "androidx.test.ext.junit",
         "hamcrest-library",
+        "kotlinx_coroutines_test",
         "mockito-target-inline-minus-junit4",
         "testables",
         "truth",
diff --git a/tests/testables/tests/goldens/recordMotion_withAnimator.json b/tests/testables/tests/goldens/recordMotion_withAnimator.json
new file mode 100644
index 0000000..87fece5
--- /dev/null
+++ b/tests/testables/tests/goldens/recordMotion_withAnimator.json
@@ -0,0 +1,64 @@
+{
+  "frame_ids": [
+    0,
+    20,
+    40,
+    60,
+    80,
+    100,
+    120,
+    140,
+    160,
+    180,
+    200,
+    220,
+    240,
+    260,
+    280,
+    300,
+    320,
+    340,
+    360,
+    380,
+    400,
+    420,
+    440,
+    460,
+    480,
+    500
+  ],
+  "features": [
+    {
+      "name": "value",
+      "type": "float",
+      "data_points": [
+        1,
+        0.9960574,
+        0.98429155,
+        0.9648882,
+        0.9381534,
+        0.9045085,
+        0.8644843,
+        0.818712,
+        0.76791346,
+        0.7128896,
+        0.65450853,
+        0.5936906,
+        0.5313952,
+        0.46860474,
+        0.40630943,
+        0.34549147,
+        0.2871104,
+        0.23208654,
+        0.181288,
+        0.13551569,
+        0.09549153,
+        0.061846733,
+        0.035111785,
+        0.015708387,
+        0.003942609,
+        0
+      ]
+    }
+  ]
+}
diff --git a/tests/testables/tests/goldens/recordMotion_withSpring.json b/tests/testables/tests/goldens/recordMotion_withSpring.json
new file mode 100644
index 0000000..e9fb5b4
--- /dev/null
+++ b/tests/testables/tests/goldens/recordMotion_withSpring.json
@@ -0,0 +1,48 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272
+  ],
+  "features": [
+    {
+      "name": "value",
+      "type": "float",
+      "data_points": [
+        1,
+        0.9488604,
+        0.83574325,
+        0.7016156,
+        0.5691678,
+        0.4497436,
+        0.34789434,
+        0.26431116,
+        0.19766562,
+        0.14572789,
+        0.10601636,
+        0.076149896,
+        0.05401709,
+        0.037837274,
+        0.026161024,
+        0.017839976,
+        0.011983856,
+        0.007914998
+      ]
+    }
+  ]
+}
diff --git a/tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt b/tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt
new file mode 100644
index 0000000..fbef489
--- /dev/null
+++ b/tests/testables/tests/src/android/animation/AnimatorTestRuleToolkitTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation
+
+import android.util.FloatProperty
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.dynamicanimation.animation.SpringAnimation
+import com.android.internal.dynamicanimation.animation.SpringForce
+import kotlinx.coroutines.test.TestScope
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.motion.MotionTestRule
+import platform.test.motion.RecordedMotion
+import platform.test.motion.golden.FeatureCapture
+import platform.test.motion.golden.asDataPoint
+import platform.test.motion.testing.createGoldenPathManager
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AnimatorTestRuleToolkitTest {
+    companion object {
+        private val GOLDEN_PATH_MANAGER =
+            createGoldenPathManager("frameworks/base/tests/testables/tests/goldens")
+
+        private val TEST_PROPERTY =
+            object : FloatProperty<TestState>("value") {
+                override fun get(state: TestState): Float {
+                    return state.animatedValue
+                }
+
+                override fun setValue(state: TestState, value: Float) {
+                    state.animatedValue = value
+                }
+            }
+    }
+
+    @get:Rule(order = 0) val animatorTestRule = AnimatorTestRule(this)
+    @get:Rule(order = 1)
+    val motionRule =
+        MotionTestRule(AnimatorTestRuleToolkit(animatorTestRule, TestScope()), GOLDEN_PATH_MANAGER)
+
+    @Test
+    fun recordMotion_withAnimator() {
+        val state = TestState()
+        AnimatorSet().apply {
+            duration = 500
+            play(
+                ValueAnimator.ofFloat(state.animatedValue, 0f).apply {
+                    addUpdateListener { state.animatedValue = it.animatedValue as Float }
+                }
+            )
+            getInstrumentation().runOnMainSync { start() }
+        }
+
+        val recordedMotion =
+            record(state, MotionControl { awaitFrames(count = 26) }, sampleIntervalMs = 20L)
+
+        motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden("recordMotion_withAnimator")
+    }
+
+    @Test
+    fun recordMotion_withSpring() {
+        val state = TestState()
+        var isDone = false
+        SpringAnimation(state, TEST_PROPERTY).apply {
+            spring =
+                SpringForce(0f).apply {
+                    stiffness = 500f
+                    dampingRatio = 0.95f
+                }
+
+            setStartValue(1f)
+            setMinValue(0f)
+            setMaxValue(1f)
+            minimumVisibleChange = 0.01f
+
+            addEndListener { _, _, _, _ -> isDone = true }
+            getInstrumentation().runOnMainSync { start() }
+        }
+
+        val recordedMotion =
+            record(state, MotionControl { awaitCondition { isDone } }, sampleIntervalMs = 16L)
+
+        motionRule.assertThat(recordedMotion).timeSeriesMatchesGolden("recordMotion_withSpring")
+    }
+
+    private fun record(
+        state: TestState,
+        motionControl: MotionControl,
+        sampleIntervalMs: Long,
+    ): RecordedMotion {
+        var recordedMotion: RecordedMotion? = null
+        getInstrumentation().runOnMainSync {
+            recordedMotion =
+                motionRule.recordMotion(
+                    AnimatorRuleRecordingSpec(
+                        state,
+                        motionControl,
+                        sampleIntervalMs,
+                    ) {
+                        feature(
+                            FeatureCapture("value") { state -> state.animatedValue.asDataPoint() },
+                            "value",
+                        )
+                    }
+                )
+        }
+        return recordedMotion!!
+    }
+
+    data class TestState(var animatedValue: Float = 1f)
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 4cb7c91..7e0bbc4 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -70,7 +70,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.Uri;
-import android.net.vcn.Flags;
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
@@ -85,7 +84,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -104,7 +102,6 @@
 import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -122,8 +119,6 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class VcnManagementServiceTest {
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
     private static final String TEST_PACKAGE_NAME =
             VcnManagementServiceTest.class.getPackage().getName();
@@ -285,8 +280,6 @@
 
     @Before
     public void setUp() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_MAIN_USER);
-
         doNothing()
                 .when(mMockContext)
                 .enforceCallingOrSelfPermission(
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index e29e462..e045f10 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -224,7 +224,6 @@
         doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
         doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
         doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
-        doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled();
 
         doReturn(mUnderlyingNetworkController)
                 .when(mDeps)
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
index edad678..bc7ff47 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -34,14 +34,12 @@
 import android.net.NetworkCapabilities;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.FeatureFlags;
-import android.net.vcn.Flags;
 import android.os.Handler;
 import android.os.IPowerManager;
 import android.os.IThermalService;
 import android.os.ParcelUuid;
 import android.os.PowerManager;
 import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.TelephonyManager;
 
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -49,7 +47,6 @@
 import com.android.server.vcn.VcnNetworkProvider;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -57,8 +54,6 @@
 import java.util.UUID;
 
 public abstract class NetworkEvaluationTestBase {
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     protected static final String SSID = "TestWifi";
     protected static final String SSID_OTHER = "TestWifiOther";
     protected static final String PLMN_ID = "123456";
@@ -120,10 +115,6 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mSetFlagsRule.enableFlags(Flags.FLAG_VALIDATE_NETWORK_ON_IPSEC_LOSS);
-        mSetFlagsRule.enableFlags(Flags.FLAG_EVALUATE_IPSEC_LOSS_ON_LP_NC_CHANGE);
-        mSetFlagsRule.enableFlags(Flags.FLAG_HANDLE_SEQ_NUM_LEAP);
-
         when(mNetwork.getNetId()).thenReturn(-1);
 
         mTestLooper = new TestLooper();
@@ -136,7 +127,6 @@
                                 false /* isInTestMode */));
         doNothing().when(mVcnContext).ensureRunningOnLooperThread();
 
-        doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled();
         doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
 
         setupSystemService(
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 588624b..6f31d8d 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -226,7 +226,6 @@
     private void resetVcnContext(VcnContext vcnContext) {
         reset(vcnContext);
         doNothing().when(vcnContext).ensureRunningOnLooperThread();
-        doReturn(true).when(vcnContext).isFlagNetworkMetricMonitorEnabled();
         doReturn(true).when(vcnContext).isFlagIpSecTransformStateEnabled();
     }
 
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 37b1687..5983cf1 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -28,8 +28,19 @@
 namespace aapt {
 
 static constexpr ApiVersion sDevelopmentSdkLevel = 10000;
+
+// clang-format off
 static constexpr StringPiece sDevelopmentSdkCodeNames[] = {
-    "Q"sv, "R"sv, "S"sv, "Sv2"sv, "Tiramisu"sv, "UpsideDownCake"sv, "VanillaIceCream"sv};
+    "Q"sv,
+    "R"sv,
+    "S"sv,
+    "Sv2"sv,
+    "Tiramisu"sv,
+    "UpsideDownCake"sv,
+    "VanillaIceCream"sv,
+    "Baklava"sv,
+};
+// clang-format on
 
 static constexpr auto sPrivacySandboxSuffix = "PrivacySandbox"sv;
 
diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore
deleted file mode 100644
index 6453bde..0000000
--- a/tools/hoststubgen/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-out/
-*-out/
-*.log
diff --git a/tools/hoststubgen/OWNERS b/tools/hoststubgen/OWNERS
deleted file mode 100644
index 3d8888d..0000000
--- a/tools/hoststubgen/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:platform/frameworks/base:/ravenwood/OWNERS
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
deleted file mode 100644
index 856e6ee..0000000
--- a/tools/hoststubgen/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "imports": [
-    {
-      "path": "frameworks/base/ravenwood"
-    }
-  ]
-}
diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore
deleted file mode 100644
index 0f38407..0000000
--- a/tools/hoststubgen/hoststubgen/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-framework-all-stub-out
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
deleted file mode 100644
index 073b503..0000000
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.hoststubgen.filters
-
-import com.android.hoststubgen.ParseException
-import com.android.hoststubgen.asm.ClassNodes
-import com.android.hoststubgen.asm.splitWithLastPeriod
-import com.android.hoststubgen.asm.toHumanReadableClassName
-import com.android.hoststubgen.asm.toJvmClassName
-import com.android.hoststubgen.log
-import com.android.hoststubgen.normalizeTextLine
-import com.android.hoststubgen.whitespaceRegex
-import org.objectweb.asm.Opcodes
-import org.objectweb.asm.tree.ClassNode
-import java.io.BufferedReader
-import java.io.FileReader
-import java.io.PrintWriter
-import java.util.Objects
-import java.util.regex.Pattern
-
-/**
- * Print a class node as a "keep" policy.
- */
-fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) {
-    pw.printf("class %s %s\n", cn.name.toHumanReadableClassName(), "keep")
-
-    cn.fields?.let {
-        for (f in it.sortedWith(compareBy({ it.name }))) {
-            pw.printf("    field %s %s\n", f.name, "keep")
-        }
-    }
-    cn.methods?.let {
-        for (m in it.sortedWith(compareBy({ it.name }, { it.desc }))) {
-            pw.printf("    method %s %s %s\n", m.name, m.desc, "keep")
-        }
-    }
-}
-
-/** Return true if [access] is either public or protected. */
-private fun isVisible(access: Int): Boolean {
-    return (access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED)) != 0
-}
-
-private const val FILTER_REASON = "file-override"
-
-/**
- * Read a given "policy" file and return as an [OutputFilter]
- */
-fun createFilterFromTextPolicyFile(
-        filename: String,
-        classes: ClassNodes,
-        fallback: OutputFilter,
-        ): OutputFilter {
-    log.i("Loading offloaded annotations from $filename ...")
-    log.withIndent {
-        val subclassFilter = SubclassFilter(classes, fallback)
-        val packageFilter = PackageFilter(subclassFilter)
-        val imf = InMemoryOutputFilter(classes, packageFilter)
-
-        var lineNo = 0
-
-        var aidlPolicy: FilterPolicyWithReason? = null
-        var featureFlagsPolicy: FilterPolicyWithReason? = null
-        var syspropsPolicy: FilterPolicyWithReason? = null
-        var rFilePolicy: FilterPolicyWithReason? = null
-        val typeRenameSpec = mutableListOf<TextFilePolicyRemapperFilter.TypeRenameSpec>()
-        val methodReplaceSpec =
-            mutableListOf<TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec>()
-
-        try {
-            BufferedReader(FileReader(filename)).use { reader ->
-                var className = ""
-
-                while (true) {
-                    var line = reader.readLine() ?: break
-                    lineNo++
-
-                    line = normalizeTextLine(line)
-
-                    if (line.isEmpty()) {
-                        continue // skip empty lines.
-                    }
-
-
-                    // TODO: Method too long, break it up.
-
-                    val fields = line.split(whitespaceRegex).toTypedArray()
-                    when (fields[0].lowercase()) {
-                        "p", "package" -> {
-                            if (fields.size < 3) {
-                                throw ParseException("Package ('p') expects 2 fields.")
-                            }
-                            val name = fields[1]
-                            val rawPolicy = fields[2]
-                            if (resolveExtendingClass(name) != null) {
-                                throw ParseException("Package can't be a super class type")
-                            }
-                            if (resolveSpecialClass(name) != SpecialClass.NotSpecial) {
-                                throw ParseException("Package can't be a special class type")
-                            }
-                            if (rawPolicy.startsWith("!")) {
-                                throw ParseException("Package can't have a substitution")
-                            }
-                            if (rawPolicy.startsWith("~")) {
-                                throw ParseException("Package can't have a class load hook")
-                            }
-                            val policy = parsePolicy(rawPolicy)
-                            if (!policy.isUsableWithClasses) {
-                                throw ParseException("Package can't have policy '$policy'")
-                            }
-                            packageFilter.addPolicy(name, policy.withReason(FILTER_REASON))
-                        }
-
-                        "c", "class" -> {
-                            if (fields.size < 3) {
-                                throw ParseException("Class ('c') expects 2 fields.")
-                            }
-                            className = fields[1]
-
-                            // superClass is set when the class name starts with a "*".
-                            val superClass = resolveExtendingClass(className)
-
-                            // :aidl, etc?
-                            val classType = resolveSpecialClass(className)
-
-                            if (fields[2].startsWith("!")) {
-                                if (classType != SpecialClass.NotSpecial) {
-                                    // We could support it, but not needed at least for now.
-                                    throw ParseException(
-                                            "Special class can't have a substitution")
-                                }
-                                // It's a redirection class.
-                                val toClass = fields[2].substring(1)
-                                imf.setRedirectionClass(className, toClass)
-                            } else if (fields[2].startsWith("~")) {
-                                if (classType != SpecialClass.NotSpecial) {
-                                    // We could support it, but not needed at least for now.
-                                    throw ParseException(
-                                            "Special class can't have a class load hook")
-                                }
-                                // It's a class-load hook
-                                val callback = fields[2].substring(1)
-                                imf.setClassLoadHook(className, callback)
-                            } else {
-                                val policy = parsePolicy(fields[2])
-                                if (!policy.isUsableWithClasses) {
-                                    throw ParseException("Class can't have policy '$policy'")
-                                }
-                                Objects.requireNonNull(className)
-
-                                when (classType) {
-                                    SpecialClass.NotSpecial -> {
-                                        // TODO: Duplicate check, etc
-                                        if (superClass == null) {
-                                            imf.setPolicyForClass(
-                                                className, policy.withReason(FILTER_REASON)
-                                            )
-                                        } else {
-                                            subclassFilter.addPolicy(superClass,
-                                                policy.withReason("extends $superClass"))
-                                        }
-                                    }
-                                    SpecialClass.Aidl -> {
-                                        if (aidlPolicy != null) {
-                                            throw ParseException(
-                                                    "Policy for AIDL classes already defined")
-                                        }
-                                        aidlPolicy = policy.withReason(
-                                                "$FILTER_REASON (special-class AIDL)")
-                                    }
-                                    SpecialClass.FeatureFlags -> {
-                                        if (featureFlagsPolicy != null) {
-                                            throw ParseException(
-                                                    "Policy for feature flags already defined")
-                                        }
-                                        featureFlagsPolicy = policy.withReason(
-                                                "$FILTER_REASON (special-class feature flags)")
-                                    }
-                                    SpecialClass.Sysprops -> {
-                                        if (syspropsPolicy != null) {
-                                            throw ParseException(
-                                                    "Policy for sysprops already defined")
-                                        }
-                                        syspropsPolicy = policy.withReason(
-                                                "$FILTER_REASON (special-class sysprops)")
-                                    }
-                                    SpecialClass.RFile -> {
-                                        if (rFilePolicy != null) {
-                                            throw ParseException(
-                                                "Policy for R file already defined")
-                                        }
-                                        rFilePolicy = policy.withReason(
-                                            "$FILTER_REASON (special-class R file)")
-                                    }
-                                }
-                            }
-                        }
-
-                        "f", "field" -> {
-                            if (fields.size < 3) {
-                                throw ParseException("Field ('f') expects 2 fields.")
-                            }
-                            val name = fields[1]
-                            val policy = parsePolicy(fields[2])
-                            if (!policy.isUsableWithFields) {
-                                throw ParseException("Field can't have policy '$policy'")
-                            }
-                            Objects.requireNonNull(className)
-
-                            // TODO: Duplicate check, etc
-                            imf.setPolicyForField(className, name, policy.withReason(FILTER_REASON))
-                        }
-
-                        "m", "method" -> {
-                            if (fields.size < 4) {
-                                throw ParseException("Method ('m') expects 3 fields.")
-                            }
-                            val name = fields[1]
-                            val signature = fields[2]
-                            val policy = parsePolicy(fields[3])
-
-                            if (!policy.isUsableWithMethods) {
-                                throw ParseException("Method can't have policy '$policy'")
-                            }
-
-                            Objects.requireNonNull(className)
-
-                            imf.setPolicyForMethod(className, name, signature,
-                                    policy.withReason(FILTER_REASON))
-                            if (policy == FilterPolicy.Substitute) {
-                                val fromName = fields[3].substring(1)
-
-                                if (fromName == name) {
-                                    throw ParseException(
-                                            "Substitution must have a different name")
-                                }
-
-                                // Set the policy for the "from" method.
-                                imf.setPolicyForMethod(className, fromName, signature,
-                                    FilterPolicy.Keep.withReason(FILTER_REASON))
-
-                                val classAndMethod = splitWithLastPeriod(fromName)
-                                if (classAndMethod != null) {
-                                    // If the substitution target contains a ".", then
-                                    // it's a method call redirect.
-                                    methodReplaceSpec.add(
-                                        TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec(
-                                            className.toJvmClassName(),
-                                            name,
-                                            signature,
-                                            classAndMethod.first.toJvmClassName(),
-                                            classAndMethod.second,
-                                        )
-                                    )
-                                } else {
-                                    // It's an in-class replace.
-                                    // ("@RavenwoodReplace" equivalent)
-                                    imf.setRenameTo(className, fromName, signature, name)
-                                }
-                            }
-                        }
-                        "r", "rename" -> {
-                            if (fields.size < 3) {
-                                throw ParseException("Rename ('r') expects 2 fields.")
-                            }
-                            // Add ".*" to make it a prefix match.
-                            val pattern = Pattern.compile(fields[1] + ".*")
-
-                            // Removing the leading /'s from the prefix. This allows
-                            // using a single '/' as an empty suffix, which is useful to have a
-                            // "negative" rename rule to avoid subsequent raname's from getting
-                            // applied. (Which is needed for services.jar)
-                            val prefix = fields[2].trimStart('/')
-
-                            typeRenameSpec += TextFilePolicyRemapperFilter.TypeRenameSpec(
-                                pattern, prefix)
-                        }
-
-                        else -> {
-                            throw ParseException("Unknown directive \"${fields[0]}\"")
-                        }
-                    }
-                }
-            }
-        } catch (e: ParseException) {
-            throw e.withSourceInfo(filename, lineNo)
-        }
-
-        var ret: OutputFilter = imf
-        if (typeRenameSpec.isNotEmpty()) {
-            ret = TextFilePolicyRemapperFilter(typeRenameSpec, ret)
-        }
-        if (methodReplaceSpec.isNotEmpty()) {
-            ret = TextFilePolicyMethodReplaceFilter(methodReplaceSpec, classes, ret)
-        }
-
-        // Wrap the in-memory-filter with AHF.
-        ret = AndroidHeuristicsFilter(
-                classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, ret)
-
-        return ret
-    }
-}
-
-private enum class SpecialClass {
-    NotSpecial,
-    Aidl,
-    FeatureFlags,
-    Sysprops,
-    RFile,
-}
-
-private fun resolveSpecialClass(className: String): SpecialClass {
-    if (!className.startsWith(":")) {
-        return SpecialClass.NotSpecial
-    }
-    when (className.lowercase()) {
-        ":aidl" -> return SpecialClass.Aidl
-        ":feature_flags" -> return SpecialClass.FeatureFlags
-        ":sysprops" -> return SpecialClass.Sysprops
-        ":r" -> return SpecialClass.RFile
-    }
-    throw ParseException("Invalid special class name \"$className\"")
-}
-
-private fun resolveExtendingClass(className: String): String? {
-    if (!className.startsWith("*")) {
-        return null
-    }
-    return className.substring(1)
-}
-
-private fun parsePolicy(s: String): FilterPolicy {
-    return when (s.lowercase()) {
-        "k", "keep" -> FilterPolicy.Keep
-        "t", "throw" -> FilterPolicy.Throw
-        "r", "remove" -> FilterPolicy.Remove
-        "kc", "keepclass" -> FilterPolicy.KeepClass
-        "i", "ignore" -> FilterPolicy.Ignore
-        "rdr", "redirect" -> FilterPolicy.Redirect
-        else -> {
-            if (s.startsWith("@")) {
-                FilterPolicy.Substitute
-            } else {
-                throw ParseException("Invalid policy \"$s\"")
-            }
-        }
-    }
-}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index cba521e..196b5e7 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -22,8 +22,6 @@
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeSpec
-import java.util.HashMap
-import java.util.Map
 import javax.lang.model.element.Modifier
 
 /*
@@ -52,7 +50,7 @@
  *     public static boolean hasFeatureAutomotive(Context context);
  *     public static boolean hasFeatureLeanback(Context context);
  *     public static Boolean maybeHasFeature(String feature, int version);
- *     public static ArrayMap<String, FeatureInfo> getCompileTimeAvailableFeatures();
+ *     public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures();
  * }
  * </pre>
  */
@@ -63,6 +61,7 @@
     private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
     private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
     private val FEATUREINFO_CLASS = ClassName.get("android.content.pm", "FeatureInfo")
+    private val ARRAYMAP_CLASS = ClassName.get("android.util", "ArrayMap")
     private val ASSUME_TRUE_CLASS =
         ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8")
     private val ASSUME_FALSE_CLASS =
@@ -291,19 +290,19 @@
         features: Collection<FeatureInfo>,
     ) {
         val methodBuilder =
-                MethodSpec.methodBuilder("getCompileTimeAvailableFeatures")
+                MethodSpec.methodBuilder("getReadOnlySystemEnabledFeatures")
                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                 .addAnnotation(ClassName.get("android.annotation", "NonNull"))
                 .addJavadoc("Gets features marked as available at compile-time, keyed by name." +
                         "\n\n@hide")
                 .returns(ParameterizedTypeName.get(
-                        ClassName.get(Map::class.java),
+                        ARRAYMAP_CLASS,
                         ClassName.get(String::class.java),
                         FEATUREINFO_CLASS))
 
         val availableFeatures = features.filter { it.readonly && it.version != null }
-        methodBuilder.addStatement("Map<String, FeatureInfo> features = new \$T<>(\$L)",
-                HashMap::class.java, availableFeatures.size)
+        methodBuilder.addStatement("\$T<String, FeatureInfo> features = new \$T<>(\$L)",
+                ARRAYMAP_CLASS, ARRAYMAP_CLASS, availableFeatures.size)
         if (!availableFeatures.isEmpty()) {
             methodBuilder.addStatement("FeatureInfo fi = new FeatureInfo()")
         }
diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
index edbfc42..ee97b26 100644
--- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
@@ -13,10 +13,9 @@
 import android.content.Context;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.util.ArrayMap;
 import com.android.aconfig.annotations.AssumeFalseForR8;
 import com.android.aconfig.annotations.AssumeTrueForR8;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * @hide
@@ -94,8 +93,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(2);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(2);
         FeatureInfo fi = new FeatureInfo();
         fi.name = PackageManager.FEATURE_WATCH;
         fi.version = 1;
diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
index bf7a006..40c7db7 100644
--- a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
@@ -9,8 +9,7 @@
 import android.content.Context;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
 
 /**
  * @hide
@@ -43,8 +42,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(0);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
         return features;
     }
 }
diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
index b20b228..7bf8961 100644
--- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
@@ -12,8 +12,7 @@
 import android.content.Context;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
 
 /**
  * @hide
@@ -73,8 +72,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(0);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
         return features;
     }
 }
diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
index d91f5b6..eb7ec63 100644
--- a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
@@ -7,8 +7,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.FeatureInfo;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
 
 /**
  * @hide
@@ -32,8 +31,8 @@
      * @hide
      */
     @NonNull
-    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
-        Map<String, FeatureInfo> features = new HashMap<>(0);
+    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
         return features;
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt b/tools/systemfeatures/tests/src/ArrayMap.java
similarity index 74%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
copy to tools/systemfeatures/tests/src/ArrayMap.java
index 2f5daaa..a5ed9b0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
+++ b/tools/systemfeatures/tests/src/ArrayMap.java
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package android.util;
 
-import com.android.systemui.kosmos.Kosmos
+import java.util.HashMap;
 
-val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
+/** Stub for testing. */
+public final class ArrayMap<K, V> extends HashMap<K, V> {
+    public ArrayMap(int capacity) {
+        super(capacity);
+    }
+}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
index 39f8fc4..ed3f5c9 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
@@ -60,7 +60,7 @@
         assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
-        assertThat(RwNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+        assertThat(RwNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
     }
 
     @Test
@@ -72,7 +72,7 @@
         assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
-        assertThat(RoNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+        assertThat(RoNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
 
         // Also ensure we fall back to the PackageManager for feature APIs without an accompanying
         // versioned feature definition.
@@ -106,7 +106,7 @@
         assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
-        assertThat(RwFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+        assertThat(RwFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
     }
 
     @Test
@@ -163,7 +163,7 @@
         assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull();
         assertThat(RoFeatures.maybeHasFeature("", 0)).isNull();
 
-        Map<String, FeatureInfo> compiledFeatures = RoFeatures.getCompileTimeAvailableFeatures();
+        Map<String, FeatureInfo> compiledFeatures = RoFeatures.getReadOnlySystemEnabledFeatures();
         assertThat(compiledFeatures.keySet())
                 .containsExactly(PackageManager.FEATURE_WATCH, PackageManager.FEATURE_WIFI);
         assertThat(compiledFeatures.get(PackageManager.FEATURE_WATCH).version).isEqualTo(1);
diff --git a/wifi/java/src/android/net/wifi/WifiMigration.java b/wifi/java/src/android/net/wifi/WifiMigration.java
index f1850dd..28e9c45 100644
--- a/wifi/java/src/android/net/wifi/WifiMigration.java
+++ b/wifi/java/src/android/net/wifi/WifiMigration.java
@@ -38,6 +38,8 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.os.BackgroundThread;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
@@ -48,6 +50,8 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
 
 /**
  * Class used to provide one time hooks for existing OEM devices to migrate their config store
@@ -605,13 +609,35 @@
     /**
      * Migrate any certificates in Legacy Keystore to the newer WifiBlobstore database.
      *
-     * If there are no certificates to migrate, this method will return immediately.
+     * Operation will be handled on the BackgroundThread, and the result will be posted
+     * to the provided Executor.
+     *
+     * @param executor The executor on which callback will be invoked
+     * @param resultsCallback Callback to receive the status code
      *
      * @hide
      */
     @FlaggedApi(Flags.FLAG_LEGACY_KEYSTORE_TO_WIFI_BLOBSTORE_MIGRATION_READ_ONLY)
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public static @KeystoreMigrationStatus int migrateLegacyKeystoreToWifiBlobstore() {
+    public static void migrateLegacyKeystoreToWifiBlobstore(
+            @NonNull Executor executor, @NonNull IntConsumer resultsCallback) {
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null");
+        BackgroundThread.getHandler().post(() -> {
+            int status = migrateLegacyKeystoreToWifiBlobstoreInternal();
+            executor.execute(() -> {
+                resultsCallback.accept(status);
+            });
+        });
+    }
+
+    /**
+     * Synchronously perform the Keystore migration described in
+     * {@link #migrateLegacyKeystoreToWifiBlobstore(Executor, IntConsumer)}
+     *
+     * @hide
+     */
+    public static @KeystoreMigrationStatus int migrateLegacyKeystoreToWifiBlobstoreInternal() {
         if (!WifiBlobStore.supplicantCanAccessBlobstore()) {
             // Supplicant cannot access WifiBlobstore, so keep the certs in Legacy Keystore
             Log.i(TAG, "Avoiding migration since supplicant cannot access WifiBlobstore");
diff --git a/wifi/tests/src/android/net/wifi/WifiMigrationTest.java b/wifi/tests/src/android/net/wifi/WifiMigrationTest.java
index 0aa299f..ce5b60a 100644
--- a/wifi/tests/src/android/net/wifi/WifiMigrationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiMigrationTest.java
@@ -81,7 +81,7 @@
     public void testKeystoreMigrationAvoidedOnLegacyVendorPartition() {
         when(WifiBlobStore.supplicantCanAccessBlobstore()).thenReturn(false);
         assertEquals(WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_NOT_NEEDED,
-                WifiMigration.migrateLegacyKeystoreToWifiBlobstore());
+                WifiMigration.migrateLegacyKeystoreToWifiBlobstoreInternal());
         verifyNoMoreInteractions(mLegacyKeystore, mWifiBlobStore);
     }
 
@@ -93,7 +93,7 @@
     public void testKeystoreMigrationNoLegacyAliases() throws Exception {
         when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(new String[0]);
         assertEquals(WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_NOT_NEEDED,
-                WifiMigration.migrateLegacyKeystoreToWifiBlobstore());
+                WifiMigration.migrateLegacyKeystoreToWifiBlobstoreInternal());
         verify(mLegacyKeystore).list(anyString(), anyInt());
         verifyNoMoreInteractions(mLegacyKeystore, mWifiBlobStore);
     }
@@ -110,7 +110,7 @@
         when(mWifiBlobStore.list(anyString())).thenReturn(blobstoreAliases);
 
         assertEquals(WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_COMPLETE,
-                WifiMigration.migrateLegacyKeystoreToWifiBlobstore());
+                WifiMigration.migrateLegacyKeystoreToWifiBlobstoreInternal());
         verify(mWifiBlobStore, times(legacyAliases.length)).put(anyString(), any(byte[].class));
     }
 
@@ -129,7 +129,7 @@
 
         // Expect that only the unique legacy alias is migrated to the blobstore
         assertEquals(WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_COMPLETE,
-                WifiMigration.migrateLegacyKeystoreToWifiBlobstore());
+                WifiMigration.migrateLegacyKeystoreToWifiBlobstoreInternal());
         verify(mWifiBlobStore).list(anyString());
         verify(mWifiBlobStore).put(eq(uniqueLegacyAlias), any(byte[].class));
         verifyNoMoreInteractions(mWifiBlobStore);
@@ -146,7 +146,7 @@
         when(mLegacyKeystore.list(anyString(), anyInt())).thenThrow(
                 new ServiceSpecificException(ILegacyKeystore.ERROR_SYSTEM_ERROR));
         assertEquals(WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_NOT_NEEDED,
-                WifiMigration.migrateLegacyKeystoreToWifiBlobstore());
+                WifiMigration.migrateLegacyKeystoreToWifiBlobstoreInternal());
     }
 
     /**
@@ -157,6 +157,6 @@
     public void testKeystoreMigrationFailsIfExceptionEncountered() throws Exception {
         when(mLegacyKeystore.list(anyString(), anyInt())).thenThrow(new RemoteException());
         assertEquals(WifiMigration.KEYSTORE_MIGRATION_FAILURE_ENCOUNTERED_EXCEPTION,
-                WifiMigration.migrateLegacyKeystoreToWifiBlobstore());
+                WifiMigration.migrateLegacyKeystoreToWifiBlobstoreInternal());
     }
 }